Thursday, February 5, 2026
11 changes · saas-18.3
Enhancements to existing features
This update enhances the reliability of syncing user accounts with Google Calendar. Previously, the sync process could time out with many users due to a single, lengthy operation. Now, the system uses multiple, smaller sync jobs targeting different user groups, significantly reducing the risk of timeouts and improving overall synchronization performance.
Original PR description
Before this commit, the sync cron of google could only work by performing a search on the users and starting the sync. But the cron could timeout with a large amount of users. As users were fetched in the same order, some users could never be synced. This change allows to create several cron with different type of user in self (by company, department, etc) to allow distribute the sync effort over several crons. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#210286
Resolved issues and error corrections
This update resolves a discrepancy in how the 'Line Extension Amount' is calculated within the account_edi_ubl_cii module. The change ensures accurate untaxed and total amounts in invoices, particularly when dealing with tax rounding across multiple lines. This improves invoice accuracy and compliance with tax regulations.
Original PR description
…-10] According to [BR-CO-10], LineExtensionAmount should be: <quantity> * <price_unit_wo_tax> + charges - allowances It was implemented as: <quantity> * <price_unit_wo_tax> + charges - allowances + <delta_total_excluded> <delta_total_excluded> is needed because it's the additional delta distributed by the global rounding of taxes accross the lines. If you don't add it, you will change the untaxed and total amount of your document. Instead, this commit adds 2 new values in the base_lines's tax_details: gross_total_excluded & discount_amount being the rounded versions of raw_gross_total_excluded & raw_discount but taking care of maintaining a global consistency regarding the global rounding. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#246382
This update resolves a bug that caused invoice generation to fail when handling multiple tax lines, specifically during Peppol integration. The fix ensures accurate tax calculations by grouping tax lines before aggregation, preventing a division-by-zero error. This ensures invoices are correctly generated and sent to Peppol.
Original PR description
Steps: - Belgian localisation - Activate peppol - Have two fixed sales taxes (T1 3.5 and T2 4.5) - Have 4 product: - P1: Any sale price, taxes 21% and T1 - P2: Any sale price, taxes 21% and T2 - P3: sale price 0, taxes 0% and T1 - Create an invoice, with following invoice lines: - P1, quantity 2 - P2, quantity 2 - P3, quantity -4 - Confirm and send it to peppol -> Traceback (ZeroDivisionError) The reason is that we try to extract emptying taxes like "Vidanges" and aggregate them into new base lines, but we treat all these taxes as they are the same but they are not always the same. Therefore we aggregate both price unit and quantity and we try to divide the aggregated price by the aggregated quantity. In our case we end up with a price unit of 2 (9 + 7 - 14) and a quantity of 0 (2 + 2 + -4) which leads to a zero division error. The fix adds a grouping function in order to group the extra lines by taxes before aggregating them. opw-5384928 Forward-Port-Of: odoo/odoo#244314
This update fixes an issue where tax amounts were incorrectly reported as discounts in the MyInvois XML invoices when taxes are configured as 'Included in Price'. The fix ensures that tax amounts are handled correctly, aligning with Peppol Malaysia e-invoice specifications and preventing inaccurate reporting of discounts. This ensures compliance with invoicing regulations.
Original PR description
The _add_consolidated_invoice_base_lines_vals method computed the gross subtotal using `price_unit * quantity`. When taxes are configured as "Included in Price", `price_unit` contains the…
The _add_consolidated_invoice_base_lines_vals method computed the gross subtotal using `price_unit * quantity`. When taxes are configured as "Included in Price", `price_unit` contains the tax-included amount, but `total_excluded` (used for the discounted amount) is tax-excluded.
This caused the tax amount to be incorrectly reported as an AllowanceCharge (discount) in the MyInvois XML, because:
discount_amount = price_unit * qty - total_excluded
= tax_included - tax_excluded
= TAX AMOUNT (not a discount!)
Example: Product priced at 110 MYR with 10% tax included:
- price_unit = 110 (tax-included)
- total_excluded = 100 (tax-excluded: 110 / 1.10)
- discount_amount = 110 - 100 = 10 ← incorrectly reported as discount
refs:
The cac:AllowanceCharge element in UBL is specifically for discounts and surcharges, NOT for taxes. According to the Peppol Malaysia e-Invoice specification:
https://docs.peppol.eu/poac/my/pint-my-sb/bis/#_allowances_and_charges
https://docs.peppol.eu/poacc/billing/3.0/codelist/UNCL5189/ (For this case we are interested in code 95)
Steps to Reproduce:
1. Configure a tax as "Included in Price" with Malaysia tax type
2. Create a product with that tax.
3. Create POS orders without any discount
4. Generate consolidated invoice and XML
5. XML incorrectly shows <cac:AllowanceCharge> with tax amount as discount
The fix uses `raw_total_excluded / discount_factor` (always tax-excluded) instead of `price_unit * quantity` (may be tax-included), consistent with the parent method:
https://github.com/odoo/odoo/blob/d645361a95037ac580d55e80bcb61d1eeb293efd/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py#L1846-L1876
Ticket [link](https://www.odoo.com/odoo/project.task/5476526)
opw-5476526
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#246490
Forward-Port-Of: odoo/odoo#245535This update resolves an issue preventing sales users from creating orders with products that have custom value attributes. The fix corrects a previous access restriction, ensuring sale users can properly add and utilize these attributes when creating sales orders. This expands the functionality of the sales module and improves the user experience for sales teams.
Original PR description
### Issue: Due to this issue, sale group cannot create a sale order with a product with custom value attribute. #### Steps to reproduce: 1- Create a product using admin with a custom value attribute. 2- Using demo user with sale access group, create a so. 3- Add the created product, and fill the custom value. The sale order cannot be saved due to access error. ### Cause: This is due to #197286. However that shouldn't have been applied to `product.attribute.custom` as that shouldn't be only accessed by people who can manage product like the rest of deleted accesses, but also it's needed by sale groups creating SOs. opw-5498719
This update fixes an issue where network interruptions during check-in/out could lead to duplicate attendance records and incorrect data. The fix introduces a timeout for geolocation requests and prevents multiple clicks, ensuring accurate attendance tracking even with unreliable network connections.
Original PR description
when using signInOut with geolocation, slow or temporarily unavailable network connections could cause getCurrentPosition to hang indefinitely (default timeout is infinite). This led to: - Frontend…
when using signInOut with geolocation, slow or temporarily unavailable network connections could cause getCurrentPosition to hang indefinitely (default timeout is infinite). This led to: - Frontend not updating, allowing multiple clicks and creating duplicate attendance entries - Incorrect check-in/check-out data __Steps to reproduce:__ 1. check in while online and server reachable 2. disconnect network or make server unreachable 3. check out Currently, getCurrentPosition would hang indefinitely. till the network is restored. then it will trigger the rpc call much later than the action time. in the meantime, the user could click multiple times, creating multiple attendance records. With this fix, getCurrentPosition will timeout after 10 seconds, then it will proceed without position. and if the server is unreachable, it will show an error notification without allowing multiple clicks. __FIX__ - Adds a timeout to getCurrentPosition - Uses a `_attendanceInProgress` flag to prevent multiple clicks - Ensures only the first callback (success or error) triggers the RPC opw-5414044 Forward-Port-Of: odoo/odoo#247140 Forward-Port-Of: odoo/odoo#244477
This update fixes an issue where Odoo didn't properly account for credit notes during bank reconciliation. Now, when reconciling a bank transaction with a partially paid invoice and a credit note, the system correctly uses the remaining balance ($800) instead of the full invoice amount ($1000). This ensures accurate financial reporting.
Original PR description
1. Create an invoice for $1,000 2. Create a credit note of $200 and apply it to the invoice. The invoice is marked 'partially paid.' The remaining due is $800. 3. Create a bank transaction of $700, reconcile with the invoice. 4. Edit the counterpart line, and click "fully paid". >>> Odoo does not consider the credit note and uses the full amount of $1,000 instead of the remaining due of $800 to reconcile. With the refactor of the bank rec, the way the amount is show is computed from econciled_lines_excluding_exchange_diff_ids in apply_amount.js which only takes into account the direct invoice and not the credit notes (in _compute_reconciled_lines_excluding_exchange_diff_ids it take the matched debit and matched credit so only the partial between the transaction and the move) opw-5485663
This update resolves an issue preventing the legal validation of annual VAT reports for the LU region. The fix adds missing required fields to the XML export, ensuring compliance with Luxembourg tax regulations. This ensures accurate reporting and avoids potential validation errors.
Original PR description
### Issue: The annual VAT report could not be legally validated because some mandatory parent fields were missing in the XML export ### Cause: This PR add some required fields:…
### Issue: The annual VAT report could not be legally validated because some mandatory parent fields were missing in the XML export ### Cause: This PR add some required fields: https://github.com/odoo/enterprise/pull/93357 However, for file validation, the following parent fields are mandatory if certain child fields are present: ``` - Field 129: Field 129 is mandatory if one of the following fields is filled : 771, 971, 772, 972, 774, 773, 973, 124, 128, 197 - Field 137: Field 137 is mandatory if one of the following fields is filled : 776, 976, 777, 977, 778, 978, 134, 136, 198 - Field 145: Field 145 is mandatory if one of the following fields is filled: 781, 981, 782, 982, 783, 983, 142, 144, 199 - Field 163: Field 163 is mandatory if one of the following fields is filled : 791, 991, 793, 993, 797, 795, 995, 158, 162, 200 - Field 175: Field 175 is mandatory if one of the following fields is filled: 396, 162 ``` 164 and 165 are also added according to this assertion: https://github.com/odoo/enterprise/blob/c93388741182f1873054557a6e7767186674fafa/l10n_lu_reports/models/l10n_lu_annual_tax_report.py#L167-L171 ### Note: This PR is related to the 18.0 PR: https://github.com/odoo/enterprise/pull/104785 It also fix issues in `_add_yearly_fields()` because the validation consider form as float instead of dict ### Steps to reproduce: - Install `l10n_lu_reports` and switch to LU company - Open the Tax Report `Annual VAT Declaration` - Export the XML - Notice that codes 396, 394 149, and 153 are present, but 129, 137, 145, 163 and 175 are missing opw-5119920 Forward-Port-Of: odoo/enterprise#106457 Forward-Port-Of: odoo/enterprise#105143
This update resolves an issue where Odoo was incorrectly including an UETR tag in ISO 20022 payment files, causing rejection by strict banks. The change ensures compliance with SEPA regulations, preventing errors and improving compatibility with financial institutions. This ensures seamless payment processing for SEPA transactions.
Original PR description
In Odoo 18.0, when a user selects the pain.001.001.09 format (ISO 20022), Odoo systematically includes the <UETR> (Unique End-to-end Transaction Reference) tag for every transaction. While valid under the general ISO 20022 XML schema, the <UETR> tag is not authorized by the EPC (European Payments Council) within the standard SEPA Credit Transfer (SCT) Rulebook. Strict banks (e.g., UBS, German banks) reject the entire file with errors such as: "No child element is expected at this point" when an UETR is detected in a domestic or intra-SEPA flow. Task: 5871528 Forward-Port-Of: odoo/enterprise#105792 Forward-Port-Of: odoo/enterprise#105518
This update fixes an issue where WhatsApp messages to blacklisted numbers weren't being blocked correctly when the recipient's country differed from the sender's company country. The fix ensures that all blacklisted numbers, regardless of the recipient's location, are properly blocked, improving communication security and preventing unwanted messages.
Original PR description
Sending a WhatsApp message to a blacklisted number fails to be blocked if the recipient's phone number country differs from the sender company's country. ### Steps to reproduce 1. Configure a…
Sending a WhatsApp message to a blacklisted number fails to be blocked if the recipient's phone number country differs from the sender company's country.
### Steps to reproduce
1. Configure a WhatsApp account.
2. Set the Company's country to Germany (+49).
3. Create a Contact with a Belgian phone number (e.g. +32456001122).
4. Send a template message to this contact.
5. Have the contact reply with "STOP" to opt-out (this correctly adds +32456001122 to the blacklist).
6. Send another message to the contact.
- Expected: The message is blocked.
- Actual: The message is sent successfully.
### Root cause
The blacklist search logic relies on implicit phone number sanitization which behaves incorrectly for international numbers without a `+` prefix.
1. `whatsapp.message` stores numbers as `CountryCode + NationalNumber` without a `+` (e.g. "32456001122").
2. `phone.blacklist` stores numbers in E.164 format with a `+` (e.g. "+32456001122").
3. When searching `phone.blacklist` with "32456001122", the system interprets it as a local number for the Company's country (Germany) because of the missing `+`.
4. It reformats the search term to German E.164 ("+4932456001122").
5. The query fails to match the actual blacklisted number ("+32456001122"), allowing the message to pass.
### Fix
Explicitly prepend a `+` to the recipient's number before searching the blacklist. This forces the validation logic to parse the number as international (E.164), bypassing the company-country bias and ensuring the search term matches the stored blacklisted number.
opw-5401789
Forward-Port-Of: odoo/enterprise#106395
Forward-Port-Of: odoo/enterprise#104556This update fixes an issue where the quantity of components in a manufacturing order wasn't correctly updated after exiting the barcode MRP operation. Specifically, the system incorrectly displayed quantities after editing a component's quantity. The fix ensures that move quantities are accurately reflected, preventing discrepancies in inventory tracking.
Original PR description
**Issue** When leaving the barcode MRP operation, `post_barcode_process()` may incorrectly update the move quantities. **Steps to reproduce** - Create a product with a BOM using a component with qty…
**Issue** When leaving the barcode MRP operation, `post_barcode_process()` may incorrectly update the move quantities. **Steps to reproduce** - Create a product with a BOM using a component with qty 6. - Create an MO producing qty 1. - Open the Barcode app > Manufacturing > open the MO (remove “MO Ready” filter if needed). - Click “+1”. - Edit the component qty from 6 to 3. - Exit the operation. - Re-enter the operation. -> The component shows 3/3 instead of 3/3 and 0/3. **Cause** On exit, `_onExit`: https://github.com/odoo/enterprise/blob/776848dc4e29d07a027847fde46a59f84dd35f56/stock_barcode/static/src/models/barcode_picking_model.js#L1489 calls `post_barcode_process()`, which triggers `split_uncompleted_moves`: https://github.com/odoo/enterprise/blob/776848dc4e29d07a027847fde46a59f84dd35f56/stock_barcode/models/stock_move.py#L16 correctly creating a `stock.move.line` with qty 3. However, `_truncate_overreserved_moves`: https://github.com/odoo/enterprise/blob/776848dc4e29d07a027847fde46a59f84dd35f56/stock_barcode/models/stock_move.py#L40 then reduces the move quantity to `max_reserved_qty = 3` and unreserves the remaining 3 units: https://github.com/odoo/enterprise/blob/776848dc4e29d07a027847fde46a59f84dd35f56/stock_barcode/models/stock_move.py#L49 This happens because the newly created move line is initialized with `reserved_uom_qty = 0`: https://github.com/odoo/enterprise/blob/776848dc4e29d07a027847fde46a59f84dd35f56/stock_barcode/static/src/models/barcode_picking_model.js#L1256 leading to `max_reserved_qty = quantity_done = 3 < move.quantity = 6`, while `move.product_uom_qty` is still 6. opw-5166763 Forward-Port-Of: odoo/enterprise#104199 Forward-Port-Of: odoo/enterprise#100314