diff --git a/addons/lunch/__manifest__.py b/addons/lunch/__manifest__.py
index 452d93ff0ca..1142e121292 100644
--- a/addons/lunch/__manifest__.py
+++ b/addons/lunch/__manifest__.py
@@ -42,36 +42,23 @@ If you want to save your employees' time and avoid them to always have coins in
'application': True,
'assets': {
'web.assets_backend': [
+ 'lunch/static/src/components/*.js',
+ 'lunch/static/src/components/*.scss',
+ 'lunch/static/src/mixins/*.js',
+ 'lunch/static/src/views/*.js',
'lunch/static/src/scss/lunch_view.scss',
'lunch/static/src/scss/lunch_kanban.scss',
'lunch/static/src/scss/lunch_list.scss',
- 'lunch/static/src/js/lunch_controller_common.js',
- 'lunch/static/src/js/lunch_widget.js',
- 'lunch/static/src/js/lunch_mobile.js',
- 'lunch/static/src/js/lunch_payment_dialog.js',
- 'lunch/static/src/js/lunch_kanban_view.js',
- 'lunch/static/src/js/lunch_kanban_controller.js',
- 'lunch/static/src/js/lunch_kanban_renderer.js',
- 'lunch/static/src/js/lunch_kanban_record.js',
- 'lunch/static/src/js/lunch_model_extension.js',
- 'lunch/static/src/js/lunch_list_view.js',
- 'lunch/static/src/js/lunch_list_controller.js',
- 'lunch/static/src/js/lunch_list_renderer.js',
],
'web.assets_tests': [
'lunch/static/tests/tours/*.js',
],
'web.qunit_suite_tests': [
- 'lunch/static/tests/lunch_test_utils.js',
'lunch/static/tests/lunch_kanban_tests.js',
- 'lunch/static/tests/lunch_list_tests.js',
- ],
- 'web.qunit_mobile_suite_tests': [
- 'lunch/static/tests/lunch_test_utils.js',
- 'lunch/static/tests/lunch_kanban_mobile_tests.js',
],
'web.assets_qweb': [
- 'lunch/static/src/xml/lunch_templates.xml',
+ 'lunch/static/src/components/*.xml',
+ 'lunch/static/src/views/*.xml',
],
},
'license': 'LGPL-3',
diff --git a/addons/lunch/data/lunch_demo.xml b/addons/lunch/data/lunch_demo.xml
index 4a37949894c..dbf82ad216e 100644
--- a/addons/lunch/data/lunch_demo.xml
+++ b/addons/lunch/data/lunch_demo.xml
@@ -50,6 +50,7 @@
Coca Cola
2.9
+
@@ -214,6 +215,7 @@
3.0
+
diff --git a/addons/lunch/static/src/components/lunch_dashboard.js b/addons/lunch/static/src/components/lunch_dashboard.js
new file mode 100644
index 00000000000..b748c7b0082
--- /dev/null
+++ b/addons/lunch/static/src/components/lunch_dashboard.js
@@ -0,0 +1,179 @@
+/** @odoo-module */
+
+import { useBus, useService } from "@web/core/utils/hooks";
+import { Many2XAutocomplete } from "@web/views/fields/relational_utils";
+
+const { Component, useState, onWillStart, markup, xml } = owl;
+
+export class LunchCurrency extends Component {
+ get amount() {
+ return parseFloat(this.props.amount).toFixed(2);
+ }
+}
+LunchCurrency.template = 'lunch.LunchCurrency';
+LunchCurrency.props = ["currency", "amount"];
+
+export class LunchOrderLine extends Component {
+ setup() {
+ super.setup();
+ this.orm = useService('orm');
+ this.state = useState({ mobileOpen: false });
+ }
+
+ get line() {
+ return this.props.line;
+ }
+
+ get canEdit() {
+ return !['sent', 'confirmed'].includes(this.line.raw_state);
+ }
+
+ get badgeClass() {
+ const mapping = {'new': 'bg-warning', 'confirmed': 'bg-success', 'sent': 'bg-info', 'ordered': 'bg-danger'};
+ return mapping[this.line.raw_state];
+ }
+
+ get hasToppings() {
+ return this.line.toppings.length !== 0;
+ }
+
+ async updateQuantity(increment) {
+ await this.orm.call('lunch.order', 'update_quantity', [
+ this.props.line.id,
+ increment
+ ]);
+
+ await this.props.onUpdateQuantity();
+ }
+}
+LunchOrderLine.template = 'lunch.LunchOrderLine';
+LunchOrderLine.props = ["line", "currency", "onUpdateQuantity", "openOrderLine"];
+LunchOrderLine.components = {
+ LunchCurrency,
+};
+
+export class LunchAlert extends Component {
+ get message() {
+ return markup(this.props.message);
+ }
+}
+LunchAlert.props = ["message"];
+LunchAlert.template = xml``
+
+export class LunchAlerts extends Component {}
+LunchAlerts.components = {
+ LunchAlert,
+}
+LunchAlerts.props = ["alerts"];
+LunchAlerts.template = 'lunch.LunchAlerts';
+
+export class LunchUser extends Component {
+ getDomain() {
+ return [['share', '=', false]];
+ }
+}
+LunchUser.components = {
+ Many2XAutocomplete,
+}
+LunchUser.props = ["username", "isManager", "onUpdateUser"];
+LunchUser.template = "lunch.LunchUser";
+
+export class LunchLocation extends Component {
+ getDomain() {
+ return [];
+ }
+}
+LunchLocation.components = {
+ Many2XAutocomplete,
+}
+LunchLocation.props = ["location", "onUpdateLunchLocation"];
+LunchLocation.template = "lunch.LunchLocation";
+
+export class LunchDashboard extends Component {
+ setup() {
+ super.setup();
+ this.rpc = useService("rpc");
+ this.user = useService("user");
+ this.state = useState({
+ infos: {},
+ });
+
+ useBus(this.env.bus, 'lunch_update_dashboard', () => this._fetchLunchInfos());
+ onWillStart(async () => {
+ await this._fetchLunchInfos()
+ this.env.searchModel.updateLocationId(this.state.infos.user_location[0]);
+ });
+ }
+
+ async lunchRpc(route, args = {}) {
+ return await this.rpc(route, {
+ ...args,
+ context: this.user.context,
+ user_id: this.env.searchModel.lunchState.userId,
+ })
+ }
+
+ async _fetchLunchInfos() {
+ this.state.infos = await this.lunchRpc('/lunch/infos');
+ }
+
+ async emptyCart() {
+ await this.lunchRpc('/lunch/trash');
+ await this._fetchLunchInfos();
+ }
+
+ get hasLines() {
+ return this.state.infos.lines && this.state.infos.lines.length !== 0;
+ }
+
+ get canOrder() {
+ return this.state.infos.raw_state === 'new';
+ }
+
+ get location() {
+ return this.state.infos.user_location && this.state.infos.user_location[1];
+ }
+
+ async orderNow() {
+ if (!this.canOrder) {
+ return;
+ }
+
+ await this.lunchRpc('/lunch/pay');
+ await this._fetchLunchInfos();
+ }
+
+ async onUpdateQuantity() {
+ await this._fetchLunchInfos();
+ }
+
+ async onUpdateUser(value) {
+ if (!value) {
+ return;
+ }
+ this.env.searchModel.updateUserId(value[0].id);
+ await this._fetchLunchInfos();
+ }
+
+ async onUpdateLunchLocation(value) {
+ if (!value) {
+ return;
+ }
+
+ await this.lunchRpc('/lunch/user_location_set', {
+ location_id: value[0].id,
+ });
+ await this._fetchLunchInfos();
+ this.env.searchModel.updateLocationId(value[0].id);
+ }
+}
+LunchDashboard.components = {
+ LunchAlerts,
+ LunchCurrency,
+ LunchLocation,
+ LunchOrderLine,
+ LunchUser,
+ Many2XAutocomplete,
+};
+LunchDashboard.props = ["openOrderLine"];
+LunchDashboard.template = 'lunch.LunchDashboard';
diff --git a/addons/lunch/static/src/components/lunch_dashboard.scss b/addons/lunch/static/src/components/lunch_dashboard.scss
new file mode 100644
index 00000000000..d6bc1d508c6
--- /dev/null
+++ b/addons/lunch/static/src/components/lunch_dashboard.scss
@@ -0,0 +1,9 @@
+.lunch_topping:before {
+ content: '+ ';
+}
+
+.o_lunch_content {
+ .o-autocomplete--input {
+ cursor: pointer;
+ }
+}
diff --git a/addons/lunch/static/src/components/lunch_dashboard.xml b/addons/lunch/static/src/components/lunch_dashboard.xml
new file mode 100644
index 00000000000..7778036efc2
--- /dev/null
+++ b/addons/lunch/static/src/components/lunch_dashboard.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+ Your Account
+
+
+
+
+
+
+
+
+
+ Total
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your Cart ()
+
+
+
+
+
+
+
diff --git a/addons/lunch/static/src/js/lunch_controller_common.js b/addons/lunch/static/src/js/lunch_controller_common.js
deleted file mode 100644
index 95f3b2bfd6c..00000000000
--- a/addons/lunch/static/src/js/lunch_controller_common.js
+++ /dev/null
@@ -1,219 +0,0 @@
-odoo.define('lunch.LunchControllerCommon', function (require) {
-"use strict";
-
-/**
- * This file defines the common events and functions used by Controllers for the Lunch view.
- */
-
-var session = require('web.session');
-var core = require('web.core');
-const {Markup} = require('web.utils');
-var LunchWidget = require('lunch.LunchWidget');
-var LunchPaymentDialog = require('lunch.LunchPaymentDialog');
-
-var _t = core._t;
-
-var LunchControllerCommon = {
- custom_events: {
- add_product: '_onAddProduct',
- change_location: '_onLocationChanged',
- change_user: '_onUserChanged',
- open_wizard: '_onOpenWizard',
- order_now: '_onOrderNow',
- remove_product: '_onRemoveProduct',
- unlink_order: '_onUnlinkOrder',
- },
- /**
- * @override
- */
- init: function () {
- this._super.apply(this, arguments);
- this.editMode = false;
- this.updated = false;
- this.widgetData = null;
- this.context = session.user_context;
- this.archiveEnabled = false;
- },
- /**
- * @override
- */
- start: function () {
- // create a div inside o_content that will be used to wrap the lunch
- // banner and renderer (this is required to get the desired
- // layout with the searchPanel to the left)
- var self = this;
- this.$('.o_content').append($('
').addClass('o_lunch_content'));
- return this._super.apply(this, arguments).then(function () {
- self.$('.o_lunch_content').append(self.$('.o_lunch_view'));
- });
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- _fetchPaymentInfo: function () {
- return this._rpc({
- route: '/lunch/payment_message',
- params: {
- context: this.context,
- },
- });
- },
- async _fetchWidgetData() {
- const widgetData = await this._rpc({
- route: '/lunch/infos',
- params: {
- user_id: this.searchModel.get('userId'),
- context: this.context,
- },
- });
- widgetData.wallet = parseFloat(widgetData.wallet).toFixed(2);
- (widgetData.alerts || []).forEach(alert => { alert.message = Markup(alert.message); });
- this.widgetData = widgetData;
- },
- /**
- * Renders and appends the lunch banner widget.
- *
- * @private
- */
- _renderLunchWidget: function () {
- var oldWidget = this.widget;
- this.widget = new LunchWidget(this, Object.assign(this.widgetData, {edit: this.editMode}));
- return this.widget.appendTo(document.createDocumentFragment()).then(() => {
- this.$('.o_lunch_content').prepend(this.widget.$el);
- if (oldWidget) {
- oldWidget.destroy();
- }
- });
- },
- _showPaymentDialog: function (title) {
- var self = this;
-
- title = title || '';
-
- this._fetchPaymentInfo().then(function (data) {
- var paymentDialog = new LunchPaymentDialog(self, _.extend(data, {title: title}));
- paymentDialog.open();
- });
- },
- /**
- * Override to fetch and display the lunch data. Because of the presence of
- * the searchPanel, also wrap the lunch widget and the renderer into
- * a div, to get the desired layout.
- *
- * @override
- * @private
- */
- _update: function () {
- var def = this._fetchWidgetData().then(this._renderLunchWidget.bind(this));
- return Promise.all([def, this._super.apply(this, arguments)]);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- _onAddProduct: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- model: 'lunch.order',
- method: 'update_quantity',
- args: [[ev.data.lineId], 1],
- }).then(function () {
- self.reload();
- });
- },
- _onLocationChanged: function (ev) {
- ev.stopPropagation();
- this.searchModel.dispatch('setLocationId', ev.data.locationId);
- },
- _onOpenWizard: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- var ctx = this.searchModel.get('userId') ? {default_user_id: this.searchModel.get('userId')} : {};
-
- var options = {
- on_close: function () {
- self.reload();
- },
- };
-
- var action = {
- res_model: 'lunch.order',
- name: _t('Configure Your Order'),
- type: 'ir.actions.act_window',
- views: [[false, 'form']],
- target: 'new',
- context: _.extend(ctx, {default_product_id: ev.data.productId}),
- };
-
- if (ev.data.lineId) {
- action = _.extend(action, {
- res_id: ev.data.lineId,
- context: _.extend(action.context, {
- active_id: ev.data.lineId,
- }),
- });
- }
-
- this.do_action(action, options);
- },
- _onOrderNow: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- route: '/lunch/pay',
- params: {
- user_id: this.searchModel.get('userId'),
- context: this.context,
- },
- }).then(function (isPaid) {
- if (isPaid) {
- // TODO: feedback?
- self.reload();
- } else {
- self._showPaymentDialog(_t("Not enough money in your wallet"));
- self.reload();
- }
- });
- },
- _onRemoveProduct: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- model: 'lunch.order',
- method: 'update_quantity',
- args: [[ev.data.lineId], -1],
- }).then(function () {
- self.reload();
- });
- },
- _onUserChanged: function (ev) {
- ev.stopPropagation();
- this.searchModel.dispatch('updateUserId', ev.data.userId);
- },
- _onUnlinkOrder: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- route: '/lunch/trash',
- params: {
- user_id: this.searchModel.get('userId'),
- context: this.context,
- },
- }).then(function () {
- self.reload();
- });
- },
-};
-
-return LunchControllerCommon;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_controller.js b/addons/lunch/static/src/js/lunch_kanban_controller.js
deleted file mode 100644
index dc370a46277..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_controller.js
+++ /dev/null
@@ -1,18 +0,0 @@
-odoo.define('lunch.LunchKanbanController', function (require) {
-"use strict";
-
-/**
- * This file defines the Controller for the Lunch Kanban view, which is an
- * override of the KanbanController.
- */
-
-var KanbanController = require('web.KanbanController');
-var LunchControllerCommon = require('lunch.LunchControllerCommon');
-
-var LunchKanbanController = KanbanController.extend(LunchControllerCommon , {
- custom_events: _.extend({}, KanbanController.prototype.custom_events, LunchControllerCommon.custom_events),
-});
-
-return LunchKanbanController;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_record.js b/addons/lunch/static/src/js/lunch_kanban_record.js
deleted file mode 100644
index f58463b26ae..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_record.js
+++ /dev/null
@@ -1,38 +0,0 @@
-odoo.define('lunch.LunchKanbanRecord', function (require) {
- "use strict";
-
- /**
- * This file defines the KanbanRecord for the Lunch Kanban view.
- */
-
- var KanbanRecord = require('web.KanbanRecord');
-
- var LunchKanbanRecord = KanbanRecord.extend({
- events: _.extend({}, KanbanRecord.prototype.events, {
- 'click': '_onSelectRecord',
- }),
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Open the add product wizard
- *
- * @private
- * @param {MouseEvent} ev Click event
- */
- _onSelectRecord: function (ev) {
- ev.preventDefault();
- // ignore clicks on oe_kanban_action elements
- if (!$(ev.target).hasClass('oe_kanban_action')) {
- this.trigger_up('open_wizard', {productId: this.recordData.product_id ? this.recordData.product_id.res_id: this.recordData.id});
- }
- },
-
- _openRecord() {},
- });
-
- return LunchKanbanRecord;
-
- });
diff --git a/addons/lunch/static/src/js/lunch_kanban_renderer.js b/addons/lunch/static/src/js/lunch_kanban_renderer.js
deleted file mode 100644
index bea87992928..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_renderer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-odoo.define('lunch.LunchKanbanRenderer', function (require) {
-"use strict";
-
-/**
- * This file defines the Renderer for the Lunch Kanban view, which is an
- * override of the KanbanRenderer.
- */
-
-var LunchKanbanRecord = require('lunch.LunchKanbanRecord');
-
-var KanbanRenderer = require('web.KanbanRenderer');
-
-var LunchKanbanRenderer = KanbanRenderer.extend({
- config: _.extend({}, KanbanRenderer.prototype.config, {
- KanbanRecord: LunchKanbanRecord,
- }),
-
- /**
- * @override
- */
- start: function () {
- this.$el.addClass('o_lunch_view o_lunch_kanban_view position-relative align-content-start flex-grow-1 flex-shrink-1');
- return this._super.apply(this, arguments);
- },
-});
-
-return LunchKanbanRenderer;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_view.js b/addons/lunch/static/src/js/lunch_kanban_view.js
deleted file mode 100644
index 50d347de309..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_view.js
+++ /dev/null
@@ -1,33 +0,0 @@
-odoo.define('lunch.LunchKanbanView', function (require) {
-"use strict";
-
-var LunchKanbanController = require('lunch.LunchKanbanController');
-var LunchKanbanRenderer = require('lunch.LunchKanbanRenderer');
-
-var core = require('web.core');
-var KanbanView = require('web.KanbanView');
-var view_registry = require('web.view_registry');
-
-var _lt = core._lt;
-
-var LunchKanbanView = KanbanView.extend({
- config: _.extend({}, KanbanView.prototype.config, {
- Controller: LunchKanbanController,
- Renderer: LunchKanbanRenderer,
- }),
- display_name: _lt('Lunch Kanban'),
-
- /**
- * @override
- */
- _createSearchModel(params, extraExtensions = {}) {
- Object.assign(extraExtensions, { Lunch: {} });
- return this._super(params, extraExtensions);
- },
-});
-
-view_registry.add('lunch_kanban', LunchKanbanView);
-
-return LunchKanbanView;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_list_controller.js b/addons/lunch/static/src/js/lunch_list_controller.js
deleted file mode 100644
index bdfbab6b107..00000000000
--- a/addons/lunch/static/src/js/lunch_list_controller.js
+++ /dev/null
@@ -1,18 +0,0 @@
-odoo.define('lunch.LunchListController', function (require) {
-"use strict";
-
-/**
- * This file defines the Controller for the Lunch List view, which is an
- * override of the ListController.
- */
-
-var ListController = require('web.ListController');
-var LunchControllerCommon = require('lunch.LunchControllerCommon');
-
-var LunchListController = ListController.extend(LunchControllerCommon, {
- custom_events: _.extend({}, ListController.prototype.custom_events, LunchControllerCommon.custom_events),
-});
-
-return LunchListController;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_list_renderer.js b/addons/lunch/static/src/js/lunch_list_renderer.js
deleted file mode 100644
index 0ab0b5c9c13..00000000000
--- a/addons/lunch/static/src/js/lunch_list_renderer.js
+++ /dev/null
@@ -1,56 +0,0 @@
-odoo.define('lunch.LunchListRenderer', function (require) {
-"use strict";
-
-/**
- * This file defines the Renderer for the Lunch List view, which is an
- * override of the ListRenderer.
- */
-
-var ListRenderer = require('web.ListRenderer');
-
-var LunchListRenderer = ListRenderer.extend({
- events: _.extend({}, ListRenderer.prototype.events, {
- 'click .o_data_row': '_onClickListRow',
- }),
-
- /**
- * @override
- */
- start: function () {
- this.$el.addClass('o_lunch_view o_lunch_list_view');
- return this._super.apply(this, arguments);
- },
- /**
- * Override to add id of product_id in dataset.
- *
- * @override
- */
- _renderRow: function (record) {
- var tr = this._super.apply(this, arguments);
- tr.attr('data-product-id', record.data.id);
- return tr;
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Open the add product wizard
- *
- * @private
- * @param {MouseEvent} ev Click event
- */
- _onClickListRow: function (ev) {
- ev.preventDefault();
- var productId = ev.currentTarget.dataset && ev.currentTarget.dataset.productId ? parseInt(ev.currentTarget.dataset.productId) : null;
-
- if (productId) {
- this.trigger_up('open_wizard', {productId: productId});
- }
- },
-});
-
-return LunchListRenderer;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_list_view.js b/addons/lunch/static/src/js/lunch_list_view.js
deleted file mode 100644
index 00e8efa5aba..00000000000
--- a/addons/lunch/static/src/js/lunch_list_view.js
+++ /dev/null
@@ -1,38 +0,0 @@
-odoo.define('lunch.LunchListView', function (require) {
-"use strict";
-
-var LunchListController = require('lunch.LunchListController');
-var LunchListRenderer = require('lunch.LunchListRenderer');
-
-var core = require('web.core');
-var ListView = require('web.ListView');
-var view_registry = require('web.view_registry');
-
-var _lt = core._lt;
-
-var LunchListView = ListView.extend({
- config: _.extend({}, ListView.prototype.config, {
- Controller: LunchListController,
- Renderer: LunchListRenderer,
- }),
- display_name: _lt('Lunch List'),
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- */
- _createSearchModel(params, extraExtensions = {}) {
- Object.assign(extraExtensions, { Lunch: {} });
- return this._super(params, extraExtensions);
- },
-
-});
-
-view_registry.add('lunch_list', LunchListView);
-
-return LunchListView;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_mobile.js b/addons/lunch/static/src/js/lunch_mobile.js
deleted file mode 100644
index f6ad1e92081..00000000000
--- a/addons/lunch/static/src/js/lunch_mobile.js
+++ /dev/null
@@ -1,78 +0,0 @@
-odoo.define('lunch.LunchMobile', function (require) {
-"use strict";
-
-var config = require('web.config');
-var LunchWidget = require('lunch.LunchWidget');
-var LunchKanbanController = require('lunch.LunchKanbanController');
-var LunchListController = require('lunch.LunchListController');
-
-if (!config.device.isMobile) {
- return;
-}
-
-LunchWidget.include({
- template: "LunchWidgetMobile",
-
- /**
- * Override to set the toggle state allowing initially open it.
- *
- * @override
- */
- init: function (parent, params) {
- this._super.apply(this, arguments);
- this.keepOpen = params.keepOpen || undefined;
- },
-});
-
-var mobileFunctions = {
- /**
- * @override
- */
- init: function () {
- this._super.apply(this, arguments);
- this.openWidget = false;
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Override to add the widget's toggle state to its data.
- *
- * @override
- * @private
- */
- _renderLunchWidget: function () {
- this.widgetData.keepOpen = this.openWidget;
- this.openWidget = false;
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- */
- _onAddProduct: function () {
- this.openWidget = true;
- this._super.apply(this, arguments);
- },
-
- /**
- * @override
- * @private
- */
- _onRemoveProduct: function () {
- this.openWidget = true;
- this._super.apply(this, arguments);
- },
-};
-
-LunchKanbanController.include(mobileFunctions);
-LunchListController.include(mobileFunctions);
-
-});
diff --git a/addons/lunch/static/src/js/lunch_model.js b/addons/lunch/static/src/js/lunch_model.js
deleted file mode 100644
index 720b43565ec..00000000000
--- a/addons/lunch/static/src/js/lunch_model.js
+++ /dev/null
@@ -1,130 +0,0 @@
-odoo.define('lunch.LunchModel', function (require) {
-"use strict";
-
-/**
- * This file defines the Model for the Lunch Kanban view, which is an
- * override of the KanbanModel.
- */
-
-var session = require('web.session');
-var BasicModel = require('web.BasicModel');
-
-var LunchModel = BasicModel.extend({
- init: function () {
- this.locationId = false;
- this.userId = false;
- this._promInitLocation = null;
-
- this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * @return {Promise} resolved with the location domain
- */
- getLocationDomain: function () {
- var self = this;
- return this._initUserLocation().then(function () {
- return self._buildLocationDomainLeaf() ? [self._buildLocationDomainLeaf()]: [];
- });
- },
- __load: function () {
- var self = this;
- var args = arguments;
- var _super = this._super;
-
- return this._initUserLocation().then(function () {
- var params = args[0];
- self._addOrUpdate(params.domain, self._buildLocationDomainLeaf());
-
- return _super.apply(self, args);
- });
- },
- __reload: function (id, options) {
- var domain = options && options.domain || this.localData[id].domain;
-
- this._addOrUpdate(domain, this._buildLocationDomainLeaf());
- options = _.extend(options, {domain: domain});
-
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- _addOrUpdate: function (domain, subDomain) {
- if (subDomain && subDomain.length) {
- var key = subDomain[0];
- var index = _.findIndex(domain, function (val) {
- return val[0] === key;
- });
-
- if (index < 0) {
- domain.push(subDomain);
- } else {
- domain[index] = subDomain;
- }
-
- return domain;
- }
-
- return domain;
- },
- /**
- * Builds the domain leaf corresponding to the current user's location
- *
- * @private
- * @return {(Array[])|undefined}
- */
- _buildLocationDomainLeaf: function () {
- if (this.locationId) {
- return ['is_available_at', 'in', [this.locationId]];
- }
- },
- _getUserLocation: function () {
- return this._rpc({
- route: '/lunch/user_location_get',
- params: {
- context: session.user_context,
- user_id: this.userId,
- },
- });
- },
- /**
- * Gets the user location once.
- * Can be triggered from anywhere
- * Useful to inject the location domain in the search panel
- *
- * @private
- * @return {Promise}
- */
- _initUserLocation: function () {
- var self = this;
- if (!this._promInitLocation) {
- this._promInitLocation = new Promise(function (resolve) {
- self._getUserLocation().then(function (locationId) {
- self.locationId = locationId;
- resolve();
- });
- });
- }
- return this._promInitLocation;
- },
- _updateLocation: function (locationId) {
- this.locationId = locationId;
- return Promise.resolve();
- },
- _updateUser: function (userId) {
- this.userId = userId;
- this._promInitLocation = null;
- return this._initUserLocation();
- }
-});
-
-return LunchModel;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_model_extension.js b/addons/lunch/static/src/js/lunch_model_extension.js
deleted file mode 100644
index 15b03d31e56..00000000000
--- a/addons/lunch/static/src/js/lunch_model_extension.js
+++ /dev/null
@@ -1,100 +0,0 @@
-odoo.define("lunch/static/src/js/lunch_model_extension.js", function (require) {
- "use strict";
-
- const ActionModel = require("web.ActionModel");
-
- class LunchModelExtension extends ActionModel.Extension {
-
- //---------------------------------------------------------------------
- // Public
- //---------------------------------------------------------------------
-
- /**
- * @override
- * @returns {any}
- */
- get(property) {
- switch (property) {
- case "domain": return this.getDomain();
- case "userId": return this.state.userId;
- }
- }
-
- /**
- * @override
- */
- async load() {
- await this._updateLocationId();
- }
-
- /**
- * @override
- */
- prepareState() {
- Object.assign(this.state, {
- locationId: null,
- userId: null,
- });
- }
-
- //---------------------------------------------------------------------
- // Actions / Getters
- //---------------------------------------------------------------------
-
- /**
- * @returns {Array[] | null}
- */
- getDomain() {
- if (this.state.locationId) {
- return [["is_available_at", "in", [this.state.locationId]]];
- }
- return null;
- }
-
- /**
- * @param {number} locationId
- * @returns {Promise}
- */
- setLocationId(locationId) {
- this.state.locationId = locationId;
- this.env.services.rpc({
- route: "/lunch/user_location_set",
- params: {
- context: this.env.session.user_context,
- location_id: this.state.locationId,
- user_id: this.state.userId,
- },
- });
- }
-
- /**
- * @param {number} userId
- * @returns {Promise}
- */
- updateUserId(userId) {
- this.state.userId = userId;
- this.shouldLoad = true;
- }
-
- //---------------------------------------------------------------------
- // Private
- //---------------------------------------------------------------------
-
- /**
- * @returns {Promise}
- */
- async _updateLocationId() {
- this.state.locationId = await this.env.services.rpc({
- route: "/lunch/user_location_get",
- params: {
- context: this.env.session.user_context,
- user_id: this.state.userId,
- },
- });
- }
- }
-
- ActionModel.registry.add("Lunch", LunchModelExtension, 20);
-
- return LunchModelExtension;
-});
diff --git a/addons/lunch/static/src/js/lunch_payment_dialog.js b/addons/lunch/static/src/js/lunch_payment_dialog.js
deleted file mode 100644
index ae624403f12..00000000000
--- a/addons/lunch/static/src/js/lunch_payment_dialog.js
+++ /dev/null
@@ -1,20 +0,0 @@
-odoo.define('lunch.LunchPaymentDialog', function (require) {
-"use strict";
-
-var Dialog = require('web.Dialog');
-
-var LunchPaymentDialog = Dialog.extend({
- template: 'lunch.LunchPaymentDialog',
-
- init: function (parent, options) {
- this._super.apply(this, arguments);
-
- options = options || {};
-
- this.message = options.message || '';
- },
-});
-
-return LunchPaymentDialog;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_widget.js b/addons/lunch/static/src/js/lunch_widget.js
deleted file mode 100644
index c58cdb6df3f..00000000000
--- a/addons/lunch/static/src/js/lunch_widget.js
+++ /dev/null
@@ -1,164 +0,0 @@
-odoo.define('lunch.LunchWidget', function (require) {
-"use strict";
-
-var core = require('web.core');
-var relationalFields = require('web.relational_fields');
-var session = require('web.session');
-var Widget = require('web.Widget');
-
-var _t = core._t;
-var FieldMany2One = relationalFields.FieldMany2One;
-
-
-var LunchMany2One = FieldMany2One.extend({
- start: function () {
- this.$el.addClass('w-100');
- return this._super.apply(this, arguments);
- }
-});
-
-var LunchWidget = Widget.extend({
- template: 'LunchWidget',
- custom_events: {
- field_changed: '_onFieldChanged',
- },
- events: {
- 'click .o_add_product': '_onAddProduct',
- 'click .o_lunch_widget_order_button': '_onOrderNow',
- 'click .o_remove_product': '_onRemoveProduct',
- 'click .o_lunch_widget_unlink': '_onUnlinkOrder',
- 'click .o_lunch_open_wizard': '_onLunchOpenWizard',
- },
-
- init: function (parent, params) {
- this._super.apply(this, arguments);
-
- this.is_manager = params.is_manager || false;
- this.group_portal_id = params.group_portal_id || false;
- this.userimage = params.userimage || '';
- this.username = params.username || '';
-
- this.lunchUserField = null;
-
- this.locations = params.locations || [];
- this.userLocation = params.user_location[1] || '';
-
- const company_ids = [false].concat(session.user_context.allowed_company_ids || []);
- this.lunchLocationField = this._createMany2One('locations', 'lunch.location', this.userLocation, () => [
- ['company_id', 'in', company_ids]
- ]);
-
- this.wallet = params.wallet || 0;
- this.raw_state = params.raw_state || 'new';
- this.state = params.state || _t('To Order');
- this.lines = params.lines || [];
- this.total = params.total || 0;
-
- this.alerts = params.alerts || [];
-
- this.currency = params.currency || session.get_currency(session.company_currency_id);
- },
- willStart: function () {
- var superDef = this._super.apply(this, arguments);
-
- if (this.is_manager) {
- this.lunchUserField = this._createMany2One('users', 'res.users', this.username,
- () => [['groups_id', 'not in', [this.group_portal_id]]]
- );
- }
- return superDef;
- },
- renderElement: function () {
- this._super.apply(this, arguments);
- if (this.lunchUserField) {
- this.lunchUserField.appendTo(this.$('.o_lunch_user_field'));
- } else {
- this.$('.o_lunch_user_field').text(this.username);
- }
-
- if (this.userLocation) {
- this.lunchLocationField.appendTo(this.$('.o_lunch_location_field'));
- }
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- _createMany2One: function (name, model, value, domain, context) {
- var fields = {};
- fields[name] = {type: 'many2one', relation: model, string: name};
- var data = {};
- data[name] = {data: {display_name: value}};
-
- var record = {
- id: name,
- res_id: 1,
- model: 'dummy',
- fields: fields,
- fieldsInfo: {
- default: fields,
- },
- data: data,
- getDomain: domain || function () { return []; },
- getContext: context || function () { return {}; },
- };
- var options = {
- mode: 'edit',
- noOpen: true,
- attrs: {
- can_create: false,
- can_write: false,
- }
- };
- return new LunchMany2One(this, name, record, options);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- _onAddProduct: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
- this.trigger_up('add_product', {lineId: $(ev.currentTarget).data('id')});
- },
- _onOrderNow: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('order_now', {});
- },
- _onLunchOpenWizard: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- var target = $(ev.currentTarget);
- this.trigger_up('open_wizard', {productId: target.data('product-id'), lineId: target.data('id')});
- },
- _onFieldChanged: function (ev) {
- ev.stopPropagation();
-
- if (ev.data.dataPointID === 'users') {
- this.trigger_up('change_user', {userId: ev.data.changes.users.id});
- } else if (ev.data.dataPointID === 'locations') {
- this.trigger_up('change_location', {locationId: ev.data.changes.locations.id});
- }
- },
- _onRemoveProduct: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('remove_product', {lineId: $(ev.currentTarget).data('id')});
- },
- _onUnlinkOrder: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('unlink_order', {});
- },
-});
-
-return LunchWidget;
-
-});
diff --git a/addons/lunch/static/src/mixins/lunch_renderer_mixin.js b/addons/lunch/static/src/mixins/lunch_renderer_mixin.js
new file mode 100644
index 00000000000..b27b8fd57c4
--- /dev/null
+++ b/addons/lunch/static/src/mixins/lunch_renderer_mixin.js
@@ -0,0 +1,40 @@
+/** @odoo-module */
+
+import { useBus, useService } from "@web/core/utils/hooks";
+
+export const LunchRendererMixin = {
+ setup() {
+ this._super(...arguments);
+
+ this.action = useService("action");
+ useBus(this.env.bus, 'lunch_open_order', (ev) => this.openOrderLine(ev.detail.productId));
+ },
+
+ openOrderLine(productId, orderId) {
+ let context = {};
+
+ if (this.env.searchModel.lunchState.userId) {
+ context['default_user_id'] = this.env.searchModel.lunchState.userId;
+ }
+
+ let action = {
+ res_model: 'lunch.order',
+ name: this.env._t('Configure Your Order'),
+ type: 'ir.actions.act_window',
+ views: [[false, 'form']],
+ target: 'new',
+ context: {
+ ...context,
+ default_product_id: productId,
+ },
+ };
+
+ if (orderId) {
+ action['res_id'] = orderId;
+ }
+
+ this.action.doAction(action, {
+ onClose: () => this.env.bus.trigger('lunch_update_dashboard')
+ });
+ },
+}
diff --git a/addons/lunch/static/src/scss/lunch_kanban.scss b/addons/lunch/static/src/scss/lunch_kanban.scss
index 6cbf1da507d..ca1aa4eb3cc 100644
--- a/addons/lunch/static/src/scss/lunch_kanban.scss
+++ b/addons/lunch/static/src/scss/lunch_kanban.scss
@@ -1,5 +1,5 @@
.o_lunch_content {
- .o_legacy_kanban_view {
+ .o_kanban_renderer {
flex: 1 1 100%;
&.o_kanban_grouped {
min-height: auto; // override min-height: 100%
diff --git a/addons/lunch/static/src/views/kanban.js b/addons/lunch/static/src/views/kanban.js
new file mode 100644
index 00000000000..2f49d27fef3
--- /dev/null
+++ b/addons/lunch/static/src/views/kanban.js
@@ -0,0 +1,36 @@
+/** @odoo-module */
+
+import { patch } from '@web/core/utils/patch';
+import { registry } from '@web/core/registry';
+
+import { kanbanView } from '@web/views/kanban/kanban_view';
+import { KanbanRecord } from '@web/views/kanban/kanban_record';
+import { KanbanRenderer } from '@web/views/kanban/kanban_renderer';
+
+import { LunchDashboard } from '../components/lunch_dashboard';
+import { LunchRendererMixin } from '../mixins/lunch_renderer_mixin';
+
+import { LunchSearchModel } from './search_model';
+
+
+export class LunchKanbanRecord extends KanbanRecord {
+ onGlobalClick(ev) {
+ this.env.bus.trigger('lunch_open_order', {productId: this.props.record.resId});
+ }
+}
+
+export class LunchKanbanRenderer extends KanbanRenderer {}
+patch(LunchKanbanRenderer.prototype, 'lunch_kanban_renderer_mixin', LunchRendererMixin);
+
+LunchKanbanRenderer.template = 'lunch.KanbanRenderer';
+LunchKanbanRenderer.components = {
+ ...LunchKanbanRenderer.components,
+ LunchDashboard,
+ KanbanRecord: LunchKanbanRecord,
+}
+
+registry.category('views').add('lunch_kanban', {
+ ...kanbanView,
+ Renderer: LunchKanbanRenderer,
+ SearchModel: LunchSearchModel,
+});
diff --git a/addons/lunch/static/src/views/kanban.xml b/addons/lunch/static/src/views/kanban.xml
new file mode 100644
index 00000000000..d4b7f837f5a
--- /dev/null
+++ b/addons/lunch/static/src/views/kanban.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/addons/lunch/static/src/views/list.js b/addons/lunch/static/src/views/list.js
new file mode 100644
index 00000000000..09661d84592
--- /dev/null
+++ b/addons/lunch/static/src/views/list.js
@@ -0,0 +1,32 @@
+/** @odoo-module */
+
+import { patch } from '@web/core/utils/patch';
+import { registry } from '@web/core/registry';
+
+import { listView } from '@web/views/list/list_view';
+import { ListRenderer } from '@web/views/list/list_renderer';
+
+import { LunchDashboard } from '../components/lunch_dashboard';
+import { LunchRendererMixin } from '../mixins/lunch_renderer_mixin';
+
+import { LunchSearchModel } from './search_model';
+
+
+export class LunchListRenderer extends ListRenderer {
+ onCellClicked(record, column) {
+ this.openOrderLine(record.resId);
+ }
+}
+patch(LunchListRenderer.prototype, 'lunch_list_renderer_mixin', LunchRendererMixin);
+
+LunchListRenderer.template = 'lunch.ListRenderer';
+LunchListRenderer.components = {
+ ...LunchListRenderer.components,
+ LunchDashboard,
+}
+
+registry.category('views').add('lunch_list', {
+ ...listView,
+ Renderer: LunchListRenderer,
+ SearchModel: LunchSearchModel,
+});
diff --git a/addons/lunch/static/src/views/list.xml b/addons/lunch/static/src/views/list.xml
new file mode 100644
index 00000000000..b002cf8e7fd
--- /dev/null
+++ b/addons/lunch/static/src/views/list.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/addons/lunch/static/src/views/search_model.js b/addons/lunch/static/src/views/search_model.js
new file mode 100644
index 00000000000..9c4fb53ab51
--- /dev/null
+++ b/addons/lunch/static/src/views/search_model.js
@@ -0,0 +1,67 @@
+/** @odoo-module */
+
+import { useService } from "@web/core/utils/hooks";
+
+import { Domain } from '@web/core/domain';
+import { SearchModel } from '@web/search/search_model';
+
+const { useState, onWillStart } = owl;
+
+
+export class LunchSearchModel extends SearchModel {
+ setup() {
+ super.setup(...arguments);
+
+ this.rpc = useService('rpc');
+ this.lunchState = useState({
+ locationId: false,
+ userId: false,
+ });
+
+ onWillStart(async () => {
+ const locationId = await this.rpc('/lunch/user_location_get', {});
+ this.updateLocationId(locationId);
+ });
+ }
+
+ exportState() {
+ const state = super.exportState();
+ state.locationId = this.lunchState.locationId;
+ state.userId = this.lunchState.userId;
+ return state;
+ }
+
+ _importState(state) {
+ super._importState(...arguments);
+
+ if (state.locationId) {
+ this.lunchState.locationId = state.locationId;
+ }
+ if (state.userId) {
+ this.lunchState.userId = state.userId;
+ }
+ }
+
+ updateUserId(userId) {
+ this.lunchState.userId = userId;
+ this._notify();
+ }
+
+ updateLocationId(locationId) {
+ this.lunchState.locationId = locationId;
+ this._notify();
+ }
+
+ _getDomain(params = {}) {
+ const domain = super._getDomain(params);
+
+ if (!this.lunchState.locationId) {
+ return domain;
+ }
+
+ return Domain.and([
+ domain,
+ [['is_available_at', '=', this.lunchState.locationId]]
+ ]).toList();
+ }
+}
diff --git a/addons/lunch/static/src/xml/lunch_templates.xml b/addons/lunch/static/src/xml/lunch_templates.xml
deleted file mode 100644
index 9140a03d3ae..00000000000
--- a/addons/lunch/static/src/xml/lunch_templates.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- ctx.value = _.str.sprintf('%.2f', parseFloat(ctx.value));
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Your cart
- (
-
-
- )
-
-
-
-
-
diff --git a/addons/lunch/static/tests/lunch_kanban_tests.js b/addons/lunch/static/tests/lunch_kanban_tests.js
index 13a53618eb0..f7c63b49d09 100644
--- a/addons/lunch/static/tests/lunch_kanban_tests.js
+++ b/addons/lunch/static/tests/lunch_kanban_tests.js
@@ -1,980 +1,415 @@
-odoo.define('lunch.lunchKanbanTests', function (require) {
-"use strict";
+/** @odoo-module */
-const LunchKanbanView = require('lunch.LunchKanbanView');
+import { click, getFixture, nextTick, patchWithCleanup } from '@web/../tests/helpers/utils';
+import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
-const testUtils = require('web.test_utils');
-const {createLunchView, mockLunchRPC} = require('lunch.test_utils');
+import { LunchKanbanRenderer } from '@lunch/views/kanban';
-QUnit.module('Views');
+let target;
+let serverData;
+let lunchInfos;
-QUnit.module('LunchKanbanView', {
- beforeEach() {
- const PORTAL_GROUP_ID = 1234;
+async function makeLunchView(extraArgs = {}) {
+ return await makeView(
+ Object.assign({
+ serverData,
+ type: "kanban",
+ resModel: "lunch.product",
+ arch: `
+
+
+
+
+
+
+
+
+
+ `,
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(serverData.models['lunch.location'].records[0].id);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(lunchInfos);
+ }
+ }
+ }, extraArgs
+ ));
+}
- this.data = {
- 'product': {
- fields: {
- is_available_at: {string: 'Product Availability', type: 'many2one', relation: 'lunch.location'},
- category_id: {string: 'Product Category', type: 'many2one', relation: 'lunch.product.category'},
- supplier_id: {string: 'Vendor', type: 'many2one', relation: 'lunch.supplier'},
+QUnit.module('Lunch', {}, function() {
+QUnit.module('LunchKanban', (hooks) => {
+ hooks.beforeEach(() => {
+ target = getFixture();
+ serverData = {
+ models: {
+ 'lunch.product': {
+ fields: {
+ id: { string: "ID", type: "integer" },
+ name: { string: 'Name', type: 'char' },
+ is_available_at: { string: 'Available', type: 'integer' },
+ price: { string: 'Price', type: 'float', },
+ },
+ records: [
+ { id: 1, name: "Big Plate", is_available_at: 1, price: 4.95, },
+ { id: 2, name: "Small Plate", is_available_at: 2, price: 6.99, },
+ { id: 3, name: "Just One Plate", is_available_at: 2, price: 5.87, },
+ ]
},
- records: [
- {id: 1, name: 'Tuna sandwich', is_available_at: 1},
- ],
- },
- 'lunch.order': {
- fields: {},
- update_quantity() {
- return Promise.resolve();
+ 'lunch.location': {
+ fields: {
+ name: { string: 'Name', type: 'char' },
+ },
+ records: [
+ { id: 1, name: "Old Office" },
+ { id: 2, name: "New Office" },
+ ]
},
- },
- 'lunch.product.category': {
- fields: {},
- records: [],
- },
- 'lunch.supplier': {
- fields: {},
- records: [],
- },
- 'lunch.location': {
- fields: {
- name: {string: 'Name', type: 'char'},
- company_id: {string: 'Company', type: 'many2one', relation: 'res.company'},
+ 'lunch.order': {
+ fields: {
+ product_id: { string: 'Product', type: 'many2one', relation: 'lunch.product', },
+ }
},
- records: [
- {id: 1, name: "Office 1", company_id: false},
- {id: 2, name: "Office 2", company_id: false},
- ],
+ 'res.users': {
+ fields: {
+ share: { type: 'boolean', },
+ },
+ records: [
+ { id: 1, name: 'Johnny Hache', share: false, },
+ { id: 2, name: 'David Elora', share: false, }
+ ]
+ }
},
- 'res.users': {
- fields: {
- name: {string: 'Name', type: 'char'},
- groups_id: {string: 'Groups', type: 'many2many'},
- },
- records: [
- {id: 1, name: "Mitchell Admin", groups_id: []},
- {id: 2, name: "Marc Demo", groups_id: []},
- {id: 3, name: "Jean-Luc Portal", groups_id: [PORTAL_GROUP_ID]},
- ],
- },
- 'res.company': {
- fields: {
- name: {string: 'Name', type: 'char'},
- }, records: [
- {id: 1, name: "Dunder Trade Company"},
- ]
+ views: {
+ 'lunch.order,false,form': `
`
}
};
- this.regularInfos = {
- username: "Marc Demo",
- wallet: 36.5,
+ lunchInfos = {
+ username: "Johnny Hache",
+ wallet: 12.05,
is_manager: false,
- group_portal_id: PORTAL_GROUP_ID,
currency: {
- symbol: "\u20ac",
- position: "after"
+ symbol: "€",
+ position: "after",
},
- user_location: [2, "Office 2"],
+ user_location: [1, "Old Office"],
+ alerts: [],
+ lines: [],
};
- this.managerInfos = {
- username: "Mitchell Admin",
- wallet: 47.6,
- is_manager: true,
- group_portal_id: PORTAL_GROUP_ID,
- currency: {
- symbol: "\u20ac",
- position: "after"
- },
- user_location: [2, "Office 2"],
- };
- },
-}, function () {
- QUnit.test('basic rendering', async function (assert) {
- assert.expect(7);
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: this.regularInfos,
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- assert.containsOnce(kanban, '.o_legacy_kanban_view .o_kanban_record:not(.o_kanban_ghost)',
- "should have 1 records in the renderer");
-
- // check view layout
- assert.containsN(kanban, '.o_content > div', 2,
- "should have 2 columns");
- assert.containsOnce(kanban, '.o_content > div.o_search_panel',
- "should have a 'lunch filters' column");
- assert.containsOnce(kanban, '.o_content > .o_lunch_content',
- "should have a 'lunch wrapper' column");
- assert.containsOnce(kanban, '.o_lunch_content > .o_legacy_kanban_view',
- "should have a 'classical kanban view' column");
- assert.hasClass(kanban.$('.o_legacy_kanban_view'), 'o_lunch_kanban_view',
- "should have classname 'o_lunch_kanban_view'");
- assert.containsOnce(kanban, '.o_lunch_content > span > .o_lunch_banner',
- "should have a 'lunch' banner");
-
- kanban.destroy();
+ setupViewRegistries();
});
- QUnit.test('no flickering at reload', async function (assert) {
+ QUnit.test("Basic rendering", async function (assert) {
+ assert.expect(4);
+
+ await makeLunchView();
+
+ assert.containsOnce(target, '.o_lunch_banner');
+ assert.containsNone(target, '.o_lunch_content .alert');
+ assert.containsOnce(target, '.o_kanban_record:not(.o_kanban_ghost)', 1);
+
+ const lunchDashboard = target.querySelector('.o_lunch_banner');
+ const lunchUser = lunchDashboard.querySelector('.lunch_user span');
+ assert.equal(lunchUser.innerText, 'Johnny Hache');
+ });
+
+ QUnit.test("Open product", async function (assert) {
assert.expect(2);
- const self = this;
- let infosProm = Promise.resolve();
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route, args) {
- if (route === '/lunch/user_location_get') {
- return Promise.resolve(self.data['lunch.location'].records[0].id);
- }
- if (route === '/lunch/infos') {
- return Promise.resolve(self.regularInfos);
- }
- var result = this._super.apply(this, arguments);
- if (args.method === 'xmlid_to_res_id') {
- // delay the rendering of the lunch widget
- return infosProm.then(_.constant(result));
- }
- return result;
- },
+ await makeLunchView();
+
+ patchWithCleanup(LunchKanbanRenderer.prototype, {
+ openOrderLine(productId, orderId) {
+ assert.equal(productId, 1);
+ }
});
- infosProm = testUtils.makeTestPromise();
- kanban.reload();
+ assert.containsOnce(target, '.o_kanban_record:not(.o_kanban_ghost)');
- assert.strictEqual(kanban.$('.o_lunch_widget').length, 1,
- "old widget should still be present");
-
- await infosProm.resolve();
-
- assert.strictEqual(kanban.$('.o_lunch_widget').length, 1);
-
- kanban.destroy();
+ click(target, '.o_kanban_record:not(.o_kanban_ghost)');
});
- QUnit.module('LunchWidget', function () {
+ QUnit.test("Basic rendering with alerts", async function (assert) {
+ assert.expect(2);
- QUnit.test('empty cart', async function (assert) {
- assert.expect(3);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: this.regularInfos,
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsN($kanbanWidget, '> .o_lunch_widget_info', 3,
- "should have 3 columns");
- assert.isVisible($kanbanWidget.find('> .o_lunch_widget_info:first'),
- "should have the first column visible");
- assert.strictEqual($kanbanWidget.find('> .o_lunch_widget_info:not(:first)').html().trim(), "",
- "all columns but the first should be empty");
-
- kanban.destroy();
- });
-
- QUnit.test('search panel domain location', async function (assert) {
- assert.expect(18);
- let expectedLocation = 1;
- let locationId = this.data['lunch.location'].records[0].id;
- const regularInfos = _.extend({}, this.regularInfos);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route, args) {
- assert.step(route);
-
- if (route.startsWith('/lunch')) {
- if (route === '/lunch/user_location_set') {
- locationId = args.location_id;
- return Promise.resolve(true);
- }
- return mockLunchRPC({
- infos: regularInfos,
- userLocation: locationId,
- }).apply(this, arguments);
- }
- if (args.method === 'search_panel_select_multi_range') {
- assert.deepEqual(args.kwargs.search_domain, [["is_available_at", "in", [expectedLocation]]],
- 'The initial domain of the search panel must contain the user location');
- }
- if (route === '/web/dataset/search_read') {
- assert.deepEqual(args.domain, [["is_available_at", "in", [expectedLocation]]],
- 'The domain for fetching actual data should be correct');
- }
- return this._super.apply(this, arguments);
- },
- });
-
- expectedLocation = 2;
- await testUtils.fields.many2one.clickOpenDropdown('locations');
- await testUtils.fields.many2one.clickItem('locations', "Office 2");
-
- assert.verifySteps([
- // Initial state
- '/lunch/user_location_get',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/search_read',
- '/lunch/infos',
- // Click m2o
- '/web/dataset/call_kw/lunch.location/name_search',
- // Click new location
- '/lunch/user_location_set',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/search_read',
- '/lunch/infos',
- ]);
-
- kanban.destroy();
- });
-
- QUnit.test('search panel domain location false: fetch products in all locations', async function (assert) {
- assert.expect(9);
- const regularInfos = _.extend({}, this.regularInfos);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route, args) {
- assert.step(route);
-
- if (route.startsWith('/lunch')) {
- return mockLunchRPC({
- infos: regularInfos,
- userLocation: false,
- }).apply(this, arguments);
- }
- if (args.method === 'search_panel_select_multi_range') {
- assert.deepEqual(args.kwargs.search_domain, [],
- 'The domain should not exist since the location is false.');
- }
- if (route === '/web/dataset/search_read') {
- assert.deepEqual(args.domain, [],
- 'The domain for fetching actual data should be correct');
- }
- return this._super.apply(this, arguments);
+ let userInfos = {
+ ...lunchInfos,
+ alerts: [
+ {
+ id: 1,
+ message: '
free boudin compote for everyone',
}
- });
- assert.verifySteps([
- '/lunch/user_location_get',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/call_kw/product/search_panel_select_multi_range',
- '/web/dataset/search_read',
- '/lunch/infos',
- ])
-
- kanban.destroy();
+ ]
+ };
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(userInfos);
+ }
+ }
});
- QUnit.test('non-empty cart', async function (assert) {
- assert.expect(17);
+ assert.containsOnce(target, '.o_lunch_content .alert');
+ assert.equal(target.querySelector('.o_lunch_content .alert').innerText, 'free boudin compote for everyone');
+ });
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: Object.assign({}, this.regularInfos, {
- total: "3.00",
+ QUnit.test("Open product", async function (assert) {
+ assert.expect(2);
+
+ await makeLunchView();
+
+ patchWithCleanup(LunchKanbanRenderer.prototype, {
+ openOrderLine(productId, orderId) {
+ assert.equal(productId, 1);
+ }
+ });
+
+ assert.containsOnce(target, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ click(target, '.o_kanban_record:not(.o_kanban_ghost)');
+ });
+
+ QUnit.test("Location change", async function (assert) {
+ assert.expect(3);
+
+ let userInfos = { ...lunchInfos };
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(userInfos);
+ } else if (route == '/lunch/user_location_set') {
+ assert.equal(args.location_id, 2);
+ userInfos.user_location = [2, "New Office"];
+ return Promise.resolve(true);
+ }
+ }
+ });
+
+ click(target, '.lunch_location input');
+
+ await nextTick();
+ assert.containsOnce(target, '.lunch_location .dropdown-item:contains(New Office)');
+
+ click(target, '.lunch_location .dropdown-item:not(.ui-state-active)');
+
+ await nextTick();
+ assert.containsN(target, 'div[role=article].o_kanban_record', 2);
+ });
+
+ QUnit.test("Manager: user change", async function (assert) {
+ assert.expect(8);
+
+ let userInfos = { ...lunchInfos, is_manager: true };
+ let expectedUserId = false; // false as we are requesting for the current user
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ assert.equal(expectedUserId, args.user_id);
+
+ if (expectedUserId === 2) {
+ userInfos = {
+ ...userInfos,
+ username: 'David Elora',
+ wallet: -10000,
+ };
+ }
+
+ return Promise.resolve(userInfos);
+ } else if (route == '/lunch/user_location_set') {
+ assert.equal(args.location_id, 2);
+ userInfos.user_location = [2, "New Office"];
+ return Promise.resolve(true);
+ }
+ }
+ });
+
+ assert.containsOnce(target, '.lunch_user input');
+ click(target, '.lunch_user input');
+
+ await nextTick();
+ assert.containsOnce(target, '.lunch_user .dropdown-item:contains(David Elora)');
+
+ expectedUserId = 2;
+ click(target, '.lunch_user .dropdown-item:not(.ui-state-active)');
+
+ await nextTick();
+ const wallet = target.querySelector('.o_lunch_banner .col-9 > .d-flex > span:nth-child(2)');
+ assert.equal(wallet.innerText, '-10000.00€', 'David Elora is poor')
+
+ click(target, '.lunch_location input');
+ await nextTick();
+ click(target, '.lunch_location .dropdown-item:not(.ui-state-active)');
+
+ await nextTick();
+ const user = target.querySelector('.lunch_user input');
+ assert.equal(user.value, 'David Elora', 'changing location should not reset user');
+ });
+
+ QUnit.test("Trash existing order", async function (assert) {
+ assert.expect(5);
+
+ let userInfos = {
+ ...lunchInfos,
+ lines: [
+ {
+ id: 1,
+ product: [1, "Big Plate", "4.95"],
+ toppings: [],
+ quantity: 1,
+ price: 4.95,
+ raw_state: "new",
+ state: "To Order",
+ note: false
+ }
+ ],
+ raw_state: "new",
+ total: "4.95",
+ };
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(userInfos);
+ } else if (route == '/lunch/trash') {
+ userInfos = {
+ ...userInfos,
+ lines: [],
+ raw_state: false,
+ total: 0,
+ };
+ return Promise.resolve(true);
+ }
+ }
+ });
+
+ assert.containsN(target, 'div.o_lunch_banner > .row > div', 3);
+ assert.containsOnce(target, 'div.o_lunch_banner > .row > div:nth-child(2) button.fa-trash', 'should have trash icon');
+ assert.containsOnce(target, 'div.o_lunch_banner > .row > div:nth-child(2) ul > li', 'should have one order line');
+
+ assert.containsOnce(target, 'div.o_lunch_banner > .row > div:nth-child(3) button:contains(Order Now)');
+
+ click(target, 'div.o_lunch_banner > .row > div:nth-child(2) button.fa-trash');
+ await nextTick();
+ assert.containsN(target, 'div.o_lunch_banner > .row > div', 1);
+ });
+
+ QUnit.test("Change existing order", async function (assert) {
+ assert.expect(1);
+
+ let userInfos = {
+ ...lunchInfos,
+ lines: [
+ {
+ id: 1,
+ product: [1, "Big Plate", "4.95"],
+ toppings: [],
+ quantity: 1,
+ price: 4.95,
+ raw_state: "new",
+ state: "To Order",
+ note: false
+ }
+ ],
+ raw_state: "new",
+ total: "4.95",
+ };
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(userInfos);
+ } else if (route == '/web/dataset/call_kw/lunch.order/update_quantity') {
+ assert.equal(args.args[1], 1, 'should increment order quantity by 1');
+ userInfos = {
+ ...userInfos,
lines: [
{
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- quantity: 1.0,
- },
+ ...userInfos.lines[0],
+ product: [1, "Big Plate", "9.9"],
+ quantity: 2,
+ price: 4.95 * 2,
+ }
],
- }),
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
+ total: 4.95 * 2,
+ };
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsN($kanbanWidget, '> .o_lunch_widget_info', 3,
- "should have 3 columns");
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:eq(1)',
- "should have a second column");
-
- const $widgetSecondColumn = $kanbanWidget.find('.o_lunch_widget_info:eq(1)');
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_unlink',
- "should have a button to clear the order");
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_lines > li',
- "should have 1 order line");
-
- const $firstLine = $widgetSecondColumn.find('.o_lunch_widget_lines > li:first');
- assert.containsOnce($firstLine, 'button.o_remove_product',
- "should have a button to remove a product quantity on each line");
- assert.containsOnce($firstLine, 'button.o_add_product',
- "should have a button to add a product quantity on each line");
- assert.containsOnce($firstLine, '.o_lunch_product_quantity > :eq(1)',
- "should have the line's quantity");
- assert.strictEqual($firstLine.find('.o_lunch_product_quantity > :eq(1)').text().trim(), "1",
- "should have 1 as the line's quantity");
- assert.containsOnce($firstLine, '.o_lunch_open_wizard',
- "should have the line's product name to open the wizard");
- assert.strictEqual($firstLine.find('.o_lunch_open_wizard').text().trim(), "Tuna sandwich",
- "should have 'Tuna sandwich' as the line's product name");
- assert.containsOnce($firstLine, '.o_field_monetary',
- "should have the line's amount");
- assert.strictEqual($firstLine.find('.o_field_monetary').text().trim(), "3.00€",
- "should have '3.00€' as the line's amount");
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:eq(2)',
- "should have a third column");
-
- const $widgetThirdColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(2)');
-
- assert.containsOnce($widgetThirdColumn, '.o_field_monetary',
- "should have an account balance");
- assert.strictEqual($widgetThirdColumn.find('.o_field_monetary').text().trim(), "3.00€",
- "should have '3.00€' in the account balance");
- assert.containsOnce($widgetThirdColumn, '.o_lunch_widget_order_button',
- "should have a button to validate the order");
- assert.strictEqual($widgetThirdColumn.find('.o_lunch_widget_order_button').text().trim(), "Order now",
- "should have 'Order now' as the validate order button text");
-
- kanban.destroy();
+ return Promise.resolve(true);
+ }
+ }
});
- QUnit.test('ordered cart', async function (assert) {
- assert.expect(13);
+ click(target, 'div.o_lunch_banner > .row > div:nth-child(2) button.fa-plus-circle');
+ });
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: Object.assign({}, this.regularInfos, {
- raw_state: "ordered",
- state: "Ordered",
+ QUnit.test("Confirm existing order", async function (assert) {
+ assert.expect(3);
+
+ let userInfos = {
+ ...lunchInfos,
+ lines: [
+ {
+ id: 1,
+ product: [1, "Big Plate", "4.95"],
+ toppings: [],
+ quantity: 1,
+ price: 4.95,
+ raw_state: "new",
+ state: "To Order",
+ note: false
+ }
+ ],
+ raw_state: "new",
+ total: "4.95",
+ };
+ await makeLunchView({
+ mockRPC: (route, args) => {
+ if (route == '/lunch/user_location_get') {
+ return Promise.resolve(userInfos.user_location[0]);
+ } else if (route == '/lunch/infos') {
+ return Promise.resolve(userInfos);
+ } else if (route == '/lunch/pay') {
+ assert.equal(args.user_id, false); // Should confirm order of current user
+ userInfos = {
+ ...userInfos,
lines: [
{
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- quantity: 1.0,
+ ...userInfos.lines[0],
raw_state: 'ordered',
- },
+ state: 'Ordered,'
+ }
],
- }),
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsN($kanbanWidget, '> .o_lunch_widget_info', 3,
- "should have 3 columns");
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:eq(1)',
- "should have a second column");
-
- const $widgetSecondColumn = $kanbanWidget.find('.o_lunch_widget_info:eq(1)');
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_unlink',
- "should have a button to clear the order");
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_lines > li',
- "should have 1 order line");
-
- const $firstLine = $widgetSecondColumn.find('.o_lunch_widget_lines > li:first');
- assert.containsOnce($firstLine, 'button.o_remove_product',
- "should have a button to remove a product quantity on each line");
- assert.containsOnce($firstLine, 'button.o_add_product',
- "should have a button to add a product quantity on each line");
- assert.containsOnce($firstLine, '.o_lunch_product_quantity > :eq(1)',
- "should have the line's quantity");
- assert.strictEqual($firstLine.find('.o_lunch_product_quantity > :eq(1)').text().trim(), "1",
- "should have 1 as the line's quantity");
- assert.containsOnce($firstLine, '.o_lunch_open_wizard',
- "should have the line's product name to open the wizard");
- assert.strictEqual($firstLine.find('.o_lunch_open_wizard').text().trim(), "Tuna sandwich",
- "should have 'Tuna sandwich' as the line's product name");
- assert.containsOnce($firstLine, '.o_field_monetary',
- "should have the line's amount");
- assert.strictEqual($firstLine.find('.o_field_monetary').text().trim(), "3.00€",
- "should have '3.00€' as the line's amount");
-
- assert.strictEqual($kanbanWidget.find('> .o_lunch_widget_info:eq(2)').html().trim(), "",
- "third column should be empty");
-
- kanban.destroy();
+ raw_state: 'ordered',
+ wallet: userInfos.wallet - 4.95,
+ };
+ return Promise.resolve(true);
+ }
+ }
});
- QUnit.test('confirmed cart', async function (assert) {
- assert.expect(12);
+ const wallet = target.querySelector('.o_lunch_banner .col-9 > .d-flex > span:nth-child(2)');
+ assert.equal(wallet.innerText, '12.05€');
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: Object.assign({}, this.regularInfos, {
- raw_state: "confirmed",
- state: "Received",
- lines: [
- {
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- quantity: 1.0,
- raw_state: 'confirmed',
- state: 'Confirmed',
- },
- ],
- }),
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
+ click(target, 'div.o_lunch_banner > .row > div:nth-child(3) button');
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsN($kanbanWidget, '> .o_lunch_widget_info', 3,
- "should have 3 columns");
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:eq(1)',
- "should have a second column");
-
- const $widgetSecondColumn = $kanbanWidget.find('.o_lunch_widget_info:eq(1)');
-
- assert.containsNone($widgetSecondColumn, '.o_lunch_widget_unlink',
- "shouldn't have a button to clear the order");
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_lines > li',
- "should have 1 order line");
-
- const $firstLine = $widgetSecondColumn.find('.o_lunch_widget_lines > li:first');
- assert.containsNone($firstLine, 'button.o_remove_product',
- "shouldn't have a button to remove a product quantity on each line");
- assert.containsNone($firstLine, 'button.o_add_product',
- "shouldn't have a button to add a product quantity on each line");
- assert.containsOnce($firstLine, '.o_lunch_product_quantity',
- "should have the line's quantity");
- assert.strictEqual($firstLine.find('.o_lunch_product_quantity').text().trim(), "1",
- "should have 1 as the line's quantity");
- assert.containsNone($firstLine, '.o_lunch_open_wizard',
- "shouldn't have the line's product name to open the wizard");
- assert.containsOnce($firstLine, '.o_field_monetary',
- "should have the line's amount");
- assert.strictEqual($firstLine.find('.o_field_monetary').text().trim(), "3.00€",
- "should have '3.00€' as the line's amount");
-
- assert.strictEqual($kanbanWidget.find('> .o_lunch_widget_info:eq(2)').html().trim(), "",
- "third column should be empty");
-
- kanban.destroy();
- });
-
- QUnit.test('regular user', async function (assert) {
- assert.expect(11);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: this.regularInfos,
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:first',
- "should have a first column");
-
- const $widgetFirstColumn = $kanbanWidget.find('.o_lunch_widget_info:first');
-
- assert.containsOnce($widgetFirstColumn, 'img.rounded-circle',
- "should have a rounded avatar image");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_user_field',
- "should have a user field");
- assert.containsNone($widgetFirstColumn, '.o_lunch_user_field > .o_field_widget',
- "shouldn't have a field widget in the user field");
- assert.strictEqual($widgetFirstColumn.find('.o_lunch_user_field').text().trim(), "Marc Demo",
- "should have 'Marc Demo' in the user field");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field',
- "should have a location field");
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field > .o_field_many2one[name="locations"]',
- "should have a many2one in the location field");
-
- await testUtils.fields.many2one.clickOpenDropdown('locations');
- const $input = $widgetFirstColumn.find('.o_field_many2one[name="locations"] input');
- assert.containsN($input.autocomplete('widget'), 'li', 2,
- "autocomplete dropdown should have 2 entries");
- assert.strictEqual($input.val(), "Office 2",
- "locations input should have 'Office 2' as value");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field + div',
- "should have an account balance");
- assert.strictEqual($widgetFirstColumn.find('.o_lunch_location_field + div .o_field_monetary').text().trim(), "36.50€",
- "should have '36.50€' in the account balance");
-
- kanban.destroy();
- });
-
- QUnit.test('manager user', async function (assert) {
- assert.expect(12);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: this.managerInfos,
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $kanbanWidget = kanban.$('.o_lunch_widget');
-
- assert.containsOnce($kanbanWidget, '.o_lunch_widget_info:first',
- "should have a first column");
-
- const $widgetFirstColumn = $kanbanWidget.find('.o_lunch_widget_info:first');
-
- assert.containsOnce($widgetFirstColumn, 'img.rounded-circle',
- "should have a rounded avatar image");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_user_field',
- "should have a user field");
- assert.containsOnce($widgetFirstColumn, '.o_lunch_user_field > .o_field_many2one[name="users"]',
- "shouldn't have a field widget in the user field");
-
- await testUtils.fields.many2one.clickOpenDropdown('users');
- const $userInput = $widgetFirstColumn.find('.o_field_many2one[name="users"] input');
- assert.containsN($userInput.autocomplete('widget'), 'li', 2,
- "users autocomplete dropdown should have 2 entries");
- assert.strictEqual($userInput.val(), "Mitchell Admin",
- "should have 'Mitchell Admin' as value in user field");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field',
- "should have a location field");
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field > .o_field_many2one[name="locations"]',
- "should have a many2one in the location field");
-
- await testUtils.fields.many2one.clickOpenDropdown('locations');
- const $locationInput = $widgetFirstColumn.find('.o_field_many2one[name="locations"] input');
- assert.containsN($locationInput.autocomplete('widget'), 'li', 2,
- "locations autocomplete dropdown should have 2 entries");
- assert.strictEqual($locationInput.val(), "Office 2",
- "should have 'Office 2' as value");
-
- assert.containsOnce($widgetFirstColumn, '.o_lunch_location_field + div',
- "should have an account balance");
- assert.strictEqual($widgetFirstColumn.find('.o_lunch_location_field + div .o_field_monetary').text().trim(), "47.60€",
- "should have '47.60€' in the account balance");
-
- kanban.destroy();
- });
-
- QUnit.test('add a product', async function (assert) {
- assert.expect(1);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: this.regularInfos,
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- intercepts: {
- do_action: function (ev) {
- assert.deepEqual(ev.data.action, {
- name: "Configure Your Order",
- res_model: 'lunch.order',
- type: 'ir.actions.act_window',
- views: [[false, 'form']],
- target: 'new',
- context: {
- default_product_id: 1,
- },
- },
- "should open the wizard");
- },
- },
- });
-
- await testUtils.dom.click(kanban.$('.o_kanban_record:first'));
-
- kanban.destroy();
- });
-
- QUnit.test('add product quantity', async function (assert) {
- assert.expect(3);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: Object.assign({}, this.data, {
- 'lunch.order': {
- fields: {},
- update_quantity([lineIds, increment]) {
- assert.deepEqual(lineIds, [6], "should have [6] as lineId to update quantity");
- assert.strictEqual(increment, 1, "should have +1 as increment to update quantity");
- return Promise.resolve();
- },
- },
- }),
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: Object.assign({}, this.regularInfos, {
- lines: [
- {
- id: 6,
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- quantity: 1.0,
- },
- ],
- }),
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $widgetSecondColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(1)');
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_lines > li',
- "should have 1 order line");
-
- const $firstLine = $widgetSecondColumn.find('.o_lunch_widget_lines > li:first');
-
- await testUtils.dom.click($firstLine.find('button.o_add_product'));
-
- kanban.destroy();
- });
-
- QUnit.test('remove product quantity', async function (assert) {
- assert.expect(3);
-
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: Object.assign({}, this.data, {
- 'lunch.order': {
- fields: {},
- update_quantity([lineIds, increment]) {
- assert.deepEqual(lineIds, [6], "should have [6] as lineId to update quantity");
- assert.strictEqual(increment, -1, "should have -1 as increment to update quantity");
- return Promise.resolve();
- },
- },
- }),
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: mockLunchRPC({
- infos: Object.assign({}, this.regularInfos, {
- lines: [
- {
- id: 6,
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- quantity: 1.0,
- },
- ],
- }),
- userLocation: this.data['lunch.location'].records[0].id,
- }),
- });
-
- const $widgetSecondColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(1)');
-
- assert.containsOnce($widgetSecondColumn, '.o_lunch_widget_lines > li',
- "should have 1 order line");
-
- const $firstLine = $widgetSecondColumn.find('.o_lunch_widget_lines > li:first');
-
- await testUtils.dom.click($firstLine.find('button.o_remove_product'));
-
- kanban.destroy();
- });
-
- QUnit.test('clear order', async function (assert) {
- assert.expect(1);
-
- const self = this;
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route) {
- if (route.startsWith('/lunch')) {
- if (route === '/lunch/trash') {
- assert.ok('should perform clear order RPC call');
- return Promise.resolve();
- }
- return mockLunchRPC({
- infos: Object.assign({}, self.regularInfos, {
- lines: [
- {
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- },
- ],
- }),
- userLocation: self.data['lunch.location'].records[0].id,
- }).apply(this, arguments);
- }
- return this._super.apply(this, arguments);
- },
- });
-
- const $widgetSecondColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(1)');
-
- await testUtils.dom.click($widgetSecondColumn.find('button.o_lunch_widget_unlink'));
-
- kanban.destroy();
- });
-
- QUnit.test('validate order: success', async function (assert) {
- assert.expect(1);
-
- const self = this;
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route) {
- if (route.startsWith('/lunch')) {
- if (route === '/lunch/pay') {
- assert.ok("should perform pay order RPC call");
- return Promise.resolve(true);
- }
- return mockLunchRPC({
- infos: Object.assign({}, self.regularInfos, {
- lines: [
- {
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- },
- ],
- }),
- userLocation: self.data['lunch.location'].records[0].id,
- }).apply(this, arguments);
- }
- return this._super.apply(this, arguments);
- },
- });
-
- const $widgetThirdColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(2)');
-
- await testUtils.dom.click($widgetThirdColumn.find('button.o_lunch_widget_order_button'));
-
- kanban.destroy();
- });
-
- QUnit.test('validate order: failure', async function (assert) {
- assert.expect(5);
-
- const self = this;
- const kanban = await createLunchView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: `
-
-
-
-
-
-
-
- `,
- mockRPC: function (route) {
- if (route.startsWith('/lunch')) {
- if (route === '/lunch/pay') {
- assert.ok('should perform pay order RPC call');
- return Promise.resolve(false);
- }
- if (route === '/lunch/payment_message') {
- assert.ok('should perform payment message RPC call');
- return Promise.resolve({ message: 'This is a payment message.'});
- }
- return mockLunchRPC({
- infos: Object.assign({}, self.regularInfos, {
- lines: [
- {
- product: [1, "Tuna sandwich", "3.00"],
- toppings: [],
- },
- ],
- }),
- userLocation: self.data['lunch.location'].records[0].id,
- }).apply(this, arguments);
- }
- return this._super.apply(this, arguments);
- },
- });
-
- const $widgetThirdColumn = kanban.$('.o_lunch_widget .o_lunch_widget_info:eq(2)');
-
- await testUtils.dom.click($widgetThirdColumn.find('button.o_lunch_widget_order_button'));
-
- assert.containsOnce(document.body, '.modal', "should open a Dialog box");
- assert.strictEqual($('.modal-title').text().trim(),
- "Not enough money in your wallet", "should have a Dialog's title");
- assert.strictEqual($('.modal-body').text().trim(),
- "This is a payment message.", "should have a Dialog's message");
-
- kanban.destroy();
- });
+ await nextTick();
+ assert.equal(wallet.innerText, '7.10€', 'Wallet should update');
});
});
-
});
diff --git a/addons/lunch/static/tests/lunch_test_utils.js b/addons/lunch/static/tests/lunch_test_utils.js
deleted file mode 100644
index baa7f18904c..00000000000
--- a/addons/lunch/static/tests/lunch_test_utils.js
+++ /dev/null
@@ -1,59 +0,0 @@
-odoo.define('lunch.test_utils', function (require) {
-"use strict";
-
-const AbstractStorageService = require('web.AbstractStorageService');
-const RamStorage = require('web.RamStorage');
-const {createView} = require('web.test_utils');
-
-/**
- * Helper to create a lunch view with searchpanel
- *
- * @param {object} params
- */
-async function createLunchView(params) {
- params.archs = params.archs || {};
- var searchArch = params.archs[`${params.model},false,search`] || '
';
- var searchPanelArch = `
-
-
-
-
- `;
- searchArch = searchArch.split('')[0] + searchPanelArch + '';
- params.archs[`${params.model},false,search`] = searchArch;
- if (!params.services || !params.services.local_storage) {
- // the searchPanel uses the localStorage to store/retrieve default
- // active category value
- params.services = params.services || {};
- const RamStorageService = AbstractStorageService.extend({
- storage: new RamStorage(),
- });
- params.services.local_storage = RamStorageService;
- }
- return createView(params);
-}
-
-/**
- * Helper to generate a mockRPC function for the mandatory lunch routes (prefixed by '/lunch')
- *
- * @param {object} infos
- * @param {integer} userLocation
- */
-function mockLunchRPC({infos, userLocation}) {
- return async function (route) {
- if (route === '/lunch/infos') {
- return Promise.resolve(infos);
- }
- if (route === '/lunch/user_location_get') {
- return Promise.resolve(userLocation);
- }
- return this._super.apply(this, arguments);
- };
-}
-
-return {
- createLunchView,
- mockLunchRPC,
-};
-
-});
diff --git a/addons/lunch/views/lunch_orders_views.xml b/addons/lunch/views/lunch_orders_views.xml
index 2241bf80433..a6b05af3ebd 100644
--- a/addons/lunch/views/lunch_orders_views.xml
+++ b/addons/lunch/views/lunch_orders_views.xml
@@ -200,7 +200,7 @@
lunch.order.view.form
lunch.order
-