Friday, April 3, 2026
7 changes · saas-18.3
New functionality added to Odoo
This update adds support for Peppol Business Level Responses (BLR) for invoices and credit notes, ensuring compliance with European standards for electronic invoicing. It introduces new response types (acknowledgement, confirmation, rejection) to facilitate communication with Peppol participants, improving integration with this key trading partner. This change supports efficient and accurate exchange of financial documents within the Peppol network.
Original PR description
Peppol offers a response system to the document received through it. These are called Business Level Responses, and their documentation can be found here:…
Peppol offers a response system to the document received through it. These are called Business Level Responses, and their documentation can be found here: https://docs.peppol.eu/poacc/upgrade-3/profiles/63-invoiceresponse/#introduction-to-openpeppol-and-bis The specific BLR implemented in this commit is targeted to invoices and credit notes. 3 types of responses are mandatory for a Peppol participant to correctly adhere to the BLR service: acknowledgement (different to the transport ack), confirmation and rejection. More response's types are available but were not implemented as they're not mandatory/needed (for now). For rejection, a list of at least one reason must be given, and actions can be suggested to the sender of the document for the eventual next invoice shipment. Same principle goes for Nemhandel, with some differences: only 2 responses, BusinessAccept and BusinessReject (no Acknowledgement, and no reasons are needed in case of rejection. Instead, the user can send a string message. iap PR: https://github.com/odoo/iap-apps/pull/1364 task-5237698 Forward-Port-Of: odoo/odoo#243191
Enhancements to existing features
This update enhances the accuracy of product imports by making the matching process more flexible. Previously, exact name matches were case-sensitive and substring searches could lead to incorrect matches. Now, the system uses a similarity ratio (90%) to improve reliability and reduce errors, ensuring products are correctly associated during import.
Original PR description
Before this commit: - Product retrieval during import relied on exact name match and substring (ilike) search. - Exact name search was case sensitive, so values like `Network Cable` would not match…
Resolved issues and error corrections
This update resolves an issue where the Master Production Schedule (MPS) wasn't accurately considering safety stock levels for indirect demand. The changes ensure that demand forecasts are adjusted to account for safety stock, leading to more reliable production planning and reduced stockouts. This improves inventory management efficiency.
Original PR description
Steps to reproduce: ------------------- * Enable "Master Production Schedule" in Inventory settings * Create tracked Product "Child" and set up a vendor * Create tracked Product "Parent" and set up a…
Before this commit: - Product retrieval during import relied on exact name match and substring (ilike) search. - Exact name search was case sensitive, so values like `Network Cable` would not match `Network cable`. - Substring matching could return unrelated products (e.g. `Wireless bluetooth speaker` gets matched with `Wireless bluetooth speaker battery`), leading to unrelated matches. After this commit: - Exact name search is now case insensitive, allowing matches such as `Network Cable` and `network cable`. - Substring based matching has been replaced with a similarity ratio (90%) to reduce false positives and improve matching reliability against customer database product names. Technical: - Replaced `=` with `=ilike` in the exact name search domain to make the lookup case insensitive. - Similarity ratio is computed using Python's `difflib.SequenceMatcher` on product names, with a minimum threshold of 90% to qualify as a match. - Added system parameter for configurable product name similarity threshold. task-5951469 Forward-Port-Of: odoo/odoo#257175 Forward-Port-Of: odoo/odoo#252147
Steps to reproduce: ------------------- * Enable "Master Production Schedule" in Inventory settings * Create tracked Product "Child" and set up a vendor * Create tracked Product "Parent" and set up a bom as component "Child" and Lead Time: 2 days * Create tracked Product "GParent" and set up a bom as component "Parent" and Lead Time: 2 days * Open MPS and add your three products: - Child, Parent: activate indirect demand - Parent: Safety Stock Target of 10 * Add 1 in the forecast demand for "Gparent" on third column -> Will have 20 Indirect Demand Forecast of Child in the first column and -9 on the second Observation: ------------- Usefull comment form the function : https://github.com/odoo/enterprise/blob/b332af45a46b2295797a5096f68b7953554a495b/mrp_mps/models/mrp_mps.py#L424-L447 When creating a demand from the MPS, it will always take the first date of the interval (ex: Week 10 (2-8/Mar), it will create the demand for the 2 of Mars) When calculating the production schedule. we wil we calculate each product for each date_range: https://github.com/odoo/enterprise/blob/ea805995f66e007c0aef6b473a2b16dbe54d73bc/mrp_mps/models/mrp_mps.py#L488 https://github.com/odoo/enterprise/blob/ea805995f66e007c0aef6b473a2b16dbe54d73bc/mrp_mps/models/mrp_mps.py#L509 When calculating the values for a product, we will set the indirect demand qty for it component The demand will created the demand in function of the date of when the parent need and the lead time (it will for the previous date range because of the lead time): https://github.com/odoo/enterprise/blob/ea805995f66e007c0aef6b473a2b16dbe54d73bc/mrp_mps/models/mrp_mps.py#L554 https://github.com/odoo/enterprise/blob/ea805995f66e007c0aef6b473a2b16dbe54d73bc/mrp_mps/models/mrp_mps.py#L555 If the demand is not equal to the resplensih_qty we will create another demand to compensate, it will use the first date of range minus the lead time it will send it to the previous date range: https://github.com/odoo/enterprise/blob/ea805995f66e007c0aef6b473a2b16dbe54d73bc/mrp_mps/models/mrp_mps.py#L556-L560 In our case this will create the issue, since it will try to compensate each time on the previous week. opw-5413838 Forward-Port-Of: odoo/enterprise#112393 Forward-Port-Of: odoo/enterprise#107671
This update fixes a crash that occurred when scanning GS1 barcodes in the Point of Sale system. The issue stemmed from incorrect barcode handling during inventory updates, specifically when using lot numbers. The fix ensures the system correctly identifies and processes GS1 barcodes for accurate product scanning and order fulfillment.
Original PR description
**Steps to reproduce:** * Install `point_of_sale` module. * Go to Settings: * Enable Lots & Serial Numbers. * Enable Variants. * Set Barcode Nomenclature to Default GS1 Nomenclature. * Create a…
**Steps to reproduce:**
* Install `point_of_sale` module.
* Go to Settings:
* Enable Lots & Serial Numbers.
* Enable Variants.
* Set Barcode Nomenclature to Default GS1 Nomenclature.
* Create a product:
* Enable Track Inventory set to By Lots.
* Under Attributes & Variants:
* Add an attribute with two values and save.
* Generate product variants:
* Open one variant and set barcode to 5123648695416.
* Update inventory:
* Go to the main product (template).
* Update On Hand Quantity:
* Update On Hand Quantity with a lot/serial number:
010512364869541610784512.
* Select the variant with the defined barcode.
* Under Point of Sale tab:
* Set a POS Category.
* Open a POS session and scan:
010512364869541610784512.
**Observed behavior:**
* Scanning the GS1 barcode in POS raises a traceback:
*TypeError: Cannot read properties of undefined (reading
'product_template_attribute_value_ids')*.
**Cause:**
[Scans GS1 barcode: 010512364869541610784512]
│
├─ ProductScreen._barcodeGS1Action(parsed_results)
│ • product = await _getProductByBarcode(productBarcode) ✅ found
│ • calls `addLineToCurrentOrder(vals, { code: lotBarcode })`
│ ⚠️ only `lotBarcode` passed, `productBarcode` discarded
│
├─ PosStore.addLineToCurrentOrder() → addLineToOrder()
│ • product has variants → isConfigurable() = true
│
├─ PosStore.handleConfigurableProduct()
│ • calls openConfigurator(productTemplate, { ...opts })
│ opts = { code: lotBarcode }
│
└─ PosStore.openConfigurator()
• opts.code = lotBarcode → truthy → enters if(opts.code) branch
• getBy("barcode", opts.code.base_code)
• getBy("barcode", "784512") ← "784512" is a LOT number, not a product barcode!
→ returns undefined ❌
• product packaging lookup also fails → undefined ❌
• product = undefined
│
└─ attributeLinesValues.map(values =>
values.filter(value =>
product.product_template_attribute_value_ids.includes(value)
^^^^^^^ undefined → 💥 TypeError
**Fix:**
* Pass the product from `handleConfigurableProduct` to the configurator.
* If no product is found using `opts.code`, use the passed product
instead.
---
opw-6031909
Forward-Port-Of: odoo/odoo#254572This update fixes an issue where newly created stock move lines would disappear from the picking details view after a refresh. The fix ensures that all move lines associated with a picking are consistently displayed, improving the user experience and preventing data loss.
Original PR description
**Problem:** When creating a new stock.move.line in the moves view (accessed via smart button from a picking), the newly created line disappears after any refresh action (manual refresh or triggering…
**Problem:**
When creating a new stock.move.line in the moves view (accessed via smart button from a picking), the newly created line disappears after any refresh action (manual refresh or triggering "Put in Pack").
**Steps to reproduce:**
1. Open a receipt/picking operation
2. Click on the "Moves" smart button to open the detailed operations view
3. Create a new stock.move.line record
4. Click "Put in Pack" or manually refresh the page
5. Observe that the newly created line disappears
**Current behavior:**
The newly created stock.move.line disappears from the view after refresh, and only reappears if you navigate back to the picking and then return to the moves view.
**Expected behavior:**
The newly created stock.move.line should remain visible in the view after refresh or any action that triggers a view reload.
**Cause of the issue:**
The action_detailed_operations method uses a static domain [('id', 'in', self.move_line_ids.ids)] that captures a snapshot of move line IDs at the moment the action is opened.
https://github.com/odoo/odoo/blob/22ac818970f104a732cc7d24afc440cf0e6d74bd/addons/stock/models/stock_picking.py#L1204-L1212 When a new stock.move.line is created in this view, its ID is not included in the original static list. Any refresh (manual or triggered by operations like "Put in Pack") re-applies this static domain, filtering out the newly created lines because their IDs weren't captured in the initial list.
**Fix:**
Using a dynamic domain based on picking_id ensures all move lines belonging to the picking are always visible, regardless of when they were created. This aligns with the expected behavior of showing "all move lines for this picking" rather than "only the move lines that existed when the view was opened". The relational lookup [('picking_id', '=', self.id)] is re-evaluated on each refresh, automatically including any newly created lines that have the correct picking_id set.
opw-5398620
Forward-Port-Of: odoo/odoo#247170This update resolves an issue where the LPP (a Swiss tax levy) was incorrectly applied to employee payrolls when employees were not covered by insurance. The fix ensures that LPP is only calculated for insured employees, aligning with Swiss tax regulations and improving payroll accuracy. This change impacts the Swiss payroll module.
Original PR description
Forward-Port-Of: odoo/enterprise#112824
This update corrects a bug where discounts were applied twice to service tasks, resulting in incorrect pricing. The fix ensures discounts are applied correctly based on the sales order line or list price, preventing over-discounting and ensuring accurate pricing for service tasks.
Original PR description
Currently, when the user creates a task for a customer with a discount pricelist, the discount is applied twice for the service. <h2>Steps to produce:</h2> * Install `industry_fsm_sale` and enable…
Currently, when the user creates a task for a customer with a discount pricelist, the discount is applied twice for the service. <h2>Steps to produce:</h2> * Install `industry_fsm_sale` and enable `Discounts` and `Pricelists` in settings. * Create a pricelist with a price rule of type discount that applies 10 percent discount to every product. * Go to Customers > Acme Corporation > Sales & Purchase and set the pricelist. * Go to Field Service > Create a Task, and set `Customer` to Acme Corporation. * Add a timesheet with Time Spent 1 > Mark the task as Done > Sale Order <h2>Observed behavior:</h2> The discount is applied twice to the product on SO: **Product**: Service on Timesheets **Unit Price**: `$40` (excluding tax) **First discount:** The 10 percent discount on the unit price of the product. Product unit price is set from `$40 -> $36 ` **Second discount:** The 10 percent discount on the SO line itself. `$36 -> $32.4 ` The untaxed amount is: `$32.40` which should be `$36.00` <h2>Root cause:</h2> This happens because, at line [1], the unit price is already set to the final price from the pricelist when the sale order line is created. Since discounts are enabled, [2] applies an additional discount to that same price, causing the discount to be applied twice. <h2>Solution:</h2> When creating the sales order: * **Discount setting is on:** use list price so the discount is applied from the sales order. * **Discount setting is off:** set the product unit price to the discounted price. [1]- https://github.com/odoo/enterprise/blob/224d2453cc975a3e333825370beaf30d27d89f10/industry_fsm_sale/models/project_task.py#L658 [2]- https://github.com/odoo/odoo/blob/76717e588bfd012b42e859bfc829257d899c6165/addons/sale/models/sale_order_line.py#L788 opw-5432088 Forward-Port-Of: odoo/enterprise#112761 Forward-Port-Of: odoo/enterprise#103950