diff --git a/addons/event_booth_sale/__manifest__.py b/addons/event_booth_sale/__manifest__.py index 3b613d4639e..ebdb1b78f32 100644 --- a/addons/event_booth_sale/__manifest__.py +++ b/addons/event_booth_sale/__manifest__.py @@ -24,9 +24,7 @@ Sell your event booths and track payments on sale orders. 'auto_install': True, 'assets': { 'web.assets_backend': [ - 'event_booth_sale/static/src/js/event_booth_configurator_form_controller.js', - 'event_booth_sale/static/src/js/event_booth_configurator_form_view.js', - 'event_booth_sale/static/src/js/event_booth_configurator_widget.js', + 'event_booth_sale/static/src/**/*', ] }, 'license': 'LGPL-3', diff --git a/addons/event_booth_sale/static/src/js/event_booth_configurator_form_controller.js b/addons/event_booth_sale/static/src/js/event_booth_configurator_form_controller.js deleted file mode 100644 index 5aceb053baa..00000000000 --- a/addons/event_booth_sale/static/src/js/event_booth_configurator_form_controller.js +++ /dev/null @@ -1,44 +0,0 @@ -odoo.define('event_booth_sale.event_booth_configurator_form_controller', function (require) { -'use strict'; - -const FormController = require('web.FormController'); -/** - * This controller is overridden to allow configuring sale_order_lines through a popup - * window when a product with 'detailed_type' == 'event_booth' is selected. - * - * This allows keeping an editable list view for sales order and remove the noise of - * those 3 fields ('event_id', 'event_booth_category_id' and 'event_booth_ids') - */ -const EventBoothConfiguratorFormController = FormController.extend({ - - //-------------------------------------------------------------------------- - // Overrides - //-------------------------------------------------------------------------- - - saveRecord: function () { - const self = this; - return this._super.apply(this, arguments).then(function () { - const state = self.model.get(self.handle, {raw: true}); - self.do_action({ - type: 'ir.actions.act_window_close', - infos: { - eventBoothConfiguration: { - event_id: {id: state.data.event_id}, - event_booth_category_id: {id: state.data.event_booth_category_id}, - event_booth_pending_ids: { - operation: 'MULTI', - commands: [{ - operation: 'REPLACE_WITH', - ids: state.data.event_booth_ids, - }], - } - } - } - }); - }); - } -}); - -return EventBoothConfiguratorFormController; - -}); diff --git a/addons/event_booth_sale/static/src/js/event_booth_configurator_form_view.js b/addons/event_booth_sale/static/src/js/event_booth_configurator_form_view.js deleted file mode 100644 index b1f7d849cbf..00000000000 --- a/addons/event_booth_sale/static/src/js/event_booth_configurator_form_view.js +++ /dev/null @@ -1,21 +0,0 @@ -odoo.define('event_booth_sale.event_booth_configurator_form_view', function (require) { -'use strict'; - -const EventBoothConfiguratorFormController = require('event_booth_sale.event_booth_configurator_form_controller'); -const FormView = require('web.FormView'); -const viewRegistry = require('web.view_registry'); - -/** - * @see EventBoothConfiguratorFormController for more information. - */ -const EventBoothConfiguratorFormView = FormView.extend({ - config: _.extend({}, FormView.prototype.config, { - Controller: EventBoothConfiguratorFormController, - }), -}); - -viewRegistry.add('event_booth_configurator_form', EventBoothConfiguratorFormView); - -return EventBoothConfiguratorFormView; - -}); diff --git a/addons/event_booth_sale/static/src/js/event_booth_configurator_model.js b/addons/event_booth_sale/static/src/js/event_booth_configurator_model.js new file mode 100644 index 00000000000..bfae6c692fb --- /dev/null +++ b/addons/event_booth_sale/static/src/js/event_booth_configurator_model.js @@ -0,0 +1,44 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { formView } from "@web/views/form/form_view" +import { Record, RelationalModel } from "@web/views/relational_model"; + +/** + * This model is overridden to allow configuring sale_order_lines through a popup + * window when a product with 'detailed_type' == 'event_booth' is selected. + * + * This allows keeping an editable list view for sales order and remove the noise of + * those 3 fields ('event_id', 'event_booth_category_id' and 'event_booth_ids') + */ +class EventBoothConfiguratorRelationalModel extends RelationalModel {} + +class EventBoothConfiguratorRecord extends Record { + + //-------------------------------------------------------------------------- + // Overrides + //-------------------------------------------------------------------------- + async save(options = {}) { + await super.save(options); + this.model.action.doAction({type: 'ir.actions.act_window_close', infos: { + eventBoothConfiguration: { + event_id: this.data.event_id, + event_booth_category_id: this.data.event_booth_category_id, + event_booth_pending_ids: { + operation: 'MULTI', + commands: [{ + operation: 'REPLACE_WITH', + ids: this.data.event_booth_ids.currentIds, + }], + } + } + }}); + } +} + +EventBoothConfiguratorRelationalModel.Record = EventBoothConfiguratorRecord; + +registry.category("views").add("event_booth_configurator_form", { + ...formView, + Model: EventBoothConfiguratorRelationalModel, +}); diff --git a/addons/event_booth_sale/static/src/js/event_booth_configurator_widget.js b/addons/event_booth_sale/static/src/js/event_booth_configurator_widget.js deleted file mode 100644 index f3a2559aaaf..00000000000 --- a/addons/event_booth_sale/static/src/js/event_booth_configurator_widget.js +++ /dev/null @@ -1,149 +0,0 @@ -odoo.define('event_booth_sale.product_configurator', function (require) { -const ProductConfiguratorWidget = require('sale.product_configurator'); - -/** - * Extension of the ProductConfiguratorWidget to support event booth configuration. - * It opens when an event booth product_product is set. - * - * The event booth information include: - * - event_id - * - event_booth_category_id - * - event_booth_ids - * - */ -ProductConfiguratorWidget.include({ - - //-------------------------------------------------------------------------- - // Overrides - //-------------------------------------------------------------------------- - - /** - * @returns {boolean} - * - * @override - * @private - */ - _isConfigurableLine: function () { - return this.recordData.detailed_type === 'event_booth' || this._super.apply(this, arguments); - }, - - /** - * Opens the event booth configurator in 'edit' mode - * - * @override - * @private - */ - _onEditLineConfiguration: function () { - if (this.recordData.detailed_type === 'event_booth') { - const defaultValues = { - default_product_id: this.recordData.product_id.data.id, - default_sale_order_line_id: this.recordData.id || null - }; - if (this.recordData.event_id) { - defaultValues.default_event_id = this.recordData.event_id.data.id; - } - if (this.recordData.event_booth_category_id) { - defaultValues.default_event_booth_category_id = this.recordData.event_booth_category_id.data.id; - } - if (this.recordData.event_booth_pending_ids) { - defaultValues.default_event_booth_ids = this.recordData.event_booth_pending_ids.res_ids; - } - this._openEventBoothConfigurator(defaultValues, this.dataPointID); - } else { - this._super.apply(this, arguments); - } - }, - - /** - * @param {integer} productId: product.product ID - * @param {String} dataPointId - * @returns {Promise} stopPropagation true if a suitable configurator has been found. - * - * @override - * @private - */ - _onProductChange: function (productId, dataPointId) { - return this._super.apply(this, arguments).then((stopPropagation) => { - if (stopPropagation) { - return Promise.resolve(true); - } else { - return this._isProductEventBooth(productId, dataPointId); - } - }); - }, - - //-------------------------------------------------------------------------- - // Private - //------------------------------------------------------------------------- - - /** - * This method will check if the productId needs a configuration or not. - * - * @param {integer} productId: product.product ID - * @param {String} dataPointId - * @returns {Promise} stopPropagation true if the product is an event booth. - * - * @private - */ - _isProductEventBooth: function (productId, dataPointId) { - const self = this; - return this._rpc({ - model: 'product.product', - method: 'read', - args: [productId, ['detailed_type']], - }).then(function (result) { - if (result && result[0].detailed_type === 'event_booth') { - self._openEventBoothConfigurator({ - default_product_id: productId - }, dataPointId); - return Promise.resolve(true); - } - return Promise.resolve(false); - }); - }, - - /** - * Opens the event booth configurator allowing to configure the SO line with its informations. - * - * When the window is closed, configured values are used to trigger a 'field_changed' - * event to modify the current SO line. - * - * If the window is closed without providing the required values 'event_id', 'event_booth_category_id' - * and 'event_booth_pending_ids', the product_id field is cleaned. - * - * @param {Object} additionalContext: various "default_" values added to context when calling - * configuration wizard model; - * @param dataPointId - * - * @private - */ - _openEventBoothConfigurator: function (additionalContext, dataPointId) { - const self = this; - this.do_action('event_booth_sale.event_booth_configurator_action', { - additional_context: additionalContext, - on_close: function (result) { - if (result && !result.special) { - self.trigger_up('field_changed', { - changes: result.eventBoothConfiguration, - dataPointID: dataPointId, - onSuccess: function () { - self._onLineConfigured(); - } - }); - } else if (!self.recordData.event_id || !self.recordData.event_booth_pending_ids) { - self.trigger_up('field_changed', { - changes: { - product_id: false, - name: '', - }, - dataPointID: dataPointId, - }); - } - } - }); - }, -}); - -return ProductConfiguratorWidget; - -}); diff --git a/addons/event_booth_sale/static/src/js/sale_product_field.js b/addons/event_booth_sale/static/src/js/sale_product_field.js new file mode 100644 index 00000000000..f8ee540436c --- /dev/null +++ b/addons/event_booth_sale/static/src/js/sale_product_field.js @@ -0,0 +1,72 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { SaleOrderLineProductField } from '@sale/js/sale_product_field'; + + +patch(SaleOrderLineProductField.prototype, 'event_booth_sale', { + + async _onProductUpdate() { + this._super(...arguments); + if (this.props.record.data.product_type === 'event_booth') { + this._openEventBoothConfigurator(false); + } + }, + + _editLineConfiguration() { + this._super(...arguments); + if (this.props.record.data.product_type === 'event_booth') { + this._openEventBoothConfigurator(true); + } + }, + + get isConfigurableLine() { + return this._super(...arguments) || Boolean(this.props.record.data.event_booth_category_id); + }, + + async _openEventBoothConfigurator(edit) { + let actionContext = { + default_product_id: this.props.record.data.product_id[0], + }; + if (edit) { + const recordData = this.props.record.data; + if (recordData.event_id) { + actionContext.default_event_id = recordData.event_id[0]; + } + if (recordData.event_booth_category_id) { + actionContext.default_event_booth_category_id = recordData.event_booth_category_id[0]; + } + if (recordData.event_booth_pending_ids) { + actionContext.default_event_booth_ids = recordData.event_booth_pending_ids.records.map( + record => { + return [4, record.data.id]; + } + ); + } + } + this.action.doAction( + 'event_booth_sale.event_booth_configurator_action', + { + additionalContext: actionContext, + onClose: async (closeInfo) => { + if (!closeInfo || closeInfo.special) { + // wizard popup closed or 'Cancel' button triggered + if (!this.props.record.data.event_ticket_id) { + // remove product if event configuration was cancelled. + this.props.record.update({ + [this.props.name]: undefined, + }); + } + } else { + const eventBoothConfiguration = closeInfo.eventBoothConfiguration; + this.props.record.update({ + event_id: eventBoothConfiguration.event_id, + event_booth_category_id: eventBoothConfiguration.event_booth_category_id, + event_booth_pending_ids: eventBoothConfiguration.event_booth_pending_ids, + }); + } + } + } + ); + }, +}); diff --git a/addons/event_sale/__manifest__.py b/addons/event_sale/__manifest__.py index caff9de6f23..120d5d3f682 100644 --- a/addons/event_sale/__manifest__.py +++ b/addons/event_sale/__manifest__.py @@ -48,9 +48,6 @@ this event. 'web.assets_tests': [ 'event_sale/static/tests/tours/**/*', ], - 'web.qunit_suite_tests': [ - 'event_sale/static/tests/event_configurator.test.js', - ], }, 'license': 'LGPL-3', } diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index a80ad830e98..879b56916ba 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -21,12 +21,14 @@ class SaleOrder(models.Model): def action_confirm(self): res = super(SaleOrder, self).action_confirm() for so in self: + if not any(line.product_type == 'event' for line in so.order_line): + continue # confirm registration if it was free (otherwise it will be confirmed once invoice fully paid) so.order_line._update_registrations(confirm=so.amount_total == 0, cancel_to_draft=False) - if any(line.event_ok for line in so.order_line): - return self.env['ir.actions.act_window'] \ - .with_context(default_sale_order_id=so.id) \ - ._for_xml_id('event_sale.action_sale_order_event_registration') + if len(self) == 1: + return self.env['ir.actions.act_window'].with_context( + default_sale_order_id=so.id + )._for_xml_id('event_sale.action_sale_order_event_registration') return res def _action_cancel(self): @@ -68,6 +70,7 @@ class SaleOrderLine(models.Model): 'event.event.ticket', string='Event Ticket', compute="_compute_event_ticket_id", store=True, readonly=False, precompute=True, help="Choose an event ticket and it will automatically create a registration for this event ticket.") + # TODO in master: remove this field, unused anymore event_ok = fields.Boolean(compute='_compute_event_ok') @api.depends('product_id.detailed_type') @@ -89,7 +92,9 @@ class SaleOrderLine(models.Model): RegistrationSudo = self.env['event.registration'].sudo() registrations = RegistrationSudo.search([('sale_order_line_id', 'in', self.ids)]) registrations_vals = [] - for so_line in self.filtered('event_ok'): + for so_line in self: + if not so_line.product_type == 'event': + continue existing_registrations = registrations.filtered(lambda self: self.sale_order_line_id.id == so_line.id) if confirm: existing_registrations.filtered(lambda self: self.state not in ['open', 'cancel']).action_confirm() diff --git a/addons/event_sale/static/src/js/event_configurator_model.js b/addons/event_sale/static/src/js/event_configurator_model.js index 79995b110f5..bbbdb882b96 100644 --- a/addons/event_sale/static/src/js/event_configurator_model.js +++ b/addons/event_sale/static/src/js/event_configurator_model.js @@ -28,8 +28,8 @@ class EventConfiguratorRecord extends Record { await super.save(options); this.model.action.doAction({type: 'ir.actions.act_window_close', infos: { eventConfiguration: { - event_id: {id: this.data.event_id[0]}, - event_ticket_id: {id: this.data.event_ticket_id[0]} + event_id: this.data.event_id, + event_ticket_id: this.data.event_ticket_id, } }}); } diff --git a/addons/event_sale/static/src/js/event_configurator_widget.js b/addons/event_sale/static/src/js/event_configurator_widget.js deleted file mode 100644 index 7d2f130c66a..00000000000 --- a/addons/event_sale/static/src/js/event_configurator_widget.js +++ /dev/null @@ -1,144 +0,0 @@ -odoo.define('event_sale.product_configurator', function (require) { -var ProductConfiguratorWidget = require('sale.product_configurator'); - -/** - * Extension of the ProductConfiguratorWidget to support event product configuration. - * It opens when an event product_product is set. - * - * The event information include: - * - event_id - * - event_ticket_id - * - */ -ProductConfiguratorWidget.include({ - /** - * @returns {boolean} - * - * @override - * @private - */ - _isConfigurableLine: function () { - return this.recordData.event_ok || this._super.apply(this, arguments); - }, - - /** - * @param {integer} productId - * @param {String} dataPointID - * @returns {Promise} stopPropagation true if a suitable configurator has been found. - * - * @override - * @private - */ - _onProductChange: function (productId, dataPointId) { - var self = this; - return this._super.apply(this, arguments).then(function (stopPropagation) { - if (stopPropagation || productId === undefined) { - return Promise.resolve(true); - } else { - return self._checkForEvent(productId, dataPointId); - } - }); - }, - - /** - * This method will check if the productId needs configuration or not: - * - * @param {integer} productId - * @param {string} dataPointID - * @returns {Promise} stopPropagation true if the product is an event ticket. - * - * @private - */ - _checkForEvent: function (productId, dataPointId) { - var self = this; - return this._rpc({ - model: 'product.product', - method: 'read', - args: [productId, ['detailed_type']], - }).then(function (result) { - if (Array.isArray(result) && result.length && result[0].detailed_type === 'event') { - self._openEventConfigurator({ - default_product_id: productId - }, - dataPointId - ); - return Promise.resolve(true); - } - return Promise.resolve(false); - }); - }, - - /** - * Opens the event configurator in 'edit' mode. - * - * @override - * @private - */ - _onEditLineConfiguration: function () { - if (this.recordData.event_ok) { - var defaultValues = { - default_product_id: this.recordData.product_id.data.id - }; - - if (this.recordData.event_id) { - defaultValues.default_event_id = this.recordData.event_id.data.id; - } - - if (this.recordData.event_ticket_id) { - defaultValues.default_event_ticket_id = this.recordData.event_ticket_id.data.id; - } - - this._openEventConfigurator(defaultValues, this.dataPointID); - } else { - this._super.apply(this, arguments); - } - }, - - /** - * Opens the event configurator to allow configuring the SO line with events information. - * - * When the window is closed, configured values are used to trigger a 'field_changed' - * event to modify the current SO line. - * - * If the window is closed without providing the required values 'event_id' and - * 'event_ticket_id', the product_id field is cleaned. - * - * @param {Object} data various "default_" values - * @param {string} dataPointId - * - * @private - */ - _openEventConfigurator: function (data, dataPointId) { - var self = this; - this.do_action('event_sale.event_configurator_action', { - additional_context: data, - on_close: function (result) { - if (result && !result.special) { - self.trigger_up('field_changed', { - dataPointID: dataPointId, - changes: result.eventConfiguration, - onSuccess: function () { - // Call post-init function. - self._onLineConfigured(); - } - }); - } else { - if (!self.recordData.event_id || !self.recordData.event_ticket_id) { - self.trigger_up('field_changed', { - dataPointID: dataPointId, - changes: { - product_id: false, - name: '' - }, - }); - } - } - } - }); - } -}); - - -return ProductConfiguratorWidget; - -}); diff --git a/addons/event_sale/static/src/js/sale_product_field.js b/addons/event_sale/static/src/js/sale_product_field.js new file mode 100644 index 00000000000..3540cf1e754 --- /dev/null +++ b/addons/event_sale/static/src/js/sale_product_field.js @@ -0,0 +1,61 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { SaleOrderLineProductField } from '@sale/js/sale_product_field'; + + +patch(SaleOrderLineProductField.prototype, 'event_sale', { + + async _onProductUpdate() { + this._super(...arguments); + if (this.props.record.data.product_type === 'event') { + this._openEventConfigurator(); + } + }, + + _editLineConfiguration() { + this._super(...arguments); + if (this.props.record.data.product_type === 'event') { + this._openEventConfigurator(); + } + }, + + get isConfigurableLine() { + return this._super(...arguments) || Boolean(this.props.record.data.event_ticket_id); + }, + + async _openEventConfigurator() { + let actionContext = { + 'default_product_id': this.props.record.data.product_id[0], + }; + if (this.props.record.data.event_id) { + actionContext.default_event_id = this.props.record.data.event_id[0]; + } + if (this.props.record.data.event_ticket_id) { + actionContext.default_event_ticket_id = this.props.record.data.event_ticket_id[0]; + } + this.action.doAction( + 'event_sale.event_configurator_action', + { + additionalContext: actionContext, + onClose: async (closeInfo) => { + if (!closeInfo || closeInfo.special) { + // wizard popup closed or 'Cancel' button triggered + if (!this.props.record.data.event_ticket_id) { + // remove product if event configuration was cancelled. + this.props.record.update({ + [this.props.name]: undefined, + }); + } + } else { + const eventConfiguration = closeInfo.eventConfiguration; + this.props.record.update({ + 'event_id': eventConfiguration.event_id, + 'event_ticket_id': eventConfiguration.event_ticket_id, + }); + } + } + } + ); + }, +}); diff --git a/addons/event_sale/static/tests/event_configurator.test.js b/addons/event_sale/static/tests/event_configurator.test.js deleted file mode 100644 index 7a462379bb7..00000000000 --- a/addons/event_sale/static/tests/event_configurator.test.js +++ /dev/null @@ -1,140 +0,0 @@ -odoo.define('event.configurator.tests', function (require) { - "use strict"; - - var FormView = require('web.FormView'); - var testUtils = require('web.test_utils'); - var createView = testUtils.createView; - - var getArch = function (){ - return '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
'; - }; - - QUnit.module('Event Configurator', { - beforeEach: function () { - this.data = { - 'product.product': { - fields: { - id: {type: 'integer'}, - detailed_type: {type: 'selection'}, - rent_ok: {type: 'boolean'}//sale_rental purposes - }, - records: [{ - id: 1, - display_name: "Customizable Event", - detailed_type: 'event', - rent_ok: false//sale_rental purposes - }, { - id: 2, - display_name: "Desk", - detailed_type: 'service', - rent_ok: false//sale_rental purposes - }] - }, - sale_order: { - fields: { - id: {type: 'integer'}, - sale_order_line: { - string: 'lines', - type: 'one2many', - relation: 'sale_order_line' - }, - } - }, - sale_order_line: { - fields: { - product_id: { - string: 'product', - type: 'many2one', - relation: 'product.product' - }, - event_ok: {type: 'boolean'}, - rent_ok: {type: 'boolean'},//sale_rental purposes - event_id: { - string: 'event', - type: 'many2one', - relation: 'event' - }, - event_ticket_id: { - string: 'event_ticket', - type: 'many2one', - relation: 'event_ticket' - } - } - } - }; - } - }, function (){ - QUnit.test('Select a regular product and verify that the event configurator is not opened', async function (assert) { - // When the module event_booth_sale is installed, it will also call the rpc: read/detailed_type when selecting a product. - // This means that 2 asserts are done when this module is installed. - // Note that: this test will fail if the module event_booth_sale is not installed. - assert.expect(2); - - var form = await createView({ - View: FormView, - model: 'sale_order', - data: this.data, - arch: getArch(), - mockRPC: function (route, params) { - if (params.method === 'read' && params.args[1][0] === 'detailed_type') { - assert.ok(true); - return Promise.resolve([{detailed_type: 'service'}]); - } - return this._super.apply(this, arguments); - }, - intercepts: { - do_action: function (ev) { - if (ev.data.action === 'event_sale.event_configurator_action') { - assert.ok(false, "Should not execute the configure action"); - } - }, - } - }); - - await testUtils.dom.click(form.$("a:contains('Add a product')")); - await testUtils.fields.many2one.searchAndClickItem("product_id", {item: 'Desk'}) - form.destroy(); - }); - - QUnit.test('Select a configurable event and verify that the event configurator is opened', async function (assert) { - assert.expect(2); - - var form = await createView({ - View: FormView, - model: 'sale_order', - data: this.data, - arch: getArch(), - mockRPC: function (route, params) { - if (params.method === 'read' && params.args[1][0] === 'detailed_type') { - assert.ok(true); - return Promise.resolve([{detailed_type: 'event'}]); - } - return this._super.apply(this, arguments); - }, - intercepts: { - do_action: function (ev) { - if (ev.data.action === 'event_sale.event_configurator_action') { - assert.ok(true); - } - }, - } - }); - - await testUtils.dom.click(form.$("a:contains('Add a product')")); - await testUtils.fields.many2one.searchAndClickItem("product_id", {item: 'Customizable Event'}); - form.destroy(); - }); - }); -}); diff --git a/addons/event_sale/static/tests/tours/event_configurator_ui.js b/addons/event_sale/static/tests/tours/event_configurator_ui.js index 5bf4fc4ffaf..a24d3ee9795 100644 --- a/addons/event_sale/static/tests/tours/event_configurator_ui.js +++ b/addons/event_sale/static/tests/tours/event_configurator_ui.js @@ -42,24 +42,23 @@ tour.register('event_configurator_tour', { }, { trigger: '.o_event_sale_js_event_configurator_ok' }, { - trigger: 'textarea[name="name"]', - run: function () { - var $textarea = $('textarea[name="name"]'); - if ($textarea.val().includes('Design Fair Los Angeles') && $textarea.val().includes('VIP')) { - $textarea.addClass('tour_success'); - } - } -}, { - trigger: 'textarea[name="name"].tour_success', + trigger: "td[name='name'][data-tooltip*='VIP']", run: function () {} // check }, { trigger: 'ul.nav a:contains("Order Lines")', run: 'click' +}, { + content: "search the partner", + trigger: 'div[name="partner_id"] input', + run: 'text Azure' +}, { + content: "select the partner", + trigger: 'ul.ui-autocomplete > li > a:contains(Azure)', }, { trigger: 'td:contains("Event")', run: 'click' }, { - trigger: '.o_edit_product_configuration' + trigger: 'button.fa-pencil' }, { trigger: 'div[name="event_ticket_id"] input', run: 'click' @@ -70,17 +69,9 @@ tour.register('event_configurator_tour', { }, { trigger: '.o_event_sale_js_event_configurator_ok' }, { - trigger: 'textarea[name="name"]', - run: function () { - var $textarea = $('textarea[name="name"]'); - if ($textarea.val().includes('Standard')) { - $textarea.addClass('tour_success_2'); - } - } -}, { - trigger: 'textarea[name="name"].tour_success_2', + trigger: "td[name='name'][data-tooltip*='Standard']", run: function () {} // check -}, ...tour.stepUtils.discardForm() +}, ...tour.stepUtils.saveForm() ]); }); diff --git a/addons/event_sale/views/sale_order_views.xml b/addons/event_sale/views/sale_order_views.xml index 6bf4ddeae4e..7b48def1f3b 100644 --- a/addons/event_sale/views/sale_order_views.xml +++ b/addons/event_sale/views/sale_order_views.xml @@ -19,7 +19,7 @@ ('date_end','>=',time.strftime('%Y-%m-%d 00:00:00')), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id) ]" - attrs="{'invisible': [('event_ok', '=', False)], 'required': [('event_ok', '!=', False)]}" + attrs="{'invisible': [('product_type', '!=', 'event')], 'required': [('product_type', '=', 'event')]}" options="{'no_open': True, 'no_create': True}" /> - - diff --git a/addons/product_matrix/__manifest__.py b/addons/product_matrix/__manifest__.py index d43d245d31d..3f2956ae5cf 100644 --- a/addons/product_matrix/__manifest__.py +++ b/addons/product_matrix/__manifest__.py @@ -20,7 +20,6 @@ Please refer to Sale Matrix or Purchase Matrix for the use of this module. ], 'assets': { 'web.assets_backend': [ - 'product_matrix/static/src/js/section_and_note_widget.js', 'product_matrix/static/src/scss/product_matrix.scss', 'product_matrix/static/src/xml/**/*', ], diff --git a/addons/product_matrix/static/src/js/section_and_note_widget.js b/addons/product_matrix/static/src/js/section_and_note_widget.js deleted file mode 100644 index 5d9499e52e0..00000000000 --- a/addons/product_matrix/static/src/js/section_and_note_widget.js +++ /dev/null @@ -1,148 +0,0 @@ -odoo.define('product_matrix.section_and_note_widget', function (require) { - -var Dialog = require('web.Dialog'); -var core = require('web.core'); -var _t = core._t; -var qweb = core.qweb; -const {Markup} = require('web.utils'); -var fieldRegistry = require('web.field_registry'); -var {format} = require('web.field_utils'); -require('account.section_and_note_backend'); - -var SectionAndNoteFieldOne2Many = fieldRegistry.get('section_and_note_one2many'); - -SectionAndNoteFieldOne2Many.include({ - custom_events: _.extend({}, SectionAndNoteFieldOne2Many.prototype.custom_events, { - open_matrix: '_openMatrix', - }), - - /** - * Sends the matrix modifications to the server - * Through a change on an invisible non stored field. - * - * @param {List} list of changes in the matrix, to be applied to the order. - * {integer} quantity: float - * {List} ptav_ids: product.template.attribute.value ids - * - * @private - */ - _applyGrid: function (changes, productTemplateId) { - // the getParent is to trigger the event on the form Controller instead of the one2many. - // If not, the one2many crashes on reset because it tries to find an operation in the event - // even if there isn't any. - // the only solution would be to use a custom event catched on a new controller - // on the so/po form (as a js_class). - this.trigger_up('field_changed', { - dataPointID: this.dataPointID, - changes: { - grid: JSON.stringify({changes: changes, product_template_id: productTemplateId}), - grid_update: true // to say that the changes to grid have to be applied to the SO. - }, - viewType: 'form', - }); - }, - - /** - * Catches the event asking for matrix opening - * - * @param {OdooEvent} ev various values needed to open the matrix - * {integer} data.product_template_id product.template id - * {list} data.editedCellAttributes list of product.template.attribute.value ids - * {bool} data.edit whether the line source should be deleted or not. - * - * @private - */ - _openMatrix: function (ev) { - ev.stopPropagation(); - var self = this; - var dataPointId = ev.data.dataPointId; - var productTemplateId = ev.data.product_template_id; - var editedCellAttributes = ev.data.editedCellAttributes; - if (!ev.data.edit) { - // remove the line used to open the matrix - this._setValue({operation: 'DELETE', ids: [dataPointId]}); - } - // the getParent is to trigger the event on the form Controller instead of the one2many. - // If not, the one2many crashes on reset because it tries to find an operation in the event - // even if there isn't any. - // the only solution would be to use a custom event catched on a new controller - // on the so/po form (as a js_class). - this.trigger_up('field_changed', { - dataPointID: this.dataPointID, - changes: { - grid_product_tmpl_id: {id: productTemplateId} - }, - viewType: 'form', - onSuccess: function () { - const gridInfo = self.recordData.grid; - self._openMatrixConfigurator(gridInfo, productTemplateId, editedCellAttributes); - } - }); - }, - - /** - * Triggers Matrix Dialog opening - * - * @param {String} jsonInfo matrix dialog content - * @param {integer} productTemplateId product.template id - * @param {editedCellAttributes} list of product.template.attribute.value ids - * used to focus on the matrix cell representing the edited line. - * - * @private - */ - _openMatrixConfigurator: function (jsonInfo, productTemplateId, editedCellAttributes) { - var self = this; - var infos = JSON.parse(jsonInfo); - var MatrixDialog = new Dialog(this, { - title: _t('Choose Product Variants'), - size: 'extra-large', // adapt size depending on matrix size? - $content: $(qweb.render( - 'product_matrix.matrix', { - header: infos.header, - rows: infos.matrix, - format({price, currency_id}) { - if (!price) { return ""; } - const sign = price < 0 ? '-' : '+'; - const formatted = format.monetary(Math.abs(price), null, {currency_id}); - return Markup`${sign} ${formatted}`; - } - } - )), - buttons: [ - {text: _t('Confirm'), classes: 'btn-primary', close: true, click: function (result) { - var $inputs = this.$('.o_matrix_input'); - var matrixChanges = []; - _.each($inputs, function (matrixInput) { - if (matrixInput.value && matrixInput.value !== matrixInput.attributes.value.nodeValue) { - matrixChanges.push({ - qty: parseFloat(matrixInput.value), - ptav_ids: matrixInput.attributes.ptav_ids.nodeValue.split(",").map(function (id) { - return parseInt(id); - }), - }); - } - }); - if (matrixChanges.length > 0) { - self._applyGrid(matrixChanges, productTemplateId); - } - }}, - {text: _t('Close'), close: true}, - ], - }).open(); - - MatrixDialog.opened(function () { - MatrixDialog.$content.closest('.o_dialog_container').removeClass('d-none'); - if (editedCellAttributes.length > 0) { - var str = editedCellAttributes.toString(); - MatrixDialog.$content.find('.o_matrix_input').filter((k, v) => v.attributes.ptav_ids.nodeValue === str)[0].focus(); - } else { - MatrixDialog.$content.find('.o_matrix_input:first()').focus(); - } - }); - }, - -}); - -return SectionAndNoteFieldOne2Many; - -}); diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml index 46b970ba22d..834152b9a0e 100644 --- a/addons/purchase/views/purchase_views.xml +++ b/addons/purchase/views/purchase_views.xml @@ -132,7 +132,7 @@ purchase.order.form purchase.order -
+