Friday, August 21, 2026
39 changes · saas-19.3
New functionality added to Odoo
Adds Sri Lanka-specific tax invoice handling so businesses can issue invoices in the required local format. The update introduces compliant invoice numbering, VAT registration detection, and report wording changes for tax invoices, helping Sri Lankan companies meet local invoicing rules.
Original PR description
This commit introduces the `l10n_lk_invoice` module to support specific tax invoicing requirements for the Sri Lankan localization. Key features include: * Custom Sequence Format: Implements the…
This commit introduces the `l10n_lk_invoice` module to support specific tax invoicing requirements for the Sri Lankan localization. Key features include: * Custom Sequence Format: Implements the mandatory Sri Lankan tax invoice sequence format `YYMMM_QQQQ_XXXXX` (e.g., `26MAY_BRN01_00001`), utilizing the journal code as the `QQQQ` component. * VAT Registration Tracking: Adds a `l10n_lk_vat_registered` boolean field to `res.partner` and `res.company`. This auto-computes based on the Sri Lankan VAT format (requiring >= 13 digits and ending in the "7000" suffix). * PDF Report Modifications: * Replaces the "Invoice" title with "Tax Invoice" when both the supplier and the customer are VAT registered, AND the invoice contains taxable supplies (excludes fully exempt invoices). * Replaces "Delivery Date" with "Supply Date" on tax invoices. * Injects "Mode of Payment" into the document header when a preferred payment method is selected on a tax invoice. * Resequencing Wizard Support: Overrides `account.resequence.wizard` to seamlessly handle Sri Lanka's specific month abbreviation formatting during mass resequencing. Task-6209151 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273592
Adds a dedicated, responsive schedule page for each event location so attendees can see current and upcoming sessions in the event’s local timezone. The display updates automatically and keeps the latest schedule visible during temporary connection problems, improving on-site event communication.
Original PR description
Add a dedicated responsive schedule page for each event location, showing live and upcoming published tracks in the event's timezone. The display refreshes automatically without a full page reload and keeps the last known schedule visible during network issues. Task-6357556
Polish companies can now prepare a dedicated VAT-UE report instead of relying on the generic EC Sales List. The report includes relevant EU sales and purchase transaction types and supports XML export in the official Polish structure, helping streamline compliance filing.
Original PR description
Description of the issue this commit addresses: Polish companies only have the generic EC Sales List without purchase transactions or an XML export matching the official VAT-UE structure. --- Desired behavior after this commit is merged: This commit adds a Polish VAT-UE report covering intra-Community supplies, acquisitions, services, triangular transactions, and the official XML export. --- task-6368808 Forward-Port-Of: odoo/enterprise#127083
Enhancements to existing features
Point of Sale now continues after payment validation without waiting for the receipt to finish printing. This reduces delays at checkout and keeps cashier workflows moving, while tests were updated to match the new behavior.
Original PR description
- Stop awaiting the receipt print in the POS after order payment validation - Adapt tours to this behavior change task-id: 6425204 enterprise PR: https://github.com/odoo/enterprise/pull/127818 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282990 Forward-Port-Of: odoo/odoo#280002
Resolved issues and error corrections
Starting a new chat now only broadcasts it to active users, preventing archived accounts from causing access errors. This keeps chat creation reliable for current users and ensures one invalid recipient does not block notifications to others.
Original PR description
Before this commit, starting a chat with a partner that has an archived user broadcast the new channel to that user too. This happens because _get_or_create_chat searches the partners with active_test=False, only to check that the given ids exist, and a recordset union keeps the environment of its left operand. The flag therefore reaches user_ids, which stops filtering on active. Archived users only reach the broadcast since "[REF] mail, im_livechat: use user in channel._broadcast", as the main_user_id it replaced is always active. The problem is that _broadcast passes each user to with_user, so the whole channel payload is computed with the rights of a user the caller never asked for, and lands on a bus channel no session can subscribe to. This commit fixes the issue by keeping active_test=False on the search alone, and by asking for active users at the broadcast. task-6483179
The Point of Sale product configuration window now shows product reference codes again, making it easier for staff to identify the correct items. Searches using a variant reference now open the matching variant directly, reducing selection mistakes during sales.
Original PR description
We reintroduce the default code on the product configuration modal, and ensure that searching for a variant reference opens the right variant. task-6463377 Forward-Port-Of: odoo/odoo#282889 Forward-Port-Of: odoo/odoo#282707
French PDP e-reporting entries now have a dedicated view that highlights information specific to those records. This helps users review relevant details more easily without changing the standard accounting move view used elsewhere.
Original PR description
This commit will add a new view for the ereporting moves to be able to see some specific info without touching the base move view. task-6274213 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270101
Bank statement matching now recognizes invoice payment references even when formatting characters such as slashes are missing. This helps payments reconcile automatically in more cases, reducing manual matching work for accounting teams.
Original PR description
Backport of: https://github.com/odoo/odoo/commit/1a737a654e1f51ae4979a95a960c33770cf0746d Before this commit, the "try_auto_reconcile" algorithm was finding moves when there was a perfect match with either the ref of a move line, the move name, the payment reference and now a sanitize version of the payment ref. For example if an invoice had SO12/1234 as the payment reference, if the statement line has a label SO121234 nothing was found. This commit will then add a new non stored computed field to sanitize the payment ref on the invoice level to help those cases task-6119841 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283508
Turkish payroll settings have been updated for 2026, including clearer minimum wage naming and new configurable social security contribution parameters. This helps payroll teams calculate employee contributions more accurately using the applicable minimum and maximum contribution bases.
Original PR description
- Update the Turkish payroll rule parameters for 2026. - Rename the minimum wage parameter to 'Turkiye Minimum Net Wage'. - Add configurable parameters for the SSI minimum contribution base and employee contribution rate. - Update the SSI contribution computation to account for both the minimum and maximum contribution bases. **task-6397284** Forward-Port-Of: odoo/enterprise#128520 Forward-Port-Of: odoo/enterprise#125985
Amazon order syncing has been updated to use Amazon's newer order service before the old one is retired in 2027. This should keep Amazon sales integrations working reliably and improve sync efficiency by receiving order details in fewer steps.
Original PR description
Amazon has announced the deprecation of the Orders v0 API, with a removal date of March 27, 2027. In this commit, we migrate to the new v2026-01-01 API. This new version restructures how order data is queried and delivered, shifting from a multi-request architecture to a nested consolidated payload. This optimizes our sync performance by eliminating the N+1 query problem when fetching order items. Key changes: - Operation Consolidation: `getOrders` is replaced by `searchOrders`. Because Amazon now embeds orderItems directly inside each order object natively, we remove our secondary item-fetching loops. - Financial aggregation: Item prices, taxes, shipping, and discounts are no longer flat fields on the item but are centralized into a `proceeds` object. - Replacing of deprecated flags. - Reorganization of order-related fields. task-5972714 Forward-Port-Of: odoo/enterprise#128568 Forward-Port-Of: odoo/enterprise#114591
Bank reconciliation can now recognize invoice payment references even when formatting characters such as slashes are missing from the bank statement label. This helps payments match invoices more reliably and reduces manual reconciliation work.
Original PR description
Backport of: https://github.com/odoo/enterprise/commit/e0d3591c9c03077f01cb8c93979d610ab99a833c Before this commit, the "try_auto_reconcile" algorithm was finding moves when there was a perfect match with either the ref of a move line, the move name, the payment reference and now a sanitize version of the payment ref. For example if an invoice had SO12/1234 as the payment reference, if the statement line has a label SO121234 nothing was found. This commit will then add a new non stored computed field to sanitize the payment ref on the invoice level to help those cases task-6119841 Forward-Port-Of: odoo/enterprise#128574
Accrued expense entries no longer fail when a purchase order line has been reduced to zero quantity. This prevents an error popup and lets users complete the accounting process for received goods more reliably.
Original PR description
### Steps to Reproduce: 1. Have a product where Track Inventory is enabled and the product category is FIFO and Perpetual 2. Create a PO for the product 3. Validate the receipt 4. Update the quantity…
### Steps to Reproduce: 1. Have a product where Track Inventory is enabled and the product category is FIFO and Perpetual 2. Create a PO for the product 3. Validate the receipt 4. Update the quantity on the PO to 0 5. Create Accrued Expense Entry > Traceback ### Description of the issue/feature this PR addresses: **Issue:** Currently when generating an Accrued Expense Entry for a PO where quantity on the line is updated to 0, the system crashes with an RPC error. This happens because reducing the line quantity to 0 sets the overall order amount to 0.0. Then. when the accrued orders wizard tries to calculate line-item ratios, it triggers a `ZeroDivisionError`. **Solution:** We can add a zero-check fallback condition when computing the line ratio inside `_compute_move_vals` in the `AccountAccruedOrdersWizard` class. The ratio calculation now defaults to 0.0 if the order total is zero, preventing division by zero. ### Current behavior before PR: Triggering the Accrued Expense Entry wizard on a PO with a changed quantity of 0.0 causes a `ZeroDivisionError` server error. The user receives an RPC error dialog and cannot proceed with creating the journal entry. ### Desired behavior after PR: The wizard should be able to process Purchase Orders with a line quantity of 0 without throwing an RPC error. The system should now cleanly generate the accrual entry based on received quantities. opw-6459403 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281808
Daily time off accruals based on worked time now respect the employee's local working calendar. This prevents employees on Monday-Friday schedules from incorrectly earning leave on Saturdays in some timezones.
Original PR description
## Current behavior: On a Monday–Friday working schedule, a Daily accrual plan that is based on worked time grants accrued time on Saturday as well, even though Saturday is not a working day. The…
## Current behavior: On a Monday–Friday working schedule, a Daily accrual plan that is based on worked time grants accrued time on Saturday as well, even though Saturday is not a working day. The employee accrues on 6 days per week instead of 5 (Sunday is correctly skipped. Only Saturday is wrong). ## Expected behavior: The employee accrues only on the 5 working days (Mon–Fri) → 5 grants per week. Saturday and Sunday should add nothing. ## Setup: - Working schedule: Standard 40h/week, Monday–Friday, 08:00–17:00. - All timezones set to Australia/Brisbane (UTC+10) and matching: employee, working schedule, and user are all the same timezone. - Accrual plan milestone: accrue 5 Hours, Daily, "At the end of the accrual period", "Based on worked time = Yes". ## Steps to reproduce: - Create the working schedule and accrual plan above, with the calendar timezone set to Australia/Brisbane. - Assign the accrual allocation to an employee, Starting on a Monday. - On the Time Off dashboard, use "Balance at the (date)" to project the balance day by day across a weekend (Friday → Saturday → Sunday → Monday). ## Cause of the issue: Accrual period boundaries were built as naive UTC midnights instead of local calendar midnights. ## Fix: Localize accrual period boundaries in the employee/resource timezone before calling resource calendar APIs. This bug is reproducible in multiple versions. PRs for: - v19.0: https://github.com/odoo/odoo/pull/279029 - v18.0: https://github.com/odoo/odoo/pull/279036 opw-6316062 Forward-Port-Of: odoo/odoo#283215 Forward-Port-Of: odoo/odoo#279029
Visitors viewing a public channel they have not joined will no longer be unexpectedly moved away from that channel. This ensures they can see the intended conversation and messages instead of being redirected to another pinned chat or an empty view.
Original PR description
Before this commit, a visitor opening the public page of a channel they are not a member of sees the channel for a moment, then Discuss leaves it on its own, and the messages of that channel never appear. This happens because every member of a channel notifies it that its pin state changed, self member or not. The member panel loads the members of the displayed channel right after the page, and as the visitor has no member there, the channel then concludes it is not pinned and Discuss opens the first pinned channel of the sidebar, or none at all. This commit fixes the issue by notifying the channel only when the pin state of self member changes. Backport of odoo/odoo@32f6eafea7071345622a068556c367bcf3169f79 Forward-Port-Of: odoo/odoo#283587
The Point of Sale combo configurator now keeps separate choices for identical products with different combo options. This prevents an extra burger line from appearing when staff apply a combo order, making restaurant tickets clearer and more accurate.
Original PR description
Steps: --- - Open the Burger combo choice. - Set the maximum quantity to 2. - Open the restaurant. - Add a cheese burger with Belgian fresh homemade fries. - Add another cheese burger with sweet potato fries. - Add Coca-Cola. - Click Apply. Issue: --- - A new cheese burger line is created even though a cheese burger line already exists. Cause: --- - When the same product has different configurations but belongs to the same `combo_item`, the last configuration overrides the previous one due to using the same key. Fix: --- - Differentiate configurations by appending `lineUuid` to the configuration key. - Ensure each product configuration is handled independently when computing included and extra combo items. task-5480195 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282988 Forward-Port-Of: odoo/odoo#243954
This fixes an intermittent issue where the website editing toolbar could disappear after a very quick edit-and-save action. Users can reliably return to editing, publishing, and preview controls without needing to reload or restart their workflow.
Original PR description
### Issue: `test_19_website_page_options` and `test_32_website_background_colorpicker` fail intermittently on RunBot on the "Click Edit" tour step: FAILED: [27/43] Tour website_background_colorpicker…
### Issue: `test_19_website_page_options` and `test_32_website_background_colorpicker` fail intermittently on RunBot on the "Click Edit" tour step: FAILED: [27/43] Tour website_background_colorpicker → Step <b>Click Edit</b> to start designing your homepage. (trigger: body .o_menu_systray .o_menu_systray_item.o_edit_website_container button) Element (body .o_menu_systray .o_menu_systray_item.o_edit_website_container button) has not been found. TIMEOUT step failed to complete within 10000 ms. The whole website systray (Edit, Publish, mobile preview, ...) disappears permanently after a save, with no way to bring it back ### Cause: https://github.com/odoo/odoo/blob/68066eaf3505bbf9ccaf60069072b9a62a49d4e4/addons/website/static/src/client_actions/website_preview/website_builder_action.js#L178-L207 1. Entering edit mode schedules a 200ms delayed removal of `website.WebsiteSystrayItem` from the systray registry to avoid a visual pop during the navbar hide animation 2. Leaving edit mode re-adds the entry only if not already present 3. If a full edit -> save cycle completes in under 200ms, the timer from step 1 is still pending when step 2 runs The re-add is skipped since the entry still looks present 4. The stale timer fires and removes the entry permanently Nothing re-adds it until the next edit-mode transition, which requires clicking the button that just disappeared runbot-240723 Forward-Port-Of: odoo/odoo#280064
German DIN5008 invoice PDFs sent by post now place the recipient address correctly in the required window. This prevents Pingen from rejecting Snailmail letters while keeping the normal report layout unchanged for other uses.
Original PR description
**Steps to reproduce:** - Install l10n_din5008 and accountant. - Enable Snailmail from Accounting → Settings. - Create a German customer (Fiscal Country: Germany). - Create and post a customer…
**Steps to reproduce:** - Install l10n_din5008 and accountant. - Enable Snailmail from Accounting → Settings. - Create a German customer (Fiscal Country: Germany). - Create and post a customer invoice using the DIN5008 report layout. - Select Send by Post. - Enable Developer Mode and navigate to `Settings → Technical → Email → Snailmail Letters`. - Open the generated letter and send it. **Current behavior:** The letter fails to be sent to Pingen with the following error: An error occurred when sending the document by post. Error: ` The attachment of the letter could not be sent. Please check its content and contact the support if the problem persists.` **Cause:** For Snailmail documents, Pingen validates that the recipient address is located within the DIN5008 address window. The current l10n_din5008 report renders additional document information instead of the address in the address area, preventing the compliance validation to fail. **Fix:** When rendering the report for Snailmail, ensure that only the recipient address is displayed in the DIN5008 address window while suppressing the additional information that would otherwise occupy this area. This preserves the standard DIN5008 layout for regular reports while generating a Snailmail-compliant PDF that passes Pingen’s validation. **Reference:** [Pignen Recipient Address Validation Rule](https://help.pingen.com/en/fix-and-enhance-letters/issue-with-recipient-address#040201) Ticket [link](https://www.odoo.com/odoo/project.task/6387869) opw-6387869 Forward-Port-Of: odoo/odoo#280320
Posted customer invoices no longer have their delivery date changed automatically when later deliveries are completed for the same sales order. This prevents already-finalized invoice information from shifting unexpectedly and keeps invoice records stable after posting.
Original PR description
Steps to Reproduce: 1. Confirm a Sales Order with one line -> creates delivery P1. Validate P1 with date_done = Day_A. 2. Create an invoice from the SO and post it -> invoice.delivery_date = Day_A.…
Steps to Reproduce: 1. Confirm a Sales Order with one line -> creates delivery P1. Validate P1 with date_done = Day_A. 2. Create an invoice from the SO and post it -> invoice.delivery_date = Day_A. 3. Add a new line to the same SO -> creates delivery P2. 4. Validate P2 with date_done = Day_B, where Day_B is earlier than Day_A. Issue: The already-posted invoice's `delivery_date` silently changes from Day_A to Day_B after step 4, even though nobody edited the invoice. This only happens when a delivery validated after posting has an earlier `date_done` than what was already used. Root Cause: `account.move.delivery_date (sale_stock)` is computed in `_compute_delivery_date()`, which depends on `sale.order.effective_date.effective_date` is itself computed as the earliest `date_done` among all done, facing deliveries on the order. Neither compute method checks whether the invoice is posted, so validating P2 triggers a chain reaction: the delivery is saved -> the sale order recalculates -> the invoice recalculates -> delivery_date gets overwritten on an already-posted invoice. `sale_stock` also marks `delivery_date` as protected, but this protection only works when the invoice itself is saved (write/create). Here, the change starts from saving the delivery (stock.picking), which never goes through the invoice's save method, so the protection never kicks in. `delivery_date` is also not on the list of fields Odoo normally blocks from editing after posting. Fix: `_compute_delivery_date()` now splits invoices into posted and non-posted before running. Non-posted invoices work exactly as before. Posted invoices are skipped from the sync and simply keep their current value instead of taking the newly calculated one. `sale.order.effective_date` itself is untouched only its effect on an already-posted invoice is blocked. Result: Once an invoice is posted, its `delivery_date` now stays fixed no matter what happens with later deliveries on the same sale order. `effective_date` keeps updating normally either way, confirming the fix only affects the invoice. Verified with both a script and a manual UI test. opw-6409171 Forward-Port-Of: odoo/odoo#283069 Forward-Port-Of: odoo/odoo#280978
Forms now keep the desktop-style layout on tablet-sized screens instead of switching too early to the mobile view. This makes data entry and review more comfortable on tablets by preserving the wider layout users expect.
Original PR description
Change the breakpoints to keep the desktop configuration on tablet resolutions instead of switching to the mobile display. task-6379696 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
Date and time changes shown in the chatter now use the current user's timezone instead of defaulting to UTC. The displayed format also matches the standard date/time style and includes the timezone, reducing confusion for teams working across languages and regions.
Original PR description
Although the code was attempting to render the tracking values in the current user's timezone. It did not work. While fixing this, we also wanted to make the time clearer in multi-language/timezone settings: - Date(time) is now formatted in the same way it is for datetime fields in the frontend - The timezone is added after it, so we know exactly what time is meant - Those two fields are reprocessed in the chatter so that we simply show them in the user's locale task-6456370
Odoo now shows the specific error details returned by Serbia's eFaktura service when an invoice submission fails. This helps users understand why an invoice was rejected instead of seeing only a generic technical HTTP error.
Original PR description
**Steps to reproduce:** - Install the Serbian EDI module `l10n_rs_edi`. - Configure eFaktura credentials on the company. - Create and confirm a Serbian customer invoice. - Send the invoice to…
**Steps to reproduce:**
- Install the Serbian EDI module `l10n_rs_edi`.
- Configure eFaktura credentials on the company.
- Create and confirm a Serbian customer invoice.
- Send the invoice to eFaktura.
**Observed Behavior:**
When the eFaktura API returns an HTTP error, Odoo only displays the generic exception generated by `requests`, for example an HTTP 400/500 error.
The actual error information returned by eFaktura in the response body is not shown to the user, making it difficult to understand why the invoice was rejected.
**Cause:**
`_l10n_rs_edi_send` catches `HTTPError`, `Timeout`, and `ConnectionError`, but the error message is built only from the Python exception.
For HTTP errors, the eFaktura API may return a response containing more precise information such as:
```json
{
ErrorCode: ...,
Message: ...
}
```
This response was not being used when displaying the error in Odoo.
**Fix:**
When an HTTP response is available and contains an eFaktura error payload, use the returned `ErrorCode` and `Message` as the error displayed on the invoice. Fallback to the existing connection/HTTP exception message when no usable API response is available.
opw - 6453653
Forward-Port-Of: odoo/odoo#281490This fixes a mobile scheduling issue where entering a time could close the time picker after the first digit and shift focus behind the panel. Users can now enter times more reliably in the Planning date/time field on touch devices.
Original PR description
Steps to reproduce ================== - Use a mobile viewport - Go to planning - Click on an empty cell - Click on the date - On the bottomsheet, click on the time at the bottom - Try to type 12:34…
Steps to reproduce ================== - Use a mobile viewport - Go to planning - Click on an empty cell - Click on the date - On the bottomsheet, click on the time at the bottom - Try to type 12:34 => Only 1 is entered and then the start_datetime field is focused behind the bottom sheet Cause of the issue ================== The <input type="time"/> listen to the onchange event. We listen to rawPickerProps changes using a reactive call. shouldFocus is then set to true, and the focus is done after the next render. The onchange event is called at a different time depending on the platform. On IOS and Firefox desktop: after changing hours or minutes On Android: once the apply button is clicked On Chrome desktop: After entering a single char Solution ======== The bottomsheet is only displayed when env.isSmall && hasTouch(). It doesn't make sense to focus the input, since we don't handle the keyboard in that case. opw-6386252 Forward-Port-Of: odoo/odoo#276617
Intercompany deliveries can now automatically unpack stock before it is received by the other company. This prevents confusing leftover package-based inventory records and makes intercompany stock balances easier to reconcile.
Original PR description
Issue: Compare to lot and serial number package are not multi company. It means that the package don't pass from a company to the other. So when a company deliver to another. The delivery will create a quant with the package. However the receipt in the other company will create a new quant without package (or a new package). It means that the quants are never reconcile and it could become difficult to understand what remains in intercompany location and what are artifact from past movements. In order to fix it, we introduce a new system parameter to directly unpack after the delivery. This way the receipt is always without source package and will automatically decrease the quant. opw-6376983 Forward-Port-Of: odoo/odoo#282448 Forward-Port-Of: odoo/odoo#277133
Argentinian delivery operations for Class X document types can now be saved without entering CAI authorization details or an expiration date. This aligns Odoo with local government rules, which only require those fields for Class R delivery notes.
Original PR description
Currently, when the user attempts to create a delivery operation for a class X document type, the system prompts the user to provide values for the CAI and CAI Expiration Date fields. ## Steps to…
Currently, when the user attempts to create a delivery operation for a class X document type, the system prompts the user to provide values for the CAI and CAI Expiration Date fields. ## Steps to produce: - Install `l10n_ar_stock` with demo data - Switch Company to `(AR) Exento` - Create a warehouse - Configuration > Operation Types > Delivery Orders - Set Document Type to `'(94) MAILING X' `and try to save ## Observed Behavior: The fields 'CAI' and 'CAI Expiration Date', which represent the authorization code and expiration date issued by the government, are currently configured as required fields. **Expected Behavior:** As specified on the [government site](https://www.argentina.gob.ar/normativa/nacional/resoluci%C3%B3n-1415-2003-81316/actualizacion#:~:text=Los%20datos%20indicados%20en%20el%20inciso%20a%29%2C%20puntos%207%2C%2010%2C%2011%2C%2012%20y%2013%2C%20s%C3%B3lo%20ser%C3%A1n%20para%20los%20remitos%20clase%20%27R%27%2E): > > 12. Printing authorization code, preceded by the acronym 'CAI No. ...'. > 13. Expiration date of the receipt, preceded by the legend 'Expiration Date ...' > > 'The data indicated in section a), points 7, 10, 11, 12 and 13, will only be for 'R' class delivery notes.' These statements indicate that the information mentioned in points 12 and 13, including the **CAI** and **CAI Expiration Date** fields, is applicable only to **'R'** class delivery notes. Therefore, for class X delivery notes, these fields should be optional rather than required. ## Root Cause: According to [1], the field is configured as a required field when a Document Type ID is selected. This configuration causes the **CAI** and **CAI Expiration Date** fields to become mandatory, regardless of the document type requirements defined by the government specification. [1]- https://github.com/odoo/odoo/blob/62b05c4ea61942072b6b1fb420fe3efedb11ed14/addons/l10n_ar_stock/views/stock_picking_type_views.xml#L11-L16 ## Solution: Apply constraints that align with the government specifications, allowing the CAI and CAI Expiration Date fields to remain optional for document types where they are not required. opw-6359503 Forward-Port-Of: odoo/odoo#283024 Forward-Port-Of: odoo/odoo#275533
This fix ensures that sales orders configured to require a customer signature are checked correctly during payment. It helps prevent orders from being paid before required approval steps are completed, improving process compliance for sales and point-of-sale flows.
Original PR description
See also: - https://github.com/odoo/enterprise/pull/127041 Forward-Port-Of: odoo/odoo#280403
Imported UBL invoices with document-level discounts or extra charges now correctly match those amounts to the right tax totals. This prevents incorrect tax adjustments during invoice import and improves accounting accuracy for affected supplier or customer invoices.
Original PR description
When importing UBL invoices that contain document-level allowances or charges with percentage taxes, the tax values were not linked to their corresponding `TaxSubtotal` group (`related_taxes_values`). As a result, the tax correction step (`_import_ubl_invoice_fix_taxes_amounts`) was unaware of document-level taxes, which caused wrong tax corrections. opw-6388544 Forward-Port-Of: odoo/odoo#279350
This fix prevents invoice imports from failing when a line has a 100% discount and tax is included in the price. Odoo now recalculates the tax from the original price in that case, improving reliability for electronic invoice processing.
Original PR description
Due to the following commit: 01efd8cfcce3269ca6b88d549a670b08a90298cb, a division by zero error is raised when a 100% discount is used with a price-included tax. When the discount is 100%, it is impossible to retrieve the original tax amount before discount using a simple multiplication as the current raw_tax_amount_currency is zero. In that case, we need to recompute taxes using the original unit price before discount. opw-6242701 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282430
Odoo now better identifies the correct partner or company when a formatted email address includes both a name and email. This prevents messages, such as invoice emails, from showing the wrong sender when multiple companies use the same email address.
Original PR description
### Issue: When multiple partners share the same email address, `_mail_find_partner_from_emails` may resolve to the wrong partner when the input is a formatted email like "`Name <email>`" This…
### Issue:
When multiple partners share the same email address, `_mail_find_partner_from_emails` may resolve to the wrong partner when the input is a formatted email like "`Name <email>`"
This affects use cases like email templates using `{{object.company_id.email_formatted}}` as sender, where the wrong company partner could be selected
### Cause:
The lookup in `done_partners` only matched on `email_normalized`, which cannot distinguish partners sharing the same email but with different names
The `email_formatted` field carries both name and email, allowing an exact match when the input is a formatted email
### Steps to reproduce:
- Install `account`
- Create an Email Template (Applies to: account.move, From: {{object.company_id.email_formatted}})
- Create a second company B with the same email as the default (e.g. info@yourcompany.com)
- In Settings (logged in as company B), set a Fiscal Position (e.g. US Taxable)
- Create an Invoice on company B
- In the chatter, click Send message, click the expand arrows button, use the three dots menu to select the template
- Send and check the Sender in the chatter
Before the fix, the sender resolves to the default company even though the invoice belongs to company B
opw-6260992
Forward-Port-Of: odoo/odoo#282966
Forward-Port-Of: odoo/odoo#269509Australian payroll batch processing now handles employees without leave allocations correctly. This prevents errors when payroll teams generate mixed batches of payslips, allowing processing to continue smoothly with zero unused leave where applicable.
Original PR description
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError: ``` File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip…
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError:
```
File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip
annual_gross = leaves_totals[payslip.id]['annual'] * daily_wage
~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 22
```
Current Issue:
`_l10n_au_get_unused_leave_by_type` only materialises leaves_by_date[payslip.id] inside the allocation loop, so a payslip whose employee has no matching allocation never gets a key. `_l10n_au_get_unused_leave_totals` then rebuilt a plain dict out of those entries and only fell back to a defaultdict when leaves_by_date was completely empty. A mixed batch is not empty, so the plain dict was returned and `_add_unused_leaves_to_payslip` raised on the payslips that were missing from it.
This never showed up in the UI, **where payslips are created one at a time**: a single slip either has an allocation, or produces an empty mapping that hits the fallback.
Approach:
Build the totals on a defaultdict and update it instead of returning a plain dict, so any payslip without allocation resolves to 0 rather than being absent. This also drops the need for the empty special case, and keeps the mapping consistent with the defaultdict returned by `_l10n_au_get_unused_leave_by_type`, which `_l10n_au_get_leaves_for_withhold` indexes the same way.
task-6465229
Forward-Port-Of: odoo/enterprise#127623TikTok order synchronization now skips shops that have not completed authorization. This prevents scheduled order syncs from failing when a newly connected shop is still waiting for TikTok authorization, improving reliability for sales operations.
Original PR description
Currently, an error occurs when orders are being fetched from shops with pending authorization. Steps to replicate: - Install `sale_tiktok`. - Open Sales > Configuration > Shops (Under the title…
Currently, an error occurs when orders are being fetched from shops with pending authorization.
Steps to replicate:
- Install `sale_tiktok`.
- Open Sales > Configuration > Shops (Under the title tiktok shops).
- Click `Connect New Shop` > Give values for `App key, App secret, Service ID`.
- Click `Connect Shop & Authorize` and then Return back to Odoo.
- Run the Scheduled Action `TikTok Shop: sync orders`.
Error:
```
File '/home/odoo/src/enterprise/saas-19.4/sale_tiktok/utils.py', line 171, in make_tiktok_api_request
if now > shop.access_token_expire_datetime - timedelta(minutes=5):
TypeError: unsupported operand type(s) for -: 'bool' and 'datetime.timedelta'
ValueError: TypeError('unsupported operand type(s) for -: 'bool' and 'datetime.timedelta'') while evaluating
'model._sync_orders()'
```
Cause:
- Since the shop has not yet been authorized with TikTok, the `access_token_expire_datetime` field is not set. This field is only populated after the shop is successfully authorized (see [this]).
- Later, when the `TikTok Shop: sync orders` cron runs, the flow reaches [here], where we checks whether the access token is expired and needs to be refreshed. At this point, `access_token_expire_datetime` is still False because the shop has not been authorized yet.
Solution:
- The orders should only be fetched from those shops that are authorized with TikTok.
- Used the `access_token` field to determine whether a shop is authorized, as it is only populated after the authorization flow is successfully completed.
[this]: https://github.com/odoo/enterprise/blob/593409ca160863d3e985f3cf5e2aadda1d450b41/sale_tiktok/controllers/onboarding.py#L52-L54
[here]: https://github.com/odoo/enterprise/blob/593409ca160863d3e985f3cf5e2aadda1d450b41/sale_tiktok/utils.py#L171
sentry-7631179329
Forward-Port-Of: odoo/enterprise#127148Fixed an issue where WhatsApp message templates with many mixed variable types could send values in the wrong placeholder positions. This helps ensure customers receive messages with the correct personalized information, especially in larger templates.
Original PR description
**Issue**:
Sending a WhatsApp template with 10 or more variables can assign values to the wrong placeholders when the body contains mixed variable types, such as free text, field, or user name variables.
Templates containing only free-text variables are not affected.
**Reason**:
Meta consumes template parameters positionally, but for mixed variable types, Odoo built the parameter list using the template variable recordset order.
That order can differ from the numeric placeholder order, notably placing {{10}}, {{11}}, {{12}}... before {{1}}
when sending the message, as the payload parameters are not ordered by their numeric placeholder index.
**Fix:**
Sort body variables by their numeric placeholder index before preparing the Meta payload.
Task-6401501
Forward-Port-Of: odoo/enterprise#128492
Forward-Port-Of: odoo/enterprise#125671Regular Timesheets users can now access Assistant Rules when the assistant feature is enabled, even if billing rate indicators are turned off. This prevents users from being blocked from needed configuration options unless they use debug mode.
Original PR description
Steps to reproduce --- 1. Install sale_timesheet_enterprise. 2. Go to Timesheets > Configuration > Settings (as Admin). 3. Disable the "Billing Rate Indicators" setting and enable timesheet…
Steps to reproduce --- 1. Install sale_timesheet_enterprise. 2. Go to Timesheets > Configuration > Settings (as Admin). 3. Disable the "Billing Rate Indicators" setting and enable timesheet assistant. 4. Log in as a normal user with "Timesheets > Own timesheet" access. 5. Open the Timesheets app. Issue --- The "Configuration" menu is completely hidden. The regular user cannot access the "Assistant Rules" menu unless they turn on debug mode. Cause --- When the billing rates feature is turned off, the _load_menus_blacklist function hides the Enterprise Configuration menu. The logic used an and condition, meaning the menu was only kept visible if the user was an Assistant AND had Manager/Admin rights. This locked out standard users. Fix --- Change the blacklist condition. Allow the Enterprise Configuration menu to stay visible if the user needs it for the Assistant feature (when UoM is not Days), OR if the user is a Manager/Admin. References to check other issues https://github.com/odoo/enterprise/pull/120609 https://github.com/odoo/enterprise/pull/117984 task - 6470183
Accounting users can now export the German Datev ATCH zip even when an invoice's main image attachment was added through a log note by someone else. This prevents an access error that blocked non-admin users from completing the export.
Original PR description
### Issue: When an invoice has an image as its main attachment added via a log note, any non-admin user who did not create the attachment gets an `AccessError` when exporting the Datev ATCH zip ###…
### Issue: When an invoice has an image as its main attachment added via a log note, any non-admin user who did not create the attachment gets an `AccessError` when exporting the Datev ATCH zip ### Cause: Since commit `e7c93e5a6f`, attachments uploaded via certain flows can be "orphaned" — their `res_model` is set to `False` and `res_id` to `0` via `_fix_attachments_on_record_from_files_data` This allows the attachment to appear in the chatter without being linked to the move's attachment list However, `_message_set_main_attachment_id` can still set such an orphaned attachment as `message_main_attachment_id` When a user without system rights tries to read it, the ORM access check uses `res_model=False` and `res_id=0`, which does not match the move the user has access to, raising an `AccessError` ### Steps to reproduce: - Install `l10n_de_reports` and switch to the DE company - Create and confirm an Invoice (any lines, any customer) - Add a Log Note with an image - Set Demo user's Accounting rights to `Invoicing & Banks` - Log in as Demo - Open the General Ledger - In the cog menu, choose `Datev ATCH (zip)` Before the fix, an `AccessError` is raised opw-6397804 Forward-Port-Of: odoo/enterprise#128484 Forward-Port-Of: odoo/enterprise#127787
Fixed an issue that blocked reconciliation of internal transfers between a parent company and a branch when the branch transaction used a reconciliation model. This prevents unnecessary company inconsistency errors and lets accounting teams complete valid intercompany bank matching without manual workarounds.
Original PR description
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: -…
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: - Have a company with branch both selected - On the branch, create a reconciliation model "Internal transfer" that assigns the whole balance to the liquidity transfer account - Have a Bank journal on the company and a Bank journal on the branch - On the branch bank journal, create a -100 transaction 'testb' and reconcile it using the branch internal transfer model - On the company bank journal, creata a 100 transaction, open the reconciliation widget and select the branch transaction to match it Issue: The reconciliation is refused with a company inconsistency error ``` Uh-oh! You’ve got some company inconsistencies here: - “BNK1/2026/00011 test” belongs to company “YourCompany” while “Reconciliation Model” (reconcile_model_id: 'Internal Transfer branch') belongs to another company. To avoid a mess, no company crossover is allowed! ``` However, if user manually assign the transfer account to the branch transaction, the reconciliation proceed as expected Analysis: When reconciling, we build the counterpart journal item by cloning the values of the matched move line, copying also the reconcile model. That field is company dependent and flagged copy=False, so it should not be propagated. opw-6365856 Forward-Port-Of: odoo/enterprise#127517
When generating a payroll payment file with the option to include unpaid payslips, all payslips actually included in that file are now marked as paid. This prevents payroll records from incorrectly remaining validated after payment processing, reducing manual follow-up for payroll teams.
Original PR description
Steps to reproduce: - Open the payment report wizard on a payslip or a pay run - Tick "Include Unpaid" and keep the extra payslips selected - Generate the SEPA file, then click "Mark as Paid" Issue: the extra payslips listed in the file stay in state "validated". Cause: mark_as_paid() paid payslip_ids, while the file is built from unpaid_payslips. Fix: pay the payslips that are actually listed in the file. Task 6428919 Forward-Port-Of: odoo/enterprise#127889
Odoo now saves successful UrbanPiper delivery provider status confirmations as soon as they reach the server. This prevents providers from appearing disabled in the POS when they were actually enabled through UrbanPiper, improving operational accuracy for restaurants using online delivery integrations.
Original PR description
When a POS session opens, every configured delivery provider is enabled through UrbanPiper. UrbanPiper later confirms the operation through a store_action webhook. Previously, Odoo only forwarded this confirmation to the POS frontend. The frontend was then responsible for persisting the new provider state. If no POS client received the notification, the confirmed state was not saved. Consequently, a provider could be enabled in UrbanPiper while appearing disabled in the POS. Persist successful store_action callbacks directly when they are received by the server. Update only the affected provider and let the frontend consume the resulting state without writing its potentially stale provider-state mapping back to the server. opw-6455361
German SEPA credit transfer files now leave out a company identifier that is not accepted in the older German bank file format. This keeps exported payment files valid and helps prevent banks from rejecting vendor payment batches.
Original PR description
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is…
### Issue before this commit: When generating a SEPA Credit Transfer batch using the German XML format (pain.001.001.03), the <LEI> tag is erroneously included in the exported file if an LEI is configured on the company. This invalidates the XML, causing banks to reject the file. ### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Go to Settins > Vendor Payments > SEPA Credit Transfer / ISO20022 and set Name Identification as 529900T8BM49AURSDO55 and Issuer as LEIMAN 3. Go to companies and set 529900T8BM49AURSDO55 as LEI in the DE company 4. Go to Accounting dashboard and click the 3 dots of the bank group, go to Configuration and set the Account Number and be sure in the Outgoing Payments tab XML Format is German 5. Create a new German company from Contacts with: 1. Country as Germany 2. VAT 3. Account Number in the Bank Accounts by adding one line: 1. example Account Number: DE65100500007201811026 2. example Bank: BNP Paribas 3. activate the Send Money button 7. Then go to Vendor > Payments and create a new one with Payment Method as SEPA Credit Transfer for the German company created 8. Go back and select the new payment from the list and click create batch and print it 9. In the XML of pain.001.001.03.(DE) file, the LEI tag should not be included. ### Cause of the issue: The XML generation logic does not filter out the <LEI> element for older schema versions like pain.001.001.03, which do not support this tag. ### Reason to introduce the fix: To ensure strict schema compliance and prevent bank rejections. The <LEI> element is now properly omitted from pain.001.001.03 files and restricted only to newer formats (e.g., pain.001.001.09) where it is valid. opw-6428150 Forward-Port-Of: odoo/enterprise#127758
The timesheet timer in the top menu no longer disappears when the server reaches midnight before the user does. This keeps ongoing time tracking visible and accurate for employees working in time zones behind the server timezone.
Original PR description
**Problem:** The running timesheet timer in the systray stops when the Odoo server clock passes UTC midnight, even though it is still the same day in the user's own timezone. **Steps to reproduce:**…
**Problem:** The running timesheet timer in the systray stops when the Odoo server clock passes UTC midnight, even though it is still the same day in the user's own timezone. **Steps to reproduce:** 1. Set the user's timezone to one behind UTC (e.g. America/Guadeloupe, UTC-4). 2. Start a timesheet timer while it is before local midnight but after the server has passed UTC midnight (e.g. 20:00 local = 00:00 UTC). 3. Look at the running timer in the systray. **Current behavior:** At server (UTC) midnight the running timer disappears and its ongoing count is lost. **Expected behavior:** The timer keeps running until the user's own local midnight, regardless of the server timezone. **Cause of the issue:** The systray controller derives its reference day from `date.today()`, which returns the server's (UTC) local date. The running timer's timesheet is created dated the user's local day (`hr_timesheet` uses `fields.Date.context_today`). Once the server crosses UTC midnight, `date.today()` advances to the next day while the user's local day has not, so `timesheet_systray_user_data` (searching `date == today`) and `get_timer_start_time` (searching `date` within today's bounds) no longer match the running timesheet, and the systray reports no running timer. **Fix:** Deriving the reference day from `fields.Date.context_today` aligns the systray's notion of "today" with the timezone the timesheet was recorded in, so the timer is tied to the user's local day rather than the server's. This keeps recording and retrieval consistent, since the timesheet is already dated with the user-local day on creation. opw-6343442 Forward-Port-Of: odoo/enterprise#125635
Barcode deliveries now use the actual storage location of a scanned serial-numbered item when no source location is scanned. This prevents stock from being deducted from the wrong parent location and avoids incorrect inventory balances.
Original PR description
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial…
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial stored in a sublocation (e.g. WH/Stock/Section 2). 4. Confirm a sale order for it, open the delivery in Barcode, and scan an unreserved serial. Issue --- Scanning the unreserved serial creates a new move line that falls back to _defaultLocation() because the decoded scan carries no source location (the operation type does not require scanning one) and never carries the serial's quant location. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_model.js#L937-L944 For a delivery, that default resolves to the picking's own source location (the parent WH/Stock), so the line is sourced from the parent instead of the sublocation where the serial physically sits. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_picking_model.js#L1542-L1544 On validation the unit is deducted from the parent location instead of the sublocation, leaving a stale quant of the serial in the sublocation and a negative quant at the parent. opw-5864414 Forward-Port-Of: odoo/enterprise#128523 Forward-Port-Of: odoo/enterprise#121375