Friday, September 18, 2026
44 changes · saas-19.4
Resolved issues and error corrections
This fixes how Odoo determines the flag image shown for a language, allowing custom modules to choose a different flag when needed. It matters for businesses that serve specific regions or want more accurate localization without extra workaround development.
Original PR description
Which flag stands for a language is a presentation choice, not a property of its code. `_compute_field_flag_image_url` derives the URL from the country part of the locale, and a site that wants a…
Which flag stands for a language is a presentation choice, not a property of its code. `_compute_field_flag_image_url` derives the URL from the country part of the locale, and a site that wants a different flag for a language it offers, a regional one or the flag of the country it actually serves rather than the one the code names, has to compute that URL its own way.
That is not possible today. The field passes the compute as the function object:
flag_image_url = fields.Char(compute=_compute_field_flag_image_url)
`determine()` takes the `callable` branch and calls that object with the recordset. A plain function is not bound, so the implementation of `base` runs whatever class the record has: a module that inherits res.lang and defines `_compute_field_flag_image_url` changes nothing, and nothing says so. No error, no warning, and the field keeps answering the same URL. Getting around it means redeclaring the field only to replace its compute, which is more than the situation calls for.
Passing the method name puts the computation back on the MRO and costs nothing else: `Field.get_depends` resolves a string with `resolve_mro` and collects `_depends` from every implementation it finds, so the `@api.depends('code', 'flag_image')` declared here keeps applying, to this implementation and to an override alike.
This is the same kind of fix as #185419, which made the `domain` of a field reachable by an override for the same reason. Two field declarations in the codebase still pass a function object this way; the other is `website.menu.is_mega_menu`, which passes `inverse` the same way and is left alone here.
Forward-Port-Of: odoo/odoo#288686This fixes an issue where some values in quotation header or footer PDFs could disappear when generating a PDF Quote. Businesses using PDF quote templates with structured form fields will now see the expected information preserved in the final document.
Original PR description
Issue: --- When a quotation header/footer PDF has a hierarchical AcroForm field, the value set for that field is silently dropped from the generated PDF Quote when running on pypdf 5.4.0. Steps to…
Issue: --- When a quotation header/footer PDF has a hierarchical AcroForm field, the value set for that field is silently dropped from the generated PDF Quote when running on pypdf 5.4.0. Steps to reproduce: 1- Using a python 3.13 env, install requirements.txt (or otherwise run with pypdf==5.4.0 instead of PyPDF2). 2- Upload a header/footer PDF whose form field is a hierarchical field (`/T`/`/FT`/`/V` on the parent, not on the widget itself). The attachment from the ticket can be used as a sample to reproduce the bug. 3- Create a SO and select that document in the Quote Builder tab. 4- Print -> PDF Quote. 5- The value bound to that field does not appear in the printed PDF. Cause: --- After https://github.com/odoo/odoo/commit/4b02fbd717f62dd5345dad3ffb8d428c1c180007 `_add_pages_to_writer` renames the parent `/Field` object's `/T` when the widget itself has none. PyPDF2's `addPage` inserted the reader's page as is, keeping the widget's `/Parent`. pypdf 5.4.0 deep clones the page instead and ignores `/Parent` at every depth, so the widget loses the link to that field. It ends up with neither `/T` nor `/FT`, hence nothing matches the value mapping. Fix: --- Merge the field into its widget annotations instead: copy the prefixed `/T` and the inheritable keys onto each of them, then drop `/Parent`. The field's own `/T` is left untouched, so a field owning several widgets is filled on all of them. opw-6508728 Forward-Port-Of: odoo/odoo#287910 Forward-Port-Of: odoo/odoo#286186
Fixed an issue where opening Point of Sale orders for a company could show a blank screen if one of its delivery addresses had no name. Orders linked to unnamed delivery contacts now display correctly using the parent company name, helping staff access customer order history without disruption.
Original PR description
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click…
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click "Orders" on the company Issue: The screen goes blank. Cause: The ticket screen is opened with the company name as search term. The server domain searches on `partner_id.complete_name`, so the orders of the address contact are returned too. They are then fuzzy matched on `getPartnerName()`, which returns `false` for a partner without a name, and `fuzzyLookup` calls a string method on it. The error is raised during rendering, so the whole app is destroyed. Fix: Make `getPartnerName()` always return a string, and fall back on the parent name, like the complete name does. This way the orders of the address contact are still listed when searching on the company name, instead of being filtered out by an empty name. opw-6578541 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288880
This fix ensures accounting entries are recalculated when external tax data changes, even if the invoice lines themselves are otherwise unchanged. It prevents mismatches between product lines and tax lines, helping keep tax amounts and totals accurate.
Original PR description
The account_external_tax module can modify extra_tax_data without modifying anything else on the lines. This results in: - a desync between the product lines and tax lines, - incorrect totals This makes sure we regenerate the appropriate tax lines and recompute the totals fields. opw-6547387 Forward-Port-Of: odoo/odoo#288717
This fixes cases where product quantity precision was still using an old setting name, causing the system to default to only two decimal places. It helps keep point-of-sale, stock packaging, and Jordanian POS e-invoicing calculations accurate when products require more precise unit quantities.
Original PR description
…al.precision The `decimal.precision` "Product Unit of Measure" was renamed "Product Unit" in 18.1. Some occurences called the old name still exist in the code. This is silently fallbacking on the default 2 decimals from the `precision_get`. See: https://github.com/odoo/odoo/pull/193490 task-none Forward-Port-Of: odoo/odoo#288964 Forward-Port-Of: odoo/odoo#288508
This fixes a crash that could occur when users discarded changes while editing a mass mailing. The update ensures the email campaign editor recognizes the correct close-and-discard action, improving reliability without changing the user workflow.
Original PR description
Commit [1] introduced usage of a `discardAndClose` props, but declared it as `discardChanges`. This did not cause any issue because `owl3_compatibility_layer` uses `useProps()` which accepts any props without validation. However, after commit [2] in Odoo 20.0, usage of `useProps` in the `MassMailingBuilder` means that `useProps()` is overwritten, and the component will only accept props that are declared in the schema. This commit fixes the invalid props schema. [1]: https://github.com/odoo/odoo/commit/c6fbfb69c2a59041fc360bb4ddf3b09a1eeaf012 [2]: https://github.com/odoo/odoo/commit/5df880f7ba851abf4151fca2f2af50b1c18afb2c task-6584680
This update refreshes the spreadsheet component with fixes that improve chart display, pivot table totals, and printing behavior. Users should see more accurate spreadsheet reports and fewer visual or output issues when working with charts, pivot data, and printed sheets.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/a32d26bb0f [REL] 19.4.13 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/a32d26bb0f [REL] 19.4.13 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/bd46cf2e0e [FIX] Package: saas-19.4 is no longer the latest stable branch [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/e921f97fdd [IMP] demo: debounce xml template build [Task: 6574005](https://www.odoo.com/odoo/2328/tasks/6574005) https://github.com/odoo/o-spreadsheet/commit/282235eaa2 [FIX] demo: fix scorecard demo definition [Task: 6573109](https://www.odoo.com/odoo/2328/tasks/6573109) https://github.com/odoo/o-spreadsheet/commit/f6c4679482 [FIX] Charts: Bubble chart background depends on color scheme [Task: 6448882](https://www.odoo.com/odoo/2328/tasks/6448882) https://github.com/odoo/o-spreadsheet/commit/ea1397ad84 [FIX] chart annotation: text input style [Task: 6533642](https://www.odoo.com/odoo/2328/tasks/6533642) https://github.com/odoo/o-spreadsheet/commit/651ee544aa [FIX] pivot: wrong running total with collapsed/hidden dimensions [Task: 6569607](https://www.odoo.com/odoo/2328/tasks/6569607) https://github.com/odoo/o-spreadsheet/commit/6332d219dd [FIX] pivot: fix scope of `pivot_html_renderer` css [Task: 6523521](https://www.odoo.com/odoo/2328/tasks/6523521) https://github.com/odoo/o-spreadsheet/commit/6e2465286d [FIX] print: empty pages when printing [Task: 6559815](https://www.odoo.com/odoo/2328/tasks/6559815) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
When users duplicate a section, the related product now carries over even if that field is locked. This prevents copied lines from losing product information, reducing manual correction and potential billing mistakes.
Original PR description
**Version: saas-19.4+** **Description of the issue/feature this PR addresses:** Duplicating a section doesn't copy the product when it's readonly. **Current behavior before PR:** Duplicated lines keep qty/description but product is empty. **Desired behavior after PR is merged:** Product is always copied when duplicating a section.
A navigation issue in the API documentation was fixed so opening a related field in a new tab now leads to the intended related model instead of the original model. This makes model exploration more reliable and avoids confusion for users consulting technical documentation.
Original PR description
Steps to reproduce: * open api_doc * select a model (eg: project.task) * middle click on a many2one field (eg: stage_id) Observed behavior: The new tab is opened on the base model (project.task) instead of the comodel (project.task.type). This commit fixes the issue by using `t-att-href` to point to the correct model and `preventDefault` to avoid a full reload when clicking on a m2o. Forward-Port-Of: odoo/odoo#288558
Manufactured products now follow the correct storage rules even after components are unreserved and re-reserved. This prevents finished goods, such as lot-tracked products, from being placed in the wrong warehouse location.
Original PR description
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to…
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to Settings and enable Lots & Serial Numbers and Storage Locations. - Create a product named 'Laptop' and set it to be tracked by Lots. - Create a product named 'Graphics card' with some quantity on hand. - Create a Bill of Materials (BoM) for the Laptop with Graphics card as a component. - Create a location named 'Laptop Shelf' with 'WH/Stock' as its parent location. - Create a putaway rule: - When a product arrives in: WH/Stock - Product: Laptop - Store to: WH/Stock/Laptop Shelf - Create and confirm a Manufacturing Order (MO) for the Laptop. - Unreserve the components, open Details, and re-add the Graphics card. - Click Produce All and check the on-hand quantity list view of the Laptop. ## Issue: The Laptop is stored in WH/Stock instead of WH/Stock/Laptop Shelf. ## Root cause: When the user presses the unreserve button the finished move is passed to `_do_unreserve` https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L2407 which unlinks all the move lines on finished product moves if they are not picked: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L1043 Now when the user presses the Produce All button, `button_mark_done` is called, which calls `_post_inventory` at: https://github.com/odoo/odoo/blob/a51ca77c825d2dba326dd540e2b4c04ef6c2385d/addons/mrp/models/mrp_production.py#L2236 `_post_inventory` assigns `lot_ids` to the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1928-L1930 This calls `_set_lot_ids` which creates a move line with quantity 1 at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L679-L683 After that, `_post_inventory` sets the quantity for the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1935 Which calls `_set_quantity` and the delta quantity is now zero at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L477-L483 Because `_set_lot_ids` has already created a move line that satisfies the move quantity, `_process_increase` is not called. Since `_process_increase` is not called, `_set_quantity_done` is never called at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L463-L465 Since `_set_quantity_done` is not called, `_apply_putaway_strategy` is never called within that function at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L2565 As a result, the laptop ends up in stock instead of on the shelf. This issue did not occur in lower versions because the feature to produce multiple lots was introduced in version 19.0 by the following commit: https://github.com/odoo/odoo/commit/4bb4e08066449177f89382718ceadd840ce90d0e Before this commit, only `_set_quantity` was called. Since no move line was created because lot_ids were not set in `_post_inventory`, `_process_increase` was called, which then called `_apply_putaway_strategy`, so the putaway rules were applied correctly. ## Solution: Apply putaway rules in the post-inventory function after the user manufactures a product, ensuring the items are placed in the correct locations and preventing products from being misplaced even when putaway rules are defined. opw-6517324 Forward-Port-Of: odoo/odoo#288739 Forward-Port-Of: odoo/odoo#287390
The PEPPOL invoice sending option is now disabled when an invoice has already been sent through PEPPOL. This prevents users from accidentally trying to resend an invoice and makes the screen behavior clearer.
Original PR description
If the invoice is already sent through PEPPOL, the checkbox for sending the invoice should then be readonly and unchecked. Currently is correctly unchecked but not readonly. We fix it by copying what French localization (`l10n_fr_pdp`) does already, giving a reason for disabling. <img width="1359" height="948" alt="immagine" src="https://github.com/user-attachments/assets/6f91ab34-8989-487e-8cd9-96dafee7bcb5" /> . Pad: https://pad.odoo.com/p/accountingv20 pad-accountingv20 Forward-Port-Of: odoo/odoo#288898
This fixes an issue where Point of Sale users on mobile could not see the list of search options when looking up orders. Mobile cashiers can now search orders by customer, date, receipt number, invoice number, or reference as intended, reducing friction when finding previous sales.
Original PR description
Steps to reproduce: - Open a PoS session on a small screen (mobile app or mobile browser) - Register at least one order, so the order list is not empty - Go to the Orders screen and type a term in…
Steps to reproduce: - Open a PoS session on a small screen (mobile app or mobile browser) - Register at least one order, so the order list is not empty - Go to the Orders screen and type a term in the search bar Issue: On a desktop the search bar drops down the list of fields to search on (Reference, Receipt Number, Invoice Number, Date, Customer). On a small screen that list never shows up, so the search silently falls back to the first field and there is no way to search by Date or by Customer. Cause: The list is rendered, but painted behind the order list. Under the media-breakpoint-down(sm) block of ticket_screen.scss the order list becomes `position: sticky; z-index: 1`, so a sibling rule raised `.search .fields` to `z-index: 2` to keep the dropdown on top. The `z-1` utility class put on that dropdown in 07f743843830 compiles to `z-index: 1 !important` and overrides the rule. Both elements end up at `z-index: 1` in the same stacking context, and the order list wins the paint order because it comes later in the DOM. Fix: Drop the `z-1` utility and declare `z-index: 2` on `.fields` in the search bar's own stylesheet, which makes the small-screen override in ticket_screen.scss redundant. The stacking of the dropdown now lives in a single place, next to the rest of its styling, so a utility class added to that element cannot silently disable it again. A tour clicks its target element directly and so cannot see a purely visual overlap, which is why the existing MobileTestUi runs of TicketScreen.search() never caught this. The added assertion checks that a suggestion is the topmost element at its own center. opw-6540466 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288671 Forward-Port-Of: odoo/odoo#286983
The manufacturing order Kanban progress bar now shows draft orders with a more visible color. This helps users quickly spot draft manufacturing orders instead of missing them because they blended into the background.
Original PR description
Issue before this commit: ========================= The draft progress segment in the manufacturing order Kanban view is barely visible against the background. <img width="442" height="145"…
Issue before this commit: ========================= The draft progress segment in the manufacturing order Kanban view is barely visible against the background. <img width="442" height="145" alt="image" src="https://github.com/user-attachments/assets/9ba3e4ef-7b91-4c82-86ee-1fe0e47f472f" /> Steps to reproduce: =================== - Install MRP. - Create a draft manufacturing order. - Open the manufacturing orders in the Kanban view. - Observe the progress bar and notice that the segment representing the draft state is barely visible Cause of the issue: =================== In this [commit](https://github.com/odoo/odoo/pull/251475/changes#diff-3bad372563263ef117de6ed9e222008e2a27b2ea212df89f72f269066a8eacacR603) the manufacturing order Kanban progress bar was changed to represent order states. Draft orders were assigned the `light` color, which blends into the Kanban background and makes their segment barely visible. After this commit: ================== The progress segment for the draft state is clearly visible, allowing users to easily distinguish draft orders in the Kanban view. <img width="343" height="123" alt="image" src="https://github.com/user-attachments/assets/6646143f-a299-4544-b8b5-115e13ebf8c5" /> Forward-Port-Of: odoo/odoo#288462
Sales order product selection now shows only the units of measure and packaging options that apply to the chosen product variant. This prevents sales users from accidentally selecting packaging that belongs to a different variant, improving order accuracy.
Original PR description
Issue Before This Commit: ======================== Currently, when a product variant has multiple UoMs, those UoMs appear for all variants when selecting a product in a Sale Order Line, even though…
Issue Before This Commit: ======================== Currently, when a product variant has multiple UoMs, those UoMs appear for all variants when selecting a product in a Sale Order Line, even though they are only available for a specific variant. Steps to Reproduce: ========================= 1: Install the sale and sale_management modules. 2: Enable Units of Measure and Packagings. 3: Create two variants, V1 and V2, and set an Extra Packaging of 'Pack of 6' on V2. 4: Add the product to a Sale Order Line. A product configuration wizard will open. 5: Select V2. The Unit and 'Pack of 6' UoMs are correctly displayed. 6: Select V1. Both UoMs are still displayed, even though 'Pack of 6' is not available for V1. Cause of the issue: ========================= When the variant is changed to V2 in the wizard, _get_basic_product_information is called. At that point, the condition is true, so available_uoms is updated with the UoMs available for V2. However, when the variant is changed back from V2 to V1, the method is called again, but V1 does not have multiple UoMs. Therefore, product_or_template._has_multiple_uoms() returns False, and available_uoms is not updated. As a result, the UoMs from V2 remain in available_uoms and are incorrectly displayed for V1. After This Commit: ======================== Update _get_basic_product_information to ensure that the available UoMs are updated whenever the variant changes. This ensures that only the UoMs available for the selected variant are displayed. Forward-Port-Of: odoo/odoo#288515
This change corrects an internal error message format used when validating exported records. It ensures the system reports the intended validation error instead of an unrelated technical failure, making troubleshooting more reliable.
Original PR description
The format was broken. `'{}:{}' % it` → `TypeError` instead of `AssertionError`.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#288836
Forward-Port-Of: odoo/odoo#288610This fix prevents on-site payment options from being offered in checkout scenarios that involve gift cards or eWallets where they are not supported. It helps avoid customer payment flows that could fail or create incorrect order handling.
Original PR description
We don't want to support gift cards and eWallets in certain conditions opw-6483282 Forward-Port-Of: odoo/odoo#288180 Forward-Port-Of: odoo/odoo#286946
Bank synchronization no longer removes payment methods that users have already configured on a bank journal. This preserves custom incoming and outgoing payment options while still adding any missing defaults, reducing setup loss and manual rework after connecting a bank.
Original PR description
**Steps to reproduce:** - Install Accounting - Configure "Bank" journal: * Add several incoming payments * Add several outgoing payments - From Accounting dashboard, connect Bank (e.g. Odoo Bank Sync Demo) **Issue:** After bank sync, all the incoming/outgoing payments added on the Bank journal are removed. Only the default ones are re-created. **Solution:** Keep all the existing incoming/outgoing payments (i.e. payment method lines) and only create the default ones for the payment method types that don't exist. opw-6424407 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281658
Users can now click, select, or copy the extra informational text shown under a many2one field without accidentally opening the selection dropdown. This removes a small but frustrating interaction issue and makes forms easier to use.
Original PR description
Clicking on the informative extra text of a many2one field would focus the field and open the dropdown, making it inconvenient for the user to simply select or copy the text. This commit prevents the many2one dropdown from opening when the user clicks on the extra lines. Task-6538439 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288019
This fixes an error that could crash Odoo when users checked the processing status of Romanian e-Factura documents. Businesses can now reliably retrieve invoice status from the Romanian SPV portal without interruption.
Original PR description
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error:…
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error: Incorrect padding Cause of the issue: _request_ciusro_download_answer extracts the signature file from the SPV zip answer and re-serializes it with etree.tostring(...), which gives plain XML bytes, not base64 But _l10n_ro_edi_fetch_invoice_sent_documents and the 3 other callers that persist that signature still ran it through base64.b64decode() before wrapping it in BinaryBytes (which already expects raw bytes, not base64). Decoding real XML as base64 only "works" by accident when the XML happens to contain a number of base64-alphabet characters that's a multiple of 4 after the invalid ones get silently stripped - otherwise it blows up with "Incorrect padding". This got introduced by 41fe2ebdb9cc (fields.Binary return BinaryValue), which flipped a base64.b64encode() call to base64.b64decode() at these 4 spots instead of just dropping the base64 call entirely. A previous fix (0750ee145ae2) already fixed the sibling issue on the invoice's own attachment_raw but missed these. Solution: Drop the erroneous base64.b64decode around the signature's attachment_raw at the 4 call sites, and fix the tests that were mocking attachment_raw as base64-encoded instead of raw bytes opw-6562123 Forward-Port-Of: odoo/odoo#288140
Users of the mail plugin can now stay signed in for a configurable period instead of needing to log in every day. The default token duration is seven days, reducing repeated login interruptions while keeping access limited to Outlook authentication endpoints.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#288572 Forward-Port-Of: odoo/odoo#287767
This change adds a test to ensure Avalara tax recalculation stays consistent when only exemption-related tax details are updated. It helps prevent mismatches between invoice product lines and tax lines after an exemption certificate is uploaded and taxes are recalculated.
Original PR description
We removed the explicit tax clearing [1]. It ends up triggering a situation where extra_tax_data on the product lines diverges from the tax lines. This happens when the tax integration writes only extra_tax_data without modifying anything else on the lines (if e.g. you first calculate tax, upload the exemption certificate to Avalara, and then recalculate tax). This tests that exact scenario. opw-6547387 [1] https://github.com/odoo/enterprise/pull/107863 Forward-Port-Of: odoo/enterprise#131815
Merging helpdesk tickets now updates related timesheets so they use the same sales order item as the merged destination ticket. This prevents inconsistent billing or service tracking information after tickets are combined.
Original PR description
Currently when you merge helpdesk tickets with differing sale order items, the sales order item listed on the timesheets of the source ticket is not updated to match the destination ticket's sales order item. This PR ensures uniformity bugfix-6473396 Forward-Port-Of: odoo/enterprise#131155 Forward-Port-Of: odoo/enterprise#129060
Call activities now display and use the properly formatted phone number for VoIP calls instead of falling back to the raw saved value. This improves calling reliability and clarity across contacts and other records, including cases where the phone field has a different name.
Original PR description
Steps to reproduce: - Create a new partner - Set the country address to Belgium - Set as phone number: 0498123456 - Focus-out/save => It gets formatted as +32 498 12 34 56 - Create a call activity =>…
Steps to reproduce: - Create a new partner - Set the country address to Belgium - Set as phone number: 0498123456 - Focus-out/save => It gets formatted as +32 498 12 34 56 - Create a call activity => Bug: the "tel:" link displays 0498123456 instead of using the formatted version. The issue is there since forever, but it got really noticeable since [1] which changed the way we save numbers. Before that, the "phone" field of partners indirectly was saved with the formatted value, meaning the formatted value would be used for call activities "by chance". Of course, it was still a problem for imported values or values forced through ORM code... but that was acceptable. Since [1], the formatted value is saved separately and that is the one that should be used by call activities. Actually, more than formatting and even before [1], it would have made sense to try and use the `phone_sanitized` one coming from the `mail.thread.phone` mixin, as it automatically finds the right "primary" phone field the record uses, which might not be "phone" as call activities assumed. `phone_sanitized` might not have been formatted though so it would not have been perfect anyways. An example of an improvement this fix commit does indirectly: - Install hr_recruitment (and voip) - Go to hr.applicant list - Create a new one - Just set a name and a phone, save - Create a call activity on that new record => There is no phone number displayed with it in the chatter (as the record field is not named "phone" but "partner_phone" and the record is not (yet) linked to a partner record). With this commit, we easily fix/improve 19.4 and future versions, using the new `phone_formatted` field from the `mail.thread.phone` mixin when available, otherwise fallback to any registered phone field of the record (see `_phone_get_number_fields`), not assuming "phone" is the only field to check anymore. This thus allows to ensure to use the right and formatted phone field. JS-side, this commit also ensures to "clean" the formatted version for technical uses (`tel:` and SIP uses), even though it might not really be necessary as most browsers support a fully formatted format. [1]: https://github.com/odoo/odoo/commit/262cb9dc8bde2deb39aa126ee0d8c14c0abc7301 task-6537285
This fix updates the planning field service sales timesheet code to use the renamed Product Unit precision setting. It helps ensure quantities are rounded with the intended accuracy instead of silently falling back to a generic two-decimal default.
Original PR description
The `decimal.precision` "Product Unit of Measure" was renamed "Product Unit" in 18.1. Some occurences called the old name still exist in the code. This is silently fallbacking on the default 2 decimals from the `precision_get`. See: https://github.com/odoo/odoo/pull/193490 task-none Forward-Port-Of: odoo/enterprise#132006 Forward-Port-Of: odoo/enterprise#131709
Customer payments in Mexican localization now use the payment method configured on the selected bank journal instead of always defaulting to electronic transfer. This helps ensure payment complements sent to SAT carry the correct payment method and match the company’s journal setup.
Original PR description
Currently, payment way configured on a bank journal is not taken into account, payments will default to "03 Transferencia electrónica de fondos". Steps to reproduce: - Set the "Payment Way" of a bank journal to "04 Tarjeta de Crédito". - Manually create a customer payment in that journal - Look at the "Payment Way" of the payment, then send the payment complement (REP) to the SAT. Issue: The payment way is "03 Transferencia electrónica de fondos" and the REP carries FormaDePagoP="03" The one set on the journal is ignored. Analysis: We should evaluate the journal before the transferencia default so a payment inherits the payment way, and keep transferencia as the last possible value. opw-6493514 Forward-Port-Of: odoo/enterprise#130863 Forward-Port-Of: odoo/enterprise#130133
Accounting reports now use the desktop-only layout wrapper only where it is needed. This prevents the scroll indicator from being hidden behind long reports on iPhone and iPad, making reports easier to read and navigate on mobile devices.
Original PR description
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its…
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its rendering engine) on all of them, including Chrome and Firefox ### Steps to reproduce (on iPhone): - Install `l10n_es` (contains scrollable reports by default) - Switch to the ES company - Open the Tax Report and try to scroll Before the fix, the scrollbar renders behind the report content ### Cause: `o_content` was added unconditionally in commit https://github.com/odoo/enterprise/commit/d60ea6e0f3245e394fd0cee5f22f52aff2a79e2e But its role differs between desktop and mobile, and its presence on mobile triggers a WebKit compositor bug On desktop, `o_content` is required: `.o_action` stays `overflow: hidden`, so only `.o_content` (`overflow: auto`) can scroll the report Per the CSS flexbox spec, a flex item won't shrink below its content size unless its `overflow` is not `visible` Without `o_content`, the div keeps `overflow: visible`, refuses to shrink, overflows `.o_action`, and the excess is silently clipped — content becomes unreachable, not just visually different On mobile, the framework flips scroll responsibility to `.o_action` (`overflow: auto`) and forces `.o_content` back to `overflow: initial` `o_content` is therefore not needed on mobile On WebKit (iOS/iPadOS), keeping `o_content` on mobile is harmful: it sits as a non-scrolling `position: relative` node between the real scroll ancestor (`.o_action`) and descendants that require special compositing — the sticky `thead` and the fixed-position mobile chatter This configuration causes WebKit's compositor to miscalculate the Root layer bounding box This geometry mismatch is the most likely explanation for why the native scroll indicator renders behind the report instead of on top of it Removing `o_content` on mobile avoids this node entirely and restores correct compositor geometry ### Notes: Tested on Android (Blink) with and without `o_content`: no visual difference and identical compositor layer geometry confirmed via Chrome DevTools — no regression introduced The `padding-bottom` on `.o_account_report_scroll_container` is unrelated — it is applied unconditionally and exists for a separate bug (last row clipped on scroll) opw-6191827 Forward-Port-Of: odoo/enterprise#132034 Forward-Port-Of: odoo/enterprise#128528
Payroll payment reports for India now stop when an employee has no bank account on file. This prevents reports from being generated with missing payment details, helping payroll teams catch setup issues before processing payments.
Original PR description
The payment report check looked at existing Indian bank accounts with an invalid IFSC. An employee withno bank account added nothing to it, so they passed the check and the report was generated with no account for them. task-6580632
This fix ensures the Belgian payroll calculation uses the correct employer contribution code 261 instead of 260. It helps keep payroll contributions accurate for affected Belgian payroll scenarios.
Original PR description
One-line fix to use the correct contribution task-6526911
Fixed an error that appeared when users clicked away from the notes field while creating an appointment booking in Point of Sale. This prevents interruptions during booking entry and helps staff complete appointment workflows smoothly.
Original PR description
Steps: - Open a store with appointments enabled. - Go to the Bookings tab. - Open a new booking form. - Click on the note input, enter some text, and click away. Issue: - A traceback appears when focusing out of the note editor. Fix: - Add `MediaPlugin` to the `htmlField` editor configuration, as the plugin is used by the field when committing local changes via `_commitChanges`. [here](https://github.com/odoo/odoo/blob/saas-19.4/addons/html_editor/static/src/fields/html_field.js#L253) Task-6580877
This fixes cases where spreadsheet version history could be rebuilt from the wrong starting point after a migration, causing corrupted spreadsheet states. When the issue is detected, Odoo now rebuilds history from the correct saved snapshot so users can safely view and restore spreadsheet versions.
Original PR description
…heet history Some spreadsheet can end up in a corrupted history state Original Data ⮕ no revision (XX) ⮕ Snapshot ⮕ Active Revisions and where the original data differ from the snapshot. XX: can…
…heet history Some spreadsheet can end up in a corrupted history state Original Data ⮕ no revision (XX) ⮕ Snapshot ⮕ Active Revisions and where the original data differ from the snapshot. XX: can occur after the script added https://github.com/odoo/upgrade/issues/6340. The migration script is supposed to rewrite the history of the spreadsheet when there are holes in the archived revisions continuity (eg. the history was original Data ⮕ rev **A** ⮕ rev **B** ⮕ rev **C** ⮕ snapshot ⮕ rev **D** and user deleted rev **B** for instance, the script deletes **A** and **C** and marks **D** as the very first revision). Version history was designed with the idea to replay every single revision that existed since the creation of the spreadsheet and apply it to the original data, and in case of missing revisions, in our example, B is missing, we detect the lack of continuity, we start from the snapshot, and replay every single revision since that snapshot. When the migration fixes the continuity, by deleting all the old revisions, and changing their order, we can no longer detect the lack of continuity and end up replay every available revision to the original data ,even though they are based on the snapshot. In that scenario, since the revisions will be applied on the original data but since they are based on the state of the snapshot only, the final state of the spreadsheet will be corrupted since a part of the . If a user then decides to restore the spreadsheet to the version they see in the version history (which is corrupted as mentioned) and the last snapshot is overwritten with the corrupted state, effectively breaking the spreadsheet. With this revision, we add a detection of this corrupted history state, in which case we enforce the history to be replayed from the snapshot and not the original data. Task-6533708 Forward-Port-Of: odoo/enterprise#131803 Forward-Port-Of: odoo/enterprise#130649
Belgian payroll now handles senior dependents needing care through the main senior dependent field, matching the intended future behavior. This prevents confusion in the employee family section and helps payroll calculations use the correct dependent information.
Original PR description
In the 19.2 branch, we changed the UI to make it look like the number of other disabled senior dependents was included in the "other senior dependent" field, but this is not the case. Since the `other_disabled_senior` field will be deleted in the 19.5 branch, instead of just fixing the UI in 19.2, this commit updates the logic to match the 19.5 behavior. We will now handle this using only the remaining field. Task that fixes it in 19.5: https://www.odoo.com/odoo/project/1251/tasks/6133227 Task ID: 6526570 Forward-Port-Of: odoo/enterprise#131424 Forward-Port-Of: odoo/enterprise#130319
Return attachments are now generated only once during the appropriate review or submission step. This prevents duplicate files from being created, reducing confusion and keeping tax return submissions cleaner.
Original PR description
We now call _generate_locking_attachments at review and submit so it was generating two times the attachments. The fix is to always call at submit. And for the review stage we only call it when there is no submit step.
Creating an appointment event from the calendar now respects the time slot the user draws instead of replacing it with the appointment type's default duration. This prevents unintended event lengths and makes scheduling more predictable for staff using appointment calendars.
Original PR description
When creating an event from the calendar view of an appointment type, the duration of the event was set to the duration of the appointment, therefore bypassing the slot we drew. This commit fixes this behavior by giving the priority to the end date of the drawed slot. Task-6412432 Forward-Port-Of: odoo/enterprise#130171
Planning now shows each employee's own allocated hours when a shift is assigned to multiple resources, instead of repeating the total for everyone. Field service planning also calculates break time correctly when resources with different schedules are added or removed, helping keep allocated hours accurate.
Original PR description
# [FIX] planning: split gantt progress bar hours per resource Issue: ---------------------------------------- On the planning gantt view, when a shift is assigned to several resources, the progress…
# [FIX] planning: split gantt progress bar hours per resource Issue: ---------------------------------------- On the planning gantt view, when a shift is assigned to several resources, the progress bar of each individual resource shows the sum of all resources' allocated hours instead of that resource's own hours. Steps to reproduce: ---------------------------------------- - Give two employees working calendars with a different number of daily hours (e.g. 8h and 4h) - Create one shift covering both calendars' full working hours and assign both employees to it - Open the gantt view and check the progress bar of each employee for that shift Cause: ---------------------------------------- `_gantt_progress_bar_group_by_field()` computes one duration per shift via `_get_duration_over_period()`, which already sums the working hours of every resource assigned to the shift. That same total is then added to the progress bar of each resource. Solution: ---------------------------------------- Add `_get_working_hours_over_period_per_resource()` and `_get_duration_over_period_per_resource()`, which returns the results into a dict per resource. `_get_working_hours_over_period()` then calls `_get_working_hours_over_period_per_resource()` and sums the values. `_gantt_progress_bar_group_by_field()` now calls this per-resource breakdown when grouping by `resource_ids`. # [FIX] planning_field_service: fix break_time computation Issue: ---------------------------------------- When adding multiple resources to a slot, we can break the Steps to reproduce first issue: ---------------------------------------- - Create a slot for a resource working 8 hours per day - Add another resource working 4 hours per day - Remove the resource working 4 hours - The allocated hours show "9h 36m" instead of 8h Cause: ---------------------------------------- `_get_in_schedule_break_time()` can return negative values, so it breaks the computation of `allocated_percentage` which then impacts the future allocated percentages. Solution: ---------------------------------------- Add a `max(..., 0)` to `_get_in_schedule_break_time()`. Steps to reproduce second issue: ---------------------------------------- - Create a slot for a resource working 8 hours per day - Add another resource working 4 hours per day - The break time is 0, it should be 4h Cause: ---------------------------------------- The calculation of `break_time` with multiple resources was supposed to be fixed in 583ccb226970787b9dbd4cb03ccbfd43849fb8c7 but the value is never used. Using it fixes the case for multiple resources having the same work schedule but not all case: In our case it will output 12 allocated hours and 2 hours of breaktime instead of 4. This is because the breaktime hours are only from one resources but they still get divided by the number of resources. Solution: ---------------------------------------- We should instead multiply the duration by the number of resources to get the correct number of break hours. This ensures that `allocated_hours + break_time` equals the total duration of the shift. Same in `_get_in_schedule_break_time()`. opw-6500685 Forward-Port-Of: odoo/enterprise#130429
Regular users could hit an access error when an AI chat without its own name was shown in places like Documents. The change lets the system safely display the linked AI agent name for users who already have access to the chat, preventing this interruption.
Original PR description
AI chat channels can have an empty name, in which case their display name is computed from the linked ai_agent_id. That field is restricted with fields. NO_ACCESS, so flows that read the channel display name, such as adding an AI chat attachment to Documents, could raise an access error for regular users. Compute the AI chat display name through sudo() when reading ai_agent_id, since users who can access the AI chat may safely see the agent name. task-6547727
This fix ensures AI-generated pivot reports handle grouped column data in the format the report view expects. It prevents display or loading issues when reports group columns by date intervals or similar categories.
Original PR description
The AI backend returns column groupbys as a list containing both strings and dictionaries with interval information. While this format is used to preserve the interval metadata, the pivot model expects `colGroupBys` to be a flat list of strings. This commit updates the view patch to flatten dictionary entries into their corresponding `<field>:<interval>` strings before assigning them to the pivot model metadata, ensuring the format matches the pivot model's expectations. task-6377810 Forward-Port-Of: odoo/enterprise#129515
The Canadian Balance Sheet report now displays correctly when using the split horizontally option. This ensures assets, equity, and liabilities appear in the intended columns, making the report easier to read and preventing confusion for Canadian accounting users.
Original PR description
The Canadian Balance Sheet "split horizontally" feature was broken during the refactor [1]. This commit - fixes the correct split between the Assets on the right and the Equity and Liabilities on the left, - remove the redundant split=right on child of aforementioned category. [1]: https://github.com/odoo/enterprise/pull/114127 task-none (DSH finding) **before** <img width="1903" height="861" alt="image" src="https://github.com/user-attachments/assets/58ee1ab8-5ca4-4d80-9567-5588d70b3f64" /> **after** <img width="1916" height="881" alt="image" src="https://github.com/user-attachments/assets/894358cd-d18a-4039-a468-65c7cabb20cf" /> Forward-Port-Of: odoo/enterprise#131933
The Ecuador ATS tax export now consolidates data from a company and its branches that share the same RUC. This ensures submitted XML reports match the consolidated tax dashboard and avoids missing branch invoices or totals.
Original PR description
In Ecuador, a company and its branches file under a single RUC, so the ATS is expected to consolidate all of them. However, currently the system exports only the active company report. Steps to…
In Ecuador, a company and its branches file under a single RUC, so the ATS is expected to consolidate all of them. However, currently the system exports only the active company report. Steps to reproduce: - Install the Ecuadorian localization - Create a parent company and a branch, assign the RUC of the parent to the branch, and set the legal name of the branch in Settings - Post a customer invoice with taxes in the parent (e.g. 750) and another one in the branch (e.g. 250) - Activate both companies in the company selector - Go to Accounting > Reporting > Tax return - Select "Report: 104 (EC)", the dashboard shows the consolidated values - Click on the gear icon next to "Tax Return" and select ATS Issue: The exported XML only contains the documents and the totals of the parent company (750). The branch (250) is omitted. Analysis: Currently the ATS export use self.env.company for all searches, which only retrieve the data of the first company set opw-6469807 Forward-Port-Of: odoo/enterprise#128430
This fixes an issue where custom product attribute values entered in a Point of Sale order were lost when the order triggered an inter-company purchase and sale flow. Delivery documents now keep the correct product description, reducing confusion for warehouse teams and customers.
Original PR description
*= sale_purchase_inter_company_rules Step to reproduce: - install `sale_purchase_stock_inter_company_rules` and pos with demo - from setting : - enable Multi-Step Routes - enable all options for…
*= sale_purchase_inter_company_rules
Step to reproduce:
- install `sale_purchase_stock_inter_company_rules` and pos with demo
- from setting :
- enable Multi-Step Routes
- enable all options for Inter-Company Transactions (for both companies)
- go to routes, un-archive MTO route
- create a product "A" with routes "MTO" and "buy"
- make the product available for pos (add into `Misc` category)
- create a attribute with custom attribute value and link it to "A"
- in vendor list, add "My Company (Chicago)" as vendor
- switch to Chicago company, for same product add some other vendor
- switch back to "My Company (San Francisco)"
- for pos, in setting enable "Allow Ship Later"
- open Furniture pos, create a order with Product "A"
- go to payment page and select "Ship Later" and pay
- a RFQ is created for this pos order, open and confirm it
- switch to "My Company (Chicago)"
- open sale order (remove "my quotation" filter)
- notice a quotation created from company 1, confirm it.
- open linked delivery slip
Observation:
- the delivery slip will not have description (custom value for attribute for A
is lost)
Cause:
- when preparing vals for SO for inter-company transaction from
`_prepare_sale_order_line_data` we never considered order from pos
- hence in this case, order lines never had custom attributed linked to them
- the description is computed from the attributes from SO
- SO never had them to begin with
Fix
- we introduced a method `_get_pcavs_from_pos_order` which fetch such attributes from pos order too
- to accomplish this we introduce a new module As there is no dependency with purchase and pos
opw-6213076
Forward-Port-Of: odoo/enterprise#131781
Forward-Port-Of: odoo/enterprise#119233Argentinian export invoices now send item quantities with the correct configured precision, up to the official 6-decimal limit. This prevents valid export invoices from being rejected by ARCA after upgrading to version 19.0.
Original PR description
### Problem `l10n_ar_edi` reports WSFEX / WSBFE line quantities (`Pro_qty`) truncated to 2 decimals in 19.0, no matter what precision the database has configured. `_get_line_details()` reads the…
### Problem
`l10n_ar_edi` reports WSFEX / WSBFE line quantities (`Pro_qty`) truncated to 2 decimals in 19.0, no matter what precision the database has configured.
`_get_line_details()` reads the precision like this:
```python
uom_precision_digits = min(self.env['decimal.precision'].precision_get('Product Unit of Measure'), 2)
```
In 19.0 that `decimal.precision` record was renamed to **`Product Unit`** (`uom/data/uom_data.xml`, `decimal_product_uom`), so the lookup matches no record. `precision_get` does not raise in that case, it falls back to a default of 2 (`base/models/decimal_precision.py`: `return res[0] if res else 2`), so `min(2, 2) = 2`.
The rename was applied everywhere else — `account.move.line.quantity` already declares `digits='Product Unit'`. Only this lookup kept the old name.
### Impact
A quantity of `0.042` is sent as `0.04`. ARCA rejects the invoice with **error 1815** (item math inconsistency: `unit price x quantity - discount` no longer matches the item total), which blocks export invoicing completely.
It only shows up after upgrading to 19.0: on 18.0 the record still had the old name, so the lookup worked and the configured precision was used.
On our hosted fleet we identified **67 Argentinian databases** that use WSFEX or WSBFE and have a unit precision above 2. Eight of them already run 19.0 and are affected today; the rest will hit the same rejection as they upgrade.
### Fix
Use the current record name, and raise the cap to **6**, the maximum `Pro_qty` accepts according to the WSFEX developer manual ([V3.1.1](https://www.afip.gob.ar/ws/documentacion/manuales/WSFEX-Manualparaeldesarrollador_V3.1.1_ARCA.pdf), p. 15).
### Test plan
`l10n_ar_edi/tests/test_fex.py` adds `test_ar_edi_wsfex_pro_qty_decimal_precision`, covering three cases:
| `Product Unit` | quantity | expected `Pro_qty` |
|---|---|---|
| 3 | 0.042 | `0.042` |
| 2 | 0.042 | `0.04` |
| 8 | 0.1234567 | `0.123457` (capped at 6) |
It does not go through the ARCA mock: it calls `_get_rounded_base_and_tax_lines()` and `_get_line_details()` directly.
### Note
Supersedes #130582 by the same author, which carried the same one-line fix without a test. Please review this one instead.
### Left out on purpose
`price_precision_digits` in the same method caps the unit price at 3 digits, which does not come from the specification either. That lookup still uses a record name that exists (`Product Price`), so there is no regression, and we have no rejection reported because of it. Changing it would widen the scope of a fix that has a concrete incident behind it, so it is left untouched here.
Forward-Port-Of: odoo/enterprise#131457One-time purchases of recurring products are no longer treated like active subscriptions in stock forecasts and replenishment planning. This prevents misleading endless future demand, helping businesses plan inventory more accurately.
Original PR description
When a recurring product (with `allow_one_time_sale = True`) is sold as a one-time purchase (no subscription plan), the stock forecast report and replenishment logic incorrectly treat it as an active…
When a recurring product (with `allow_one_time_sale = True`) is sold as a one-time purchase (no subscription plan), the stock forecast report and replenishment logic incorrectly treat it as an active subscription. This results in infinite projected future outgoing moves for standard sales. This occurs because the logic only checks if `recurring_invoice` is True on the product, ignoring whether the parent order actually has a `plan_id`. This commit fixes the issue by: 1. Updating `_get_stock_subscription_lines` in `sale.order.line` to filter out lines using `_subscription_is_one_time_sale()`. 2. Updating the domains in `stock.forecasted_product_product` to require `order_id.plan_id != False` for subscription forecasts, while correctly routing one-time sales (`order_id.plan_id == False`) back to the standard sale domain. 3. Adapting existing tests to verify that one-time sales do not generate future subscription stock forecasts. Task-6193648 Forward-Port-Of: odoo/enterprise#128018 Forward-Port-Of: odoo/enterprise#116889
Fixes an issue that could prevent users from submitting draft Denmark VAT reports. The report now uses the needed prior settings when calculating VAT lines, helping Danish VAT filing proceed reliably.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202 Forward-Port-Of: odoo/enterprise#130320
This fix prevents Odoo from recreating deleted standard Bulgarian accounts or taxes during setup or upgrades when their original identifiers are missing. It helps avoid incorrect accounting records and related installation or migration failures for Bulgarian SAF-T users.
Original PR description
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:…
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:
```
('l10n_bg_691001', {'l10n_bg_saft_account_code': '624'})
```
This can lead to incorrect records.
Only update records whose XMLIDs still exist, avoiding the creation of new records when the corresponding standard record has been deleted.
```
File "/home/odoo/src/enterprise/19.0/l10n_bg_saft/__init__.py", line 10, in _add_account_saft_code
Template._load_data({'account.account': Template._get_bg_saft_account_code()})
File "/tmp/tmpbu1ntr1j/migrations/account/0.0.0/pre-ensure-deferred-accounts.py", line 37, in _load_data
return super()._load_data(data, *args, **kwargs)
File "/home/odoo/src/odoo/19.0/addons/account/models/chart_template.py", line 697, in _load_data
created_records[model] = self.with_context(lang='en_US').env[model]._load_records(all_records_vals)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5196, in _load_records
records = self._load_records_create([data['values'] for data in to_create])
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5103, in _load_records_create
records = self.create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/account/models/account_account.py", line 1055, in create
)).create(vals_list_for_company)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/mail/models/mail_thread.py", line 329, in create
threads = super(MailThread, self).create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/tmp/tmpbu1ntr1j/migrations/util/orm.py", line 267, in wrapper
return f(*args, **kwargs)
File "/tmp/tmpbu1ntr1j/migrations/base/0.0.0/pre-models-match_uniq.py", line 25, in create
return super().create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4711, in create
records = self._create(data_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4887, in _create
cr.execute(SQL(
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.NotNullViolation: null value in column "account_type" of relation "account_account" violates not-null constraint
DETAIL: Failing row contains (734, null, 1, 1, null, null, null, t, f, f, 2026-08-25 08:18:15.751937, 2026-08-25 08:18:15.751937, no, f, null, null, null, null, 411).
```
upg-4611390
tbg-2902
Forward-Port-Of: odoo/enterprise#129145This fixes an issue in Odoo Studio where deleting text next to an element in a customized view could leave the text behind. The correction ensures the intended content is fully removed, reducing confusion and keeping customized views consistent with user edits.
Original PR description
Have an arch like ``` <div> <br /> TEXT <span /> </div> ``` Now remove the block `TEXT <span />` Before this commit, the resulting inheriting arch was just `<xpath expr="//span" position="replace" />` So the `TEXT` was not removed After this commit, the `TEXT` is removed along with the following span opw-6524957 Forward-Port-Of: odoo/enterprise#131812 Forward-Port-Of: odoo/enterprise#131717