Wednesday, August 26, 2026
6 changes · master
Code cleanup and technical improvements
This change restructures how loyalty rewards, gift cards, and eWallets are handled in Point of Sale. Cashiers now claim free product rewards manually, reward lines are clearer at zero price, and the system rechecks loyalty calculations at order validation to improve consistency and prevent misuse.
Original PR description
Enterprise PR-[#127058](https://github.com/odoo/enterprise/pull/127058) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948) --- Moved the computation of the different loyalty rewards from…
Enterprise PR-[#127058](https://github.com/odoo/enterprise/pull/127058) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948) --- Moved the computation of the different loyalty rewards from the pos_order.js model to their specific model files. Now the whole flow is started by `pos_order.recomputeRewards` it will delete all existing rewards on the order, then apply the discounts and auto rewards. The order keeps track of - `_active_rewards` - list of rewards manually claimed by the user, with optional qty and reward product - `_disabled_program_ids` - list of loyalty programs which were manually removed from the order by the user, so they are not automatically recomputed on the next pass - `_active_payment_programs` - the payment programs which are used in the order `pos_order.recomputeReward` will compute all the points that a reward can consume and then call `applyReward` which then decides which orderlines need to be added The frontend will keep the order consistent with the existing loyalty programs in the way they were loaded by the browser, to ensure that the displayed data is consistent while being able to still process loyalty orders while offline. Although the frontend does its own processing of loyalty, the backend will redo the same computations on order validation on `pos_order._process_saved_order` That will call `_process_loyalty` which will recompute based on the orderlines how many points the order is eligible for for each program, and how many points are consumed by each reward. Afterwards it will create the history lines based on its own computation, so that bad clients can't fool the system (and we can later trust to use loyalty programs on the self) The only thing which currently doesn't work offline is selling gift cards or ewallets. Before order validation, if there are any payment cards that need to be created, we do a RPC call to `loyalty_card.create_pos_cards` which will create the cards and send the data back to the client so that the new cards are properly added on the order. --- Here are the big UX changes: 1. Free product rewards used to be automatically added to the cart. In the old flow you would add a product, and if eligible for a free reward, another line with the negative price of the product would show up on the cart (which was the reward). Now free product rewards need to be manually claimed from the control buttons and they always show up as 0 priced order lines. 2. There is no more `Reset Rewards` button. Rewards are either applied on the order or not. The user can at any point remove reward lines from the order through the numpad, which will disable the reward. Disabled rewards can always be added back through the normal `Rewards` button. 3. We used to have a hard rule that rewards can't be claimed unless the client is already paying for something on the order. Now clients can use up their loyalty points and apply rewards without needing to also order something else --- Besides all of that, here is a list of all the tours which needed changes due to the new design, and why those changes were made: test_combo_product_dont_grant_point would create 2 combos of $48 each but then 100% discount would only reduce the total by $46 because the products in the combo have different sales taxes, and then expect the total to be 50. Now it applies the correct $48 so the total should be $48 PosLoyaltySpecificDiscountWithFreeProductTour expected for a free product reward to only be enabled when the product is added to the cart or when the reward button is pressed. Now it's treated as any other automatic reward and the free product is added to the order test_promotion_program_with_loyalty_program added a `trigger: auto` to the program. Otherwise the program is created with `trigger = false`, which can't really be produced in a real DB, unless through direct backend ORM calls or SQL. The new module relies on the trigger value for auto computed rewards. (The old one treated Falsy values as auto) test_ewallet_expiration_date expects the eWallet pay button to be available and then to throw an error dialog when trying to add an expired eWallet. Now the button will just be disabled if no valid eWallets can be added to the current order MultipleGiftWalletProgramsTour, RefundRulesProduct modified since the partner selection popup now launches automatically when an eWallet is added on the order with no partner selected EWalletProgramTour1 & EWalletLoyaltyHistory expected the partner popup to show up only after the pay button is pressed if there is no partner selected for the current order. Now the selection popup will show up as soon as a topup product is added on the order with no partner selected MultiplePhysicalGiftCardProgramSaleTour expects the user to select the program for the card when the product is added to the order, but then after the createManualGiftCard step, it asks the user again to chose a program. This seems like repetitive for no reason. So the new module will keep the program which was selected the first time by the user and skip the 2nd prompt PosLoyaltyFreeProductTour, PosLoyaltyFreeProductTour2, PosLoyaltyTour11.2, PosLoyaltyLoyaltyProgram3, PosLoyaltyTour12, test_loyalty_free_product_rewards_2, PosLoyaltyTour10, test_loyalty_reward_with_variant, test_min_qty_points_awarded, test_multiple_reward_line_free_product, PosOrderClaimReward, PosLoyaltyTour1, PosLoyaltyTour8, assumes the old free product behaviour of negative lines and auto applied rewards. It is changed to follow the new flow of requiring manual redeeming for all free product rewards and using 0 cost lines for all free product rewards PosLoyaltyLoyaltyProgram2: 1. It asserts that loyalty rewards cannot be used unless something is ordered first. Should we still enforce this constraint? 2. The last order total checked whether the reward was still applied by making sure the total is 6.40 (2 pens) and there is no negative line. This assertion doesn't work anymore when rewards have 0 price, so now it uses a doesNotHaveRewardLine method to ensure the reward is not applied PosLoyaltyRewardProductTag tries to claim the same reward 2 times on the same order with different products. This is no longer possible, the product for a reward is locked, only the qty can be changed. This is consistent with sale_loyalty and website_sale_loyalty. PosLoyaltyChangeRewardQty DDD Test Partner starts with 100 points. Adding a reward line automatically adds the maximum claimable qty. Also change the price from negative line to 0 test_buy_x_get_y_reward_qty - updated to apply the free product reward manually PosLoyaltySpecificDiscountTour tries to claim the same 10$ reward twice. The customer has enough points to claim the reward 2 times, but the new module doesn't allow applying the same discount reward multiple times, even if the points would allow. This is consistent with sale_loyalty test_receipt_data_pos_loyalty tries to call a `postProcessLoyalty` which doesn't exist anymore and loyalty is synced automatically after orders are validated test_gift_card_communication - the cards are created through the create_pos_cards method on the loyalty.card. Then the _process_loyalty() method replaces `confirm_coupon_programs` and generates all the history and card data, and then send communications for newly created cards test_loyalty_history - `create_pos_cards` creates the new cards instead of `confirm_coupon_programs`. But it only creates empty cards, which are awarded points only on `_process_loyalty` which is called afterwards. Multiple calls to `_process_loyalty` on the same order are considered duplicate, and will not create any new data on the backend. test_reward_line_tax_grouping_key created a coupons program with an automatic trigger and expected the product to apply the points. Coupon programs can only be `with-code` triggered, and the rules don't generate points, they only enable the points on the coupons. So I changed the program type to `promotion` PosLoyaltyTour2 used the old `Reset Rewards` button which doesn't exist anymore, so the removal and re-apply of rewards is done manually. It also has the same free product reward negative line changes --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update renames an internal loyalty coupon reference to a card reference in German POS certification and Mexican POS invoicing areas. It keeps these country-specific POS features aligned with the broader loyalty system refactor, reducing the risk of inconsistencies during future upgrades.
Original PR description
As part of the pos_loyalty refactor, the pos_orderline.coupon_id field was renamed to pos_orderline.card_id. This PR renames wherever the field is used to keep everything consistent Community PR-[#274951](https://github.com/odoo/odoo/pull/274951) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948)
The expense dashboard was updated as part of Odoo’s Owl 3 migration, replacing an older internal component update mechanism with its newer equivalent. This keeps the HR Expense interface aligned with the platform’s latest technology and helps reduce future maintenance risk without changing user-facing functionality.
Original PR description
As part of the Owl 3 migration, replace onWillUpdateProps hook with the appropriate Owl 3 alternatives. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update replaces an older internal screen-update mechanism across several Odoo apps so they remain compatible with the next version of Odoo's web interface framework. It is a behind-the-scenes cleanup with no intended change to everyday workflows.
Original PR description
One last batch of grouped mechanical `useLayoutEffect` migration before having to do the rest one by one. WHY: useLayoutEffect is removed in OWL3 Enterprise PR: https://github.com/odoo/enterprise/pull/128927 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change updates several Odoo Enterprise apps to use newer internal screen update mechanisms. It helps keep Documents, Social, Marketing Automation, Spreadsheet, and Timesheets compatible with upcoming platform changes without changing day-to-day functionality.
Original PR description
One last batch of grouped mechanical `useLayoutEffect` migration before having to do the rest one by one. Community PR: https://github.com/odoo/odoo/pull/284142
This change updates internal web interface code as part of Odoo's move to the newer Owl 3 framework. It helps keep the web client maintainable and compatible with future platform improvements, with no expected direct change for day-to-day users.
Original PR description
As part of the Owl 3 migration, this pr aims to replace **onWillUpdateProps** hook with the appropriate Owl 3 alternatives. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr