From fa20b34d7454c478f6299003fd1f78c3241c7d97 Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Thu, 25 Aug 2022 13:26:13 +0000 Subject: [PATCH] [IMP] pos_loyalty: loyalty, gift card and ewallet programs * Allow multiple loyalty programs. * Improve gift card workflow: https://app.excalidraw.com/l/65VNwvy7c4X/1Xt93llULvo * Integration with the ewallet programs: https://app.excalidraw.com/l/65VNwvy7c4X/4B9AEOSbaku Note that we prevent refunding eWallet and gift card top up and reward products. If for instance the user wants to refund gift cards they'll have to transfer the gift card to ewallet by topping up the customer's ewallet and paying with the gift card to be refunded. closes odoo/odoo#100891 Task-ids: 2959620, 2959661 X-original-commit: fa06e3225650ed9d1a96709b0398e7f33313acf5 Signed-off-by: Trinh Jacky (trj) Signed-off-by: Joseph Caburnay (jcb) --- addons/loyalty/models/loyalty_card.py | 3 + addons/point_of_sale/static/src/js/Chrome.js | 3 +- .../static/src/js/Popups/TextInputPopup.js | 1 + .../PartnerListScreen/PartnerListScreen.js | 4 +- .../js/Screens/TicketScreen/TicketScreen.js | 15 +- addons/point_of_sale/static/src/js/models.js | 12 +- .../static/src/xml/Popups/TextInputPopup.xml | 4 +- .../tours/helpers/ProductScreenTourMethods.js | 13 +- .../helpers/TextInputPopupTourMethods.js | 2 +- addons/pos_loyalty/__manifest__.py | 14 +- addons/pos_loyalty/data/gift_card_data.xml | 4 + addons/pos_loyalty/models/__init__.py | 1 + addons/pos_loyalty/models/loyalty_card.py | 3 + addons/pos_loyalty/models/loyalty_program.py | 2 +- addons/pos_loyalty/models/loyalty_reward.py | 13 ++ addons/pos_loyalty/models/pos_config.py | 1 + addons/pos_loyalty/models/pos_order.py | 26 ++- addons/pos_loyalty/models/pos_order_line.py | 6 + addons/pos_loyalty/models/pos_session.py | 109 ++++------ .../src/js/ControlButtons/GiftCardButton.js | 28 --- .../src/js/ControlButtons/PromoCodeButton.js | 14 +- .../js/ControlButtons/ResetProgramsButton.js | 2 +- .../src/js/ControlButtons/RewardButton.js | 10 +- .../src/js/ControlButtons/eWalletButton.js | 98 +++++++++ addons/pos_loyalty/static/src/js/Loyalty.js | 198 ++++++++++-------- .../pos_loyalty/static/src/js/OrderSummary.js | 3 - .../pos_loyalty/static/src/js/OrderWidget.js | 2 +- addons/pos_loyalty/static/src/js/Orderline.js | 15 ++ .../pos_loyalty/static/src/js/PartnerLine.js | 22 ++ .../static/src/js/PartnerListScreen.js | 21 ++ .../static/src/js/PaymentScreen.js | 14 +- .../static/src/js/Popups/GiftCardPopup.js | 165 --------------- .../static/src/js/ProductScreen.js | 125 +++++++++++ .../pos_loyalty/static/src/js/TicketScreen.js | 50 +++++ .../static/src/tours/EWalletProgramTours.js | 105 ++++++++++ .../static/src/tours/GiftCardProgramTours.js | 51 +++++ .../tours/MultipleGiftWalletProgramsTour.js | 70 +++++++ .../tours/PosLoyaltyLoyaltyProgramTour.js | 0 .../tours/PosLoyaltyRewardButtonTour.js | 0 .../src/{js => }/tours/PosLoyaltyTour.js | 0 .../{js => }/tours/PosLoyaltyTourMethods.js | 13 ++ .../{js => }/tours/PosLoyaltyValidityTour.js | 0 .../src/xml/ControlButtons/GiftCardButton.xml | 12 -- .../src/xml/ControlButtons/eWalletButton.xml | 16 ++ .../static/src/xml/OrderReceipt.xml | 44 ++-- .../static/src/xml/OrderSummary.xml | 48 +++-- .../pos_loyalty/static/src/xml/Orderline.xml | 13 ++ .../static/src/xml/PartnerLine.xml | 15 ++ .../static/src/xml/Popups/GiftCardPopup.xml | 106 ---------- addons/pos_loyalty/tests/test_frontend.py | 169 ++++++++++++++- .../views/loyalty_program_views.xml | 2 +- .../static/tests/tours/FloorScreen.tour.js | 2 +- .../pos_sale_loyalty/static/src/js/models.js | 4 +- addons/sale_loyalty/models/loyalty_card.py | 3 + 54 files changed, 1095 insertions(+), 581 deletions(-) rename addons/{pos_restaurant => point_of_sale}/static/tests/tours/helpers/TextInputPopupTourMethods.js (92%) create mode 100644 addons/pos_loyalty/models/loyalty_reward.py delete mode 100644 addons/pos_loyalty/static/src/js/ControlButtons/GiftCardButton.js create mode 100644 addons/pos_loyalty/static/src/js/ControlButtons/eWalletButton.js create mode 100644 addons/pos_loyalty/static/src/js/PartnerLine.js create mode 100644 addons/pos_loyalty/static/src/js/PartnerListScreen.js delete mode 100644 addons/pos_loyalty/static/src/js/Popups/GiftCardPopup.js create mode 100644 addons/pos_loyalty/static/src/js/TicketScreen.js create mode 100644 addons/pos_loyalty/static/src/tours/EWalletProgramTours.js create mode 100644 addons/pos_loyalty/static/src/tours/GiftCardProgramTours.js create mode 100644 addons/pos_loyalty/static/src/tours/MultipleGiftWalletProgramsTour.js rename addons/pos_loyalty/static/src/{js => }/tours/PosLoyaltyLoyaltyProgramTour.js (100%) rename addons/pos_loyalty/static/src/{js => }/tours/PosLoyaltyRewardButtonTour.js (100%) rename addons/pos_loyalty/static/src/{js => }/tours/PosLoyaltyTour.js (100%) rename addons/pos_loyalty/static/src/{js => }/tours/PosLoyaltyTourMethods.js (92%) rename addons/pos_loyalty/static/src/{js => }/tours/PosLoyaltyValidityTour.js (100%) delete mode 100644 addons/pos_loyalty/static/src/xml/ControlButtons/GiftCardButton.xml create mode 100644 addons/pos_loyalty/static/src/xml/ControlButtons/eWalletButton.xml create mode 100644 addons/pos_loyalty/static/src/xml/Orderline.xml create mode 100644 addons/pos_loyalty/static/src/xml/PartnerLine.xml delete mode 100644 addons/pos_loyalty/static/src/xml/Popups/GiftCardPopup.xml diff --git a/addons/loyalty/models/loyalty_card.py b/addons/loyalty/models/loyalty_card.py index 16c29a0f658..1c7fd86810e 100644 --- a/addons/loyalty/models/loyalty_card.py +++ b/addons/loyalty/models/loyalty_card.py @@ -69,6 +69,9 @@ class LoyaltyCard(models.Model): self.ensure_one() return None + def _has_source_order(self): + return False + def action_coupon_send(self): """ Open a window to compose an email, with the default template returned by `_get_default_template` message loaded by default diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index 36a67711cf9..ff7d3056997 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -88,7 +88,8 @@ odoo.define('point_of_sale.Chrome', function(require) { }); onError((error) => { - console.error(error); + // error is an OwlError object. + console.error(error.cause); }); this.props.setupIsDone(this); diff --git a/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js index aee6ea77394..f6c9d28f819 100644 --- a/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js @@ -29,6 +29,7 @@ odoo.define('point_of_sale.TextInputPopup', function(require) { title: '', body: '', startingValue: '', + placeholder: '', }; Registries.Component.add(TextInputPopup); diff --git a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js index 2ee190afd3c..dd16a666d81 100644 --- a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js @@ -98,7 +98,7 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { return res } get isBalanceDisplayed() { - return this.env.pos.config.module_pos_loyalty; + return false; } get partnerLink() { return `/web#model=res.partner&id=${this.state.editModeProps.partner.id}`; @@ -176,7 +176,7 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { } async searchPartner() { let result = await this.getNewPartners(); - this.env.pos.db.add_partners(result); + this.env.pos.addPartners(result); if (!this.env.pos.isEveryPartnerLoaded) await this.env.pos.updateIsEveryPartnerLoaded(); this.render(true); return result; diff --git a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js index 0f89df82efc..c1ef0c25d3d 100644 --- a/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/TicketScreen/TicketScreen.js @@ -190,15 +190,19 @@ odoo.define('point_of_sale.TicketScreen', function (require) { } async _onDoRefund() { const order = this.getSelectedSyncedOrder(); + + if (this._doesOrderHaveSoleItem(order)) { + if (!this._prepareAutoRefundOnOrder(order)) { + // Don't proceed on refund if preparation returned false. + return; + } + } + if (!order) { this._state.ui.highlightHeaderNote = !this._state.ui.highlightHeaderNote; return; } - if (this._doesOrderHaveSoleItem(order)) { - this._prepareAutoRefundOnOrder(order); - } - const partner = order.get_partner(); const allToRefundDetails = this._getRefundableDetails(partner); @@ -415,13 +419,14 @@ odoo.define('point_of_sale.TicketScreen', function (require) { _prepareAutoRefundOnOrder(order) { const selectedOrderlineId = this.getSelectedOrderlineId(); const orderline = order.orderlines.find((line) => line.id == selectedOrderlineId); - if (!orderline) return; + if (!orderline) return false; const toRefundDetail = this._getToRefundDetail(orderline); const refundableQty = orderline.get_quantity() - orderline.refunded_qty; if (this.env.pos.isProductQtyZero(refundableQty - 1)) { toRefundDetail.qty = 1; } + return true; } /** * Returns the corresponding toRefundDetail of the given orderline. diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index a1cf0709091..bc1c11f4e16 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -193,7 +193,7 @@ class PosGlobalState extends PosModel { this._loadPoSConfig(); this.bills = loadedData['pos.bill']; this.partners = loadedData['res.partner']; - this._loadResPartner(); + this.addPartners(this.partners); this.picking_type = loadedData['stock.picking.type']; this.user = loadedData['res.users']; this.pricelists = loadedData['product.pricelist']; @@ -222,8 +222,8 @@ class PosGlobalState extends PosModel { _loadPoSConfig() { this.db.set_uuid(this.config.uuid); } - _loadResPartner() { - this.db.add_partners(this.partners); + addPartners(partners) { + return this.db.add_partners(partners); } _assignApplicableItems(pricelist, correspondingProduct, pricelistItem) { if (!(pricelist.id in correspondingProduct.applicablePricelistItems)) { @@ -343,7 +343,7 @@ class PosGlobalState extends PosModel { shadow: true, }) .then(partners => { - if (this.db.add_partners(partners)) { // check if the partners we got were real updates + if (this.addPartners(partners)) { // check if the partners we got were real updates resolve(); } else { reject('Failed in updating partners.'); @@ -492,7 +492,7 @@ class PosGlobalState extends PosModel { timeout: 3000, shadow: true, }); - this.env.pos.db.add_partners(fetchedPartners); + this.addPartners(fetchedPartners); } } async _loadMissingPartners(orders) { @@ -540,7 +540,7 @@ class PosGlobalState extends PosModel { ], context: this.env.session.user_context, }, { shadow: true }); - this.db.add_partners(partners); + this.addPartners(partners); i += 1; } while(partners.length); } diff --git a/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml b/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml index de38d55d5ba..821957e15dc 100644 --- a/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/TextInputPopup.xml @@ -6,11 +6,11 @@
-
+

- +