Friday, August 21, 2026
42 changes · saas-19.2
New functionality added to Odoo
Adds support for Sri Lanka-specific tax invoice numbering and document wording so compliant invoices can be issued directly in Odoo. The change also detects VAT-registered companies and customers, updates invoice PDFs when tax invoice conditions are met, and supports resequencing using the required local format.
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
Enhancements to existing features
Manual data reloads in Point of Sale now clear all related browser-stored data, not just the main local database. This helps prevent outdated or conflicting information from remaining after a reload, reducing the chance of inconsistent sales session behavior.
Original PR description
Manual data reloads reset IndexDB but leave local and session storage intact. The goal is to clear them to avoid inconsistent data. task-6456447 Forward-Port-Of: odoo/odoo#281456
Resolved issues and error corrections
This fix prevents an error when creating accrued expense entries for purchase orders whose quantity was changed to zero after receipt. Users can now complete the accrual process without an RPC crash, improving reliability for inventory and accounting workflows.
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…
The Point of Sale product configuration window now shows each product's internal reference again. Searching by a variant reference also opens the correct variant, helping staff identify and sell the right item more reliably.
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
Bank statement lines can now be matched to invoices even when payment references contain formatting differences such as slashes. This reduces missed automatic reconciliations and helps accounting teams process payments more efficiently.
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
Adds a dedicated view for French PDP e-reporting moves so users can see relevant e-reporting details more easily. This keeps the standard accounting entry view unchanged while making compliance-related information clearer.
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
Australian payroll can now include post-tax deductions on payslips that reduce net pay without being reported through Single Touch Payroll. The update also supports separate accounting treatment for different deductions and refreshes year-to-date opening balances when new salary rules are added.
Original PR description
This commit allows post tax deductions to be added to the payslip without impacting STP. This also allows automatically creating opening balances entries for new salary rules. task - 6236042 Forward-Port-Of: odoo/enterprise#119952
The Amazon sales connector now uses Amazon’s newer Orders API ahead of the old version’s retirement in 2027. This keeps Amazon order imports reliable and should improve sync efficiency by receiving order and item details together instead of through repeated follow-up requests.
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
Users can again access convenient menu actions to share or export dynamic document folder views into spreadsheets and knowledge articles. The change also improves embedded folder sharing while protecting private access tokens from being exposed through Knowledge search state.
Original PR description
Also impacted: test_documents_full It is convenient to export a dynamic view of a folder in both spreadsheet and knowledge links settings. * Care is taken to avoid leaking access folders tokens through the search panel/model's state in knowledge. * We also enable sharing folders shared via link through embedded views as it enables benefitting from the power of them vs. adding the link to the folder in the article. * As with other actions initiated on shortcuts, the "real" operation is done on the target. Sharing the target is simpler than patching a folder "child_of" to return the target children (shortcut as documents_unique_folder_id is not supported). Task-5180137 Forward-Port-Of: odoo/enterprise#128481 Forward-Port-Of: odoo/enterprise#122481
Bank reconciliation can now match payments even when invoice payment references contain separators such as slashes that are missing from bank statement labels. This helps reduce manual reconciliation work and improves matching accuracy for common payment reference formats.
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
### 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
The Italian localization now places VAT carryover amounts in the correct monthly VAT report category. This prevents debit carryovers from being reported as credit carryovers, improving the accuracy of Italian tax reporting.
Original PR description
With a l10n_it company: - Create an invoice for december, create the tax return closing entry for this period. On the monthly VAT Report of january the carryover is declared in section VP9 which is supposed to store credit carry over not debit. opw-6354509 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279471
Customers using restaurant self-ordering with a single online payment provider no longer risk losing their order details when they go back from the payment page. The order information is saved before leaving the page, keeping customer details and pickup time intact.
Original PR description
**Setup** * Increase the debounce time of `debouncedSynchronizeLocalDataInIndexedDB` to **5 seconds** to reproduce the issue deterministically. * Configure a restaurant with **Self Ordering** enabled…
**Setup** * Increase the debounce time of `debouncedSynchronizeLocalDataInIndexedDB` to **5 seconds** to reproduce the issue deterministically. * Configure a restaurant with **Self Ordering** enabled (`QR Menu + Ordering`). * Configure **Mollie** as the **only** online payment method. **Reproduction** 1. Place a **takeout** order through the mobile menu. 2. Select a pickup time, enter the required customer information (including a mobile number), and proceed to the payment page. 3. Verify from the backend that the draft order contains the expected data (customer/partner and `preset_time`). 4. Press the browser **Back** button to return from the payment page. 5. Check the draft order in the backend again. [video](https://drive.google.com/file/d/1kNWpYuo79mYMV3eMelwJ5IDFeWUc7zsD/view) **Observed result** * The draft order loses its previously synced information. In particular, the **partner/customer** data (and other synced fields such as `preset_time`) are removed. **Expected result** * Returning from the payment page should not modify the draft order. All previously synced data should remain intact. **Cause** - When there's only a single payment method, it's [auto-selected](https://github.com/odoo/odoo/blob/161715c850496d3683baa5d1600380470d0b5ff5/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js#L21-L22) and `checkAndOpenPaymentPage` immediately opens the payment page via[ window.open()](https://github.com/odoo/odoo/blob/161715c850496d3683baa5d1600380470d0b5ff5/addons/pos_online_payment_self_order/static/src/app/pages/payment_page/payment_page.js#L35). - The order's local data is saved to IndexedDB on a 300ms debounce. If the redirect fires before that debounce completes, the save is cancelled, leaving IndexedDB out of sync with the in-memory order **Fix** - Before opening the payment URL, explicitly flush the order to IndexedDB using the `synchronizeLocalDataInIndexedDB`, ensuring the local data is persisted before the page navigates away. opw-6231478 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272724
German invoice letters sent by post now place the recipient address correctly in the required DIN5008 window. This prevents postal delivery failures with Pingen while keeping the normal report layout unchanged for non-postal documents.
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 invoices now keep their original delivery date even if later deliveries are added or validated with earlier dates. This prevents already-finalized invoice information from changing silently and improves reliability for billing records.
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
Deleting a draft invoice for timesheet-based services now makes the hours available to invoice again without changing which sales order line they belong to. This prevents delivered hours from disappearing from the original order or being incorrectly moved to another order, keeping sales and billing totals accurate.
Original PR description
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the…
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear from the original order line. Protect so_line during the write and drop the pending recompute: deleting an invoice must only make the hours invoiceable again, not change their allocation. Steps to reproduce: - Install Sales and Timesheets - Create a service product with invoice policy "Based on Timesheets" and "Create a task in a new project" - Create and confirm a sale order with this product - Log a timesheet on the generated task - Create the invoice (keep it in draft) - Remove the Sales Order Item from the task and from the project settings (or point them to a sale order item of another order) - Delete the draft invoice - Open the timesheet: its Sales Order Item is emptied (or replaced by the other order's item, whose delivered quantity now includes the hours sold on the original order), and the original line's delivered quantity is reset --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280154 Forward-Port-Of: odoo/odoo#279552
Sales orders that require a customer signature are now checked correctly when payment is made. This helps prevent orders from being completed without the required approval step, improving compliance with sales terms.
Original PR description
See also: - https://github.com/odoo/enterprise/pull/127041 Forward-Port-Of: odoo/odoo#280403
Odoo now shows the specific error code and message 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 connection or 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#281490Self-service point-of-sale orders with a total of zero now bypass the payment screen. This removes an unnecessary checkout step and makes the ordering flow smoother for customers when no payment is due.
Original PR description
Before this commit: -------- - Self-orders with a total amount of zero are still redirected to the payment page, which was unnecessary. After this commit: -------- - The payment step is now skipped for zero-amount self-orders, providing a smoother checkout flow. task-5106938 Forward-Port-Of: odoo/odoo#282841 Forward-Port-Of: odoo/odoo#230218
Visitors who open a public channel page now remain in that channel instead of being redirected away before messages load. This ensures non-members can reliably view and use public channel pages as intended.
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
Mobile users can now enter times in the date/time picker without the bottom sheet unexpectedly closing or shifting focus behind it. This makes scheduling and planning entries easier and prevents partially entered times from being saved or interrupted.
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
This fix prevents electronic invoice imports from failing when an invoice line has a 100% discount together with tax included in the price. Odoo now recalculates the tax from the original unit price in that edge case, allowing affected supplier invoices to be imported correctly.
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
Imported UBL invoices with document-level charges or discounts now correctly match those amounts with the related tax subtotal. This prevents incorrect tax adjustments during invoice import, improving accuracy for electronic invoicing.
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
Manual replenishment now correctly shows users a notification when it creates a purchase order. This helps purchasing and inventory teams immediately confirm that their replenishment action succeeded, while preserving the intended grouping of purchase order lines.
Original PR description
Currently when the user does manual replenishment no notification is displayed. ## Steps to produce: - Install Inventory and Purchase - Create a product `Chocolate Icecream` and Enable `Track…
Currently when the user does manual replenishment no notification is displayed. ## Steps to produce: - Install Inventory and Purchase - Create a product `Chocolate Icecream` and Enable `Track Inventory` - Purchase > Add a Vendor `Ice cream man` - Reordering rules > Create a new reordering rule and save: - Trigger: Manual - Min: 5 - Max:10 - Press the `Order` button ## Observed Behavior: No notification is displayed about the newly created purchase order. ## Root cause: When the Order button is pressed, the `action_replenish` method is called. This method invokes `_procure_orderpoint_confirm` at [1]. The `_procure_orderpoint_confirm` function is responsible for creating procurements from orderpoints. During this process, it retrieves the procurement values using `_prepare_procurement_values` that are later used at [2]. However, `_prepare_procurement_values` only includes the orderpoint in the procurement values when the orderpoint's trigger is set to automatic, and not when it is manual, as shown at [3]. These procurement values are then used by `_run_buy` to create a purchase order and purchase order line at [4]. Since the orderpoint is not linked to the purchase order line in this case, no matching order is found at [5], which leads to the reported issue. **Which commit caused this unintentional behavior?** This behavior was unintentionally introduced by this [commit](https://github.com/odoo/odoo/commit/2a0d2c64d0027f540101447289b4c1a10cb3ecdf) . That commit fixed an issue where purchase order lines were not being merged for temporary manual orderpoints that are created dynamically based on product demand. [1]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L342-L349 [2]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L737-L741 [3]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L687-L701 [4]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/purchase_stock/models/stock_rule.py#L156-L165 [5]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/purchase_stock/models/stock.py#L276-L296 [6]- https://github.com/odoo/odoo/blob/c076281dedeed2e25844c43c49ec17511434e3fc/addons/stock/models/stock_orderpoint.py#L365 [7]- https://github.com/odoo/odoo/blob/6a84d3e519892be333552e2e0ebf8da87e0a760c/addons/purchase_stock/models/purchase_order_line.py#L380-L384 ## Solution: Instead of removing the orderpoint ID from the procurement values, we reuse the same conditions used to identify temporary orderpoints for cleanup at [6]. Based on this, we determine how purchase order lines should be merged in the `_run_buy` method. With the previous implementation, no orderpoint was included in the procurement values. As a result, the condition at [7] checking for orderpoints always evaluated to True, causing the system to identify an existing purchase order line for the same product as a merge candidate. This solution allows us to retain that fix as well as avoid the error of notifications not showing up. opw-6311520 Forward-Port-Of: odoo/odoo#271993
Company-paid expenses created from a project are now excluded from project revenue calculations when they are only payment settlement lines. This keeps project margin reports accurate by preventing costs from being incorrectly shown as additional revenue.
Original PR description
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and…
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and post it. 3. Open the project's Actual Margins: the expense shows under Other Revenues as a positive amount. Issue --- Creating an expense from a project's Expenses view keeps `project_id` in the context until its journal entry is built. For a company-paid expense that entry is a payment, and `AccountMoveLine._compute_analytic_distribution` puts the project's analytic distribution on every line that is not receivable or payable, which also covers the Outstanding Payments liquidity line. An analytic amount is the opposite of the move line balance, so the negative liquidity balance becomes a positive analytic line, classified as Other Revenues, on top of the real cost already booked on the expense account. The receivable/payable filter from b6200026ecf1 narrowed the override introduced in ac1995ad6ddf but ignored the liquidity counterpart of a payment; since only profit and loss lines make up a project margin, restricting the distribution to `income` and `expense` accounts keeps the settlement line out of the report. https://github.com/odoo/odoo/blob/f037dead17eb6a74d1ea9c56b0861b285be17516/addons/sale_project/models/account_move_line.py#L10-L19 opw-6326551
Point-of-sale bills printed before an order is finalized no longer show the self-service invoicing QR code. This prevents customers from creating invoices too early and avoids related order and payment inconsistencies.
Original PR description
Step to reproduce: - install point_of_sale - have a pos, with `Early Receipt Printing` and `Self-service invoicing` enabled - open a pos ,select a product - from action button, click on "Bill" Observation: - We can see QR code in bill, using which a person can invoice itself, even when order is in draft state. - This cause a lot of anomoly like payment line not visible in pos order, even after successful payment Cause: - Prior to this version, `Qr` related data is shown only when `order.finalized` i.e. `status = draft` . https://github.com/odoo/odoo/blob/6f64942cbbbf2355f7328394a6d484f6828a80f1/addons/point_of_sale/static/src/app/components/receipt/order_receipt.xml#L76 - After commit https://github.com/odoo/odoo/commit/aeaca097ae39b293bff47458ae8af019585f9224 we removed this condition Fix: - The condition is brought back. opw-6427152 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Odoo now chooses the correct company or contact when several partners use the same email address but have different formatted sender names. This prevents emails and chatter messages from showing the wrong sender, especially in multi-company invoice workflows using email templates.
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#269509Fixes an issue where editing a chatter message could break or move contact mentions when one contact's name or ID was contained inside another's. This keeps message mentions reliable and prevents confusing broken links in conversations.
Original PR description
# Introduction This PR fixes broken mention links linked to the fact that we replace strings without paying attention to the fact that some strings may contain others that we want to replace later.…
# Introduction
This PR fixes broken mention links linked to the fact that we replace strings
without paying attention to the fact that some strings may contain others
that we want to replace later. This affects both id's and names of records.
See commit messages for more details.
# How to reproduce
- Create Contact A and then Contact B and either :
- Contact B's id need to contain Contact A's id (e.g. Contact B id = 12; Contact A id = 1)
- Contact B's name need to contain Contact A's name (e.g. Contact B name = ABC; Contact A name = AB)
- In a chatter create a message mentionning first Contact B and then Contact A
> Depending on the version, you might need to reload the page here
- Edit the message and save
# The issue
We see a broken mention in the chatter
# Cause
When saving an edited message, we give the raw body of the message (without the mention links) and the mentionend partners to `generateMentionsLinks` : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L152
This method's purpose is to replace the text links ("@Contact A") with actual html links. It does so by enumerating each partner given as an argument and replace the text mention with a placeholder :
https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L158
It will then replace the placeholders with actual links : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L208-L218
The issue is that in both of those steps, we can try to replace a string that is contained
in another string we want to replace.
For exemple :
"string123 some text string12"
If we try to replace "string12" first, then we will select the wrong string :
"[string12]3 some text string12".
opw-6313748
Forward-Port-Of: odoo/odoo#282354
Forward-Port-Of: odoo/odoo#272549This fixes an attendance calculation issue where a shift ending exactly at midnight could be counted as part of the next day. The change helps ensure overtime is recalculated accurately and prevents previous-day overtime from being overwritten with incorrect values.
Original PR description
When recomputing overtime, attendances overlapping the affected day are retrieved based on their check-in and check-out. An attendance whose check-out is exactly at the start of the following day is…
When recomputing overtime, attendances overlapping the affected day are retrieved based on their check-in and check-out.
An attendance whose check-out is exactly at the start of the following day is currently considered to overlap that day because the domain uses an inclusive lower bound on `check_out`.
This can cause overtime from the previous day to be recomputed using an incomplete set of attendances.
### Steps to reproduce:
* Configure an employee with a daily quantity overtime rule based on the expected hours from the contract.
* On the first day, create multiple attendances, with the last one ending exactly at midnight.
* Ensure the total worked hours on that day result in overtime.
* On the following day, create another attendance.
* Observe that recomputing the second day's overtime also retrieves the attendance ending at midnight.
* The previous day's overtime is then recomputed without the other attendances from that day, resulting in an incorrect overtime value.
* Regenerating the overtime ruleset restores the correct value.
For example, with 8.4 expected hours:
```
Day 1:
09:30 - 11:30
14:30 - 18:19
21:00 - 00:00
Day 2:
create/update an attendance
```
The `21:00 - 00:00` attendance is incorrectly included in Day 2's recomputation because its check-out equals the start of Day 2. The other Day 1 attendances are not included, so Day 1 is recomputed from only 3 hours of work.
To fix the issue we treat `check_out` as an exclusive interval boundary when determining overlap. An attendance ending exactly at the start of a day does not overlap that day, while attendances actually crossing midnight continue to be included.
opw-5474120
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#283752
Forward-Port-Of: odoo/odoo#283226This fix prevents barcode scanner input on the Point of Sale ticket screen from being mistaken for refund quantities. It helps avoid incorrect refund amounts and unnecessary error messages when staff scan products while processing returns.
Original PR description
Steps to reproduce: - Have a paid order with a product ordered once - Open the ticket screen, select that order and its line - Scan a product barcode with a keyboard-wedge scanner Issue: "Maximum…
Steps to reproduce: - Have a paid order with a product ordered once - Open the ticket screen, select that order and its line - Scan a product barcode with a keyboard-wedge scanner Issue: "Maximum Exceeded - The requested quantity to be refunded is higher than the ordered quantity. 6 is requested while only 1 can be refunded." When the line holds enough quantity no dialog is shown at all and a refund quantity taken from the barcode is silently set. Cause: A keyboard-wedge scanner types the barcode as a burst of keystrokes. The number buffer discards such bursts by waiting barcodeService.maxTimeBetweenKeysInMs before handling the keys it collected and dropping any batch of more than two, but only when its holder asks for it with `useWithBarcode`. TicketScreen never set the flag, so its buffer handled every keystroke on its own and the digits of the barcode reached _setToRefundDetail as the refund quantity. ProductScreen, OrderSummary and PaymentScreen all set it. Fix: Set `useWithBarcode: true` on the ticket screen number buffer. Since the keys are now handled with a delay, capture the buffer before the selected order or orderline changes, so that a keystroke is applied to the line that was selected when it was typed and not to the next one. opw-6465148 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281962
Opening the timesheet systray from a task will now prefill the project and task without resetting any running timer. This prevents users from losing tracked time when switching context to record or review their timesheet.
Original PR description
Before this commit, the prefill used when the user opens the timesheet systray when his current loaded page is a task, will alter the form view in the timesheet systray to set the project and task when those fields are unset. The problem is `unit_amount` is also given to 0 and so the timer is reset due to the prefill system. This commit makes sure the `unit_amount` field is not inside the prefill data to make sure the timer is no longer reset. opw-6481443
Fixed an issue where the General Ledger initial balance could show the wrong foreign currency amount or an empty currency inconsistently when companies with different currencies shared the same chart of accounts. This prevents misleading balances in multi-company accounting reports and keeps currency display consistent.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared…
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared account and: * Add both companies in the **Company** field. * Under the **Mappings** tab, configure a mapping for each company. * In each company, create and post a journal entry on the same shared account (for example, a receivable account) using the company's own currency. * Set the journal entry dates to the **current month**. * Open **Accounting → Reporting → General Ledger**. * Change the reporting period to the **following month** so the posted entries are shown as the **Initial Balance**. * Open the report separately for each company. **Observed behavior:** * From the **CAD company**, the Initial Balance displays **USD 2,000** instead of the expected **USD 1,000**. * From the **USD company**, the **Currency** column on the Initial Balance is **blank**. **Cause:** * The SQL query for the `id_with_accumulated_balance` groupby used `SUM(amount_currency)` and `MIN(currency_id)` to aggregate all pre-period lines into a single Initial Balance row. * In a multi-company shared Chart of Accounts, lines from different companies (each with their own currency) were collapsed into the same group, causing `SUM(amount_currency)` to add amounts across currencies and `MIN(currency_id)` to return an arbitrary currency ID. * Additionally, the Python accumulation loop incorrectly performed **integer addition** on `currency_id` (a foreign key), further corrupting the displayed currency. **Fix:** * Replace `SUM(amount_currency)` and `MIN(currency_id)` with `CASE` expressions `MIN = MAX` is a uniformity check that works for **any number of currencies**: if every row in the group shares the same currency the condition is true and the correct sum is returned; if even one row differs the condition is false and both fields return `NULL`. The original three-column `GROUP BY (id, date, account_id)` is preserved. * The Initial Balance row now correctly shows a **blank** currency column, consistent with the Odoo 18 behavior, instead of an incorrect aggregated foreign currency amount. opw-6375310 Forward-Port-Of: odoo/enterprise#124823
This fixes an error that could stop DHL return labels from being created when a sales order included incoterms. The return shipment request now sends the expected incoterm code, preventing the failure and allowing delivery confirmation to proceed.
Original PR description
Issue ----- When "return" is enabled, users get a traceback if the SO has incoterms. Steps to reproduce ----- - Set up DHL - enable return labels - Create a SO with incoterms & confirm it - Confirm the delivery > Traceback Cause ----- The request sent for the return label contains the incoterm record instead of its' code like in `dhl_rest_send_shipping` https://github.com/odoo/enterprise/blob/f6c94d4ca3ef4211a5ab00bf0b39f6a7675c8f79/delivery_dhl_rest/models/delivery_dhl.py#L371-L372 Which is not JSON serializable ----- Ticket: opw-6430371 Forward-Port-Of: odoo/enterprise#126459
Fixes an issue that caused an error when users reset bank statement lines to draft. This keeps accounting workflows from being interrupted when correcting or reopening bank transactions.
Original PR description
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on…
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on bank journal. - Open `Transactions`. - Create a new `statement line`. - Select the `statement line`, click the `gear action`, and click `Reset to Draft`. `AttributeError: 'bool' object has no attribute 'setdefault'` After the [recent commit], when resetting the statement line to draft, the server action run [1] and the linked move is going reset to draft, and the method returns the result [2]. After the mentioned commit, the method returns True [3]. When the result from [4] is passed to clean_action, it raises an error [5]. This commit ensures that it returns None after resetting the statement line linked to the invoice to draft, as it previously returned None and same as like [6]. [recent commit]: https://github.com/odoo/odoo/commit/712718d9df0fd5044ac57fdd9ec58e64bece36c0 [1]- https://github.com/odoo/enterprise/blob/7ca28a1c079d22b60e3756ca9b4f404771f214e8/account_accountant/views/bank_rec_widget_views.xml#L541-L551 [2]- https://github.com/odoo/enterprise/blob/eae51e3ca155ca29e1de54f4bd223e7540b2aa7f/account_accountant/models/account_bank_statement.py#L112-L114 [3]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/account/models/account_move.py#L6236-L6251 [4]: https://github.com/odoo/odoo/blob/a6f99706c6a62fc65666a0ff5e58fa465b41a6fb/addons/web/controllers/action.py#L53-L59 [5]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/web/controllers/utils.py#L24 [6]: https://github.com/odoo/odoo/blob/d7df2e8acff9eb7066993fa6a0b0c6d7c85baabc/addons/account/models/account_payment.py#L1206-L1208 sentry-7354160052 Forward-Port-Of: odoo/enterprise#127189
When an incoming VoIP call rings until it reaches voicemail, Odoo now marks it as missed instead of leaving it in a calling state. Users also see a clear “Call missed” message, reducing confusion in call history and softphone notifications.
Original PR description
Steps to reproduce: - Call your softphone with your smartphone. - Let the smartphone ring forever. => At some point, you reach the voicemail and the softphone stops ringing but: - The softphone…
Steps to reproduce: - Call your softphone with your smartphone. - Let the smartphone ring forever. => At some point, you reach the voicemail and the softphone stops ringing but: - The softphone displays a weird message with emojis. - The call record stays "Trying to call" (and might *display* "Ended unexpectedly" in 19.2). This commit fixes those two issues by showing a proper "Call missed" message and switching the call record status to "Missed". This is done in 19.2 and not before... because the behavior before is even more problematic: - 19.1: the softphone keeps ringing and crash if you try to answer, that was mostly fixed thanks to [1] and this commit takes profit of those big ameliorations to fix the issue here. - 19.0: you do not even reach voicemail (it stops without saying anything). This was apparently fixed as a side-effect of [2], which we do not consider worth even partially backporting as not critical. [1]: https://github.com/odoo/enterprise/commit/942f32316ab02d8c739fe7fdd5ec2bdde472a68e [2]: https://github.com/odoo/enterprise/commit/d24d7f3406ca47e7ac529d69957b0ad481d553bf task-6453500
This fix prevents Australian payroll batch processing from failing when some employees have leave allocations and others do not. Payslips without relevant leave now default to zero unused leave totals, allowing mixed batches to complete reliably.
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#127623Fixed an issue that prevented users from printing invoice PDFs for Colombian vendor bills after the electronic invoicing acceptance flow. The system now correctly reads the attached invoice data, avoiding a server error and allowing the PDF to be generated.
Original PR description
**Steps to reproduce:** * Install the **l10n_co_dian** module. * Go to **Settings** and, under **Colombian Electronic Invoicing**: * Disable **Testing Mode**. * Enable **DIAN Demo**. * Create a…
**Steps to reproduce:**
* Install the **l10n_co_dian** module.
* Go to **Settings** and, under **Colombian Electronic Invoicing**:
* Disable **Testing Mode**.
* Enable **DIAN Demo**.
* Create a vendor bill with a tax and confirm it.
* Click **Acknowledge Reception**.
* Click **Receive Goods**.
* Click **Accept**.
* From the gear menu, click **Print → Invoice PDF**.
**Observed behavior:**
* A server error is raised:
```
lxml.etree.XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1
```
* The PDF cannot be generated.
**Cause (two-step):**
1. **ZIP not unwrapped:** The original code called `etree.fromstring(self.l10n_co_dian_attachment_id.raw)` directly for all move types. For vendor bills (`in_invoice`) the attachment is stored as a ZIP file, so `raw` is compressed binary data — not XML. Passing it to `etree.fromstring` directly produces the `XMLSyntaxError` above.
2. **AttachedDocument wrapper not unwrapped:** Once the ZIP is correctly decompressed with `xml_utils._unzip`, the resulting XML is an `AttachedDocument` wrapper, not a plain `Invoice`. The actual invoice XML is embedded as CDATA inside `cac:Attachment/cac:ExternalReference/cbc:Description`. `_get_qr_code_value` expects the inner document and searches for nodes like `cac:AccountingSupplierParty`, `cac:LegalMonetaryTotal`, and `sts:QRCode` — none of which exist on the outer wrapper, so the QR code was blank or the method crashed.
**Fix:**
* In `_l10n_co_dian_get_invoice_report_qr_code_value`, for vendor bills (`in_invoice`/`in_refund` without support document), unzip the attachment and immediately attempt to extract the inner invoice XML from `cbc:Description` using `findtext('.//{*}Description')` (lxml namespace wildcard). If the node is present, parse its text as the actual document; otherwise fall back to the unzipped bytes directly.
**Note:**
* A unit test for the `AttachedDocument` unwrapping path was not added because the test would require a zipped fixture file (the vendor bill attachment is stored as a ZIP) which is not appropriate to commit.
* A regression test was added in `test_accept_by_customer`: after the full commercial event flow the method is called inside a `try/except etree.XMLSyntaxError` block so that any XML parse failure surfaces as a proper test *failure* rather than an unhandled test *error*.
opw-6417422
Forward-Port-Of: odoo/enterprise#126463This fix prevents Odoo Studio from crashing when users edit fields that are added or duplicated dynamically in accounting-related views. Instead of showing an error, Studio now falls back gracefully and hides fields that cannot be safely customized, improving reliability for affected workflows.
Original PR description
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements"…
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements" and edit "Invisible" on the second partner_id field - Traceback `normalize()` compares the combined arch without the studio customization to the one with it, in order to compute the smallest possible set of xpaths. To do so, it calls `apply_inheritance_specs` (the low-level function from `odoo.tools.template_inheritance`) directly on the statically combined arch. Some models add or duplicate nodes dynamically in `_get_view()` (Python postprocessing, run after the static view combination). A studio operation can target such a node, since it is what the user actually sees and clicks on. But that node has no counterpart in the purely static combined arch used by `normalize()`, so `apply_inheritance_specs` raises a ValueError. `edit_view()` only catches `ValidationError` to fall back to an un-optimized (but valid) studio arch instead of failing the request. Since the low-level function raises a plain `ValueError` here, that fallback never triggers, and the exception is not caught anywhere. To fix this, we will keep the behavior from version 18.0 and catch the ValueError raised by `apply_inheritance_specs` in `normalize_with_keyed_tree` and re-raise it as a ValidationError, like `ir.ui.view.apply_inheritance_specs` already does elsewhere. This lets `edit_view()`'s existing fallback handle the case gracefully instead of crashing. Additionally, the two models responsible for the dynamically-added nodes described above are fixed at the source. `account_invoice_extract`'s duplicated `partner_id` field and `l10n_nl_reports`'s injected `company_id` field are now marked with `data-used-by`, the same attribute `_add_missing_fields` already sets in `ir_ui_view.py` for the fields it adds. Studio already skip rendering and computing xpaths for any node carrying this attribute (since https://github.com/odoo/enterprise/pull/92862), so these nodes are no longer exposed to the user and can no longer produce a studio operation that `normalize()` is unable to locate. opw-6332911 Forward-Port-Of: odoo/enterprise#122829
Regular accounting users can now export Datev attachment ZIP files even when an invoice's main image attachment was added through a log note by someone else. This prevents an access error from blocking German reporting exports and helps teams complete required accounting file exports reliably.
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
German SEPA Credit Transfer files now omit a company LEI when using older bank file formats that do not allow it. This keeps exported payment files compliant 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
Barcode deliveries now use the real storage location of a scanned serial number, even when the source location is not scanned. This prevents inventory from being deducted from the wrong parent location and avoids inaccurate stock 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#121375
Fixed an issue that could block internal transfer reconciliation between a parent company and a branch when the branch transaction was processed through a reconciliation model. This prevents unnecessary company inconsistency errors and allows the expected accounting workflow to complete 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
Auto planning sales-related planning work now correctly includes the final day of a selected month. This prevents valid working days from being skipped, helping teams schedule the full amount of planned service time more accurately.
Original PR description
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To…
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To Plan" button, then click "Auto Plan". 4. Make sure the "Month" filter is selected in the scale options and observe the planned slots. Issue: -------- When auto planning slots for a month, the last day of the month is excluded. For example, slots are scheduled only until July 30th, even though July 31st is a working day. Cause: -------- While preparing the context, `stopDate` is set to July 31st at 00:00. It is then passed to [serializeDateTime()](https://github.com/odoo/odoo/blob/dacaad91bba8f959daf5d89a046c5a1c11e48eec/addons/web/static/src/core/l10n/dates.js#L553-L560), which converts the datetime to UTC. Depending on the user's timezone, this can shift the date to the previous day, causing the last day of the month to be excluded. Solution: ------------ Use `localEndOf()` to set `stopDate` to the local end of the selected range before passing it to `serializeDateTime()`. This ensures the last day of the month is preserved during UTC conversion. **NOTE:** Forward-port the solution from the 18.0 version, which was adapted to the publish shift use case in 18.3 and introduced this issue. Add a HOOT test case to prevent this regression in future versions. References: [18](https://github.com/odoo/enterprise/commit/bc3db24f83473d5646f7c2cfca8ed1c5b064ea2e) and [saas-18.3](https://github.com/odoo/enterprise/commit/c81fba31780869940f726b695ad46a87f69798fb) opw-6391495 Forward-Port-Of: odoo/enterprise#127950