Thursday, August 27, 2026
33 changes · saas-19.1
Resolved issues and error corrections
Payment XML files now use uppercase character encoding labels to meet stricter bank validation requirements. This helps avoid warnings or rejections from providers such as SIX in Switzerland, improving reliability of SEPA and ISO 20022 payment exports.
Original PR description
The W3C recommendations for XML state that the encoding defined for an XML document should not be case-sensitive. However, some banking providers (SIX for Switzerland) are stricter and may throw warnings or errors if upper-case is not used. https://www.w3.org/TR/2008/REC-xml-20081126/#NT-EncodingDecl opw-4948708 Forward-Port-Of: odoo/enterprise#128301 Forward-Port-Of: odoo/enterprise#125807
Restaurant point-of-sale orders now remember when the number of guests has already been entered on another device. This prevents staff from being asked for the same guest count again when opening the same table elsewhere, while keeping receipts, preparation tickets, and guest displays unchanged.
Original PR description
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the…
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the same table Issue: Device B pops the guest count numpad again, even though the guest count was already entered on device A. Cause: ensureGuestCustomerCount guarded the popup on order.uiState.guestSetted. uiState is only serialized to IndexedDB (SERIALIZED_UI_STATE_PROP, used by serializeForIndexedDB); it is never sent to the server, so the flag is local to one browser and a second device always considers the guest count as not yet asked. customer_count is synced and could carry that information, but PosStore createNewOrder pre-filled it with the table seats, so it was never 0 for a table order and could not tell "not asked" from "answered". Fix: Stop storing the seats default on the record and expose it from getCustomerCount() instead, so customer_count == 0 means "no guest count entered yet". ensureGuestCustomerCount now guards on that synced value, so an order whose guest count was entered on another device is not asked for it again. Every display goes through getCustomerCount(), so the values shown on the Guests button, the receipt and the preparation ticket are unchanged. opw-6470180 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283002
Fixes an accounting issue where reversing a cash basis tax entry after unreconciling a payment could be dated in the current period instead of the original tax period. This keeps tax reports balanced in the correct month and prevents temporary mismatches between original and reversal amounts.
Original PR description
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax…
When unreconciling a payment from an invoice with a cash basis tax, the tax cash basis (CABA) entry is reversed. The reversal is supposed to land in the same period as the origin entry so the tax report nets to zero for that period. Steps to reproduce: - Enable cash basis and create a cash basis tax (exigibility on payment) - Post an invoice dated in the past with that tax - Reconcile a bank statement line to the invoice - Resequence the cash basis entry so the month is dropped from the name (CABA/08/2026/0001 -> CABA/2026/0001) - Unreconcile the statement line Issue: The reversal CABA entry created on the last unreconcile is dated today instead of the origin entry's month. In the tax report the original tax amount stays in the statement's month while the reversal amount appears in the current month, so the two no longer cancel out. Analysis: While under a monthly journal sequence a past date returns the last day of that month, under a yearly sequence a past date within the current year returns the latter between the move date and today, moving the reversal out of the origin period. opw-6301553 Forward-Port-Of: odoo/odoo#284408 Forward-Port-Of: odoo/odoo#281250
When users drill into accounting report figures, they can now view the related journal items using all available views, such as pivot, graph, kanban, and list. This makes financial analysis more flexible and avoids forcing users into a single list-only view.
Original PR description
Problem: When auditing reports, the audit cell action was only showing the journal items in the list view, and not enabling other view modes (pivot, graph, kanban). Steps to reproduce: 1. Go to Accounting > Reporting > Balance Sheet 2. Click on any cell with a number in the report 3. Notice how the journal items are only shown in the list view, and you cannot switch to other view modes. Cause: The action was hardcoded to only show the list view. opw-6403704 Forward-Port-Of: odoo/enterprise#129214 Forward-Port-Of: odoo/enterprise#128563
This fixes an intermittent failure in mail mention suggestion tests by ensuring the test waits for the correct suggestion-list rendering. It improves confidence in automated checks without changing user-facing mail behavior.
Original PR description
Before this commit, the test "select @ mention from the suggestion list being filtered" could fail on runbot, on the check that follows the first "@": Failed to find 2 of ".o-mail-Composer-suggestion" (Timeout of 10 seconds). Found 0 instead. This happens because the test holds a render open on ImStatus, a component the member list renders as well as the composer. The composer tells the server that the user is typing, the bus sends the status back, and the member list re-renders its ImStatus with another class. The hold catches that render, the one that also brings the suggestions on screen. This commit gives the children of NavigableList an inNavigableList environment flag, and holds the render only on an ImStatus that has it. https://runbot.odoo.com/odoo/error/946282 Forward-Port-Of: odoo/odoo#284484
This fixes an issue where grouped data queries could return duplicated or incorrectly shaped results when the same grouping field was requested more than once. The change prevents crashes for callers expecting repeated values and improves reliability of reporting-style data aggregation.
Original PR description
`_read_grouping_sets` dispatches each SQL result row to the grouping set(s) that requested it, using the `GROUPING()` bitmask as the key. When a grouping set repeats a groupby spec, e.g. `['foo',…
`_read_grouping_sets` dispatches each SQL result row to the grouping set(s) that requested it, using the `GROUPING()` bitmask as the key.
When a grouping set repeats a groupby spec, e.g. `['foo', 'foo']`, it computes the exact same bitmask as the grouping set for the deduplicated column alone (`['foo']`).
Two grouping sets sharing a bitmask were treated as interchangeable duplicates: only the first one seen got its extractor registered, and its already-extracted result list was later blindly copied onto every other grouping set sharing that bitmask.
This is wrong whenever those sets don't actually share the same shape:
`_read_grouping_sets(grouping_sets=[['foo'], ['foo', 'foo']])`
returned
`[('my_foo_val', <aggregate>), ('my_foo_val', <aggregate>)]`
instead of
`[('my_foo_val', <aggregate>), ('my_foo_val', 'my_foo_val', <aggregate>)]` (repeating the value, as `_read_group` does).
Callers unpacking crashed with `ValueError: not enough values to unpack`.
Fix:
- Deduplicate the SQL terms of each grouping set before building its `GROUPING SETS (...)`, so that grouping sets which are physically the same always produce a single row from PostgreSQL.
- Register every grouping set's own extractor under its bitmask in a list, instead of keeping only the first one seen, and dispatch each result row to all of them.
task-6511626
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#284683Unsaved translation text now stays in place when a user drags the translation popup. This prevents users from losing newly entered translations during normal dialog movement, reducing rework and frustration.
Original PR description
Step to reproduce: - have atleast two language and install sale - open any product, hover over product, and click on Translation button - Enter a value for one of language - drag the dialog Observation: - we lose the data, we just entered and fallback to original data Cause: - Inputs used `t-att-value="term.value"`, bound to original data. Since this content is passed to Dialog via slot, it is rendered/patched as part of Dialog's render cycle, - Dragging updates Dialog's state, triggering a patch that re-evaluated the slotted template and reset input values (which comes from `term.value`) Fix: - bind value to `updatedTerms[term.id] ?? term.value` so edits survive patches triggered by the parent Dialog opw-6431521 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283514
This fix prevents attendance records from being incorrectly disconnected from unrelated work entries when early or batch-created attendances span UTC day boundaries. It helps keep employee attendance and work entry data accurate for payroll and HR reporting.
Original PR description
When an early attendance starts on the previous UTC day, the cleanup uses full UTC days as boundaries. This can include an unrelated work entry and remove its attendance link. Use the generated work entries as cleanup boundaries so only entries that can overlap the new entries are considered. opw-6412221 Forward-Port-Of: odoo/enterprise#129209 Forward-Port-Of: odoo/enterprise#127040
When creating an inventory delivery, selecting a customer no longer replaces the operation type's custom destination location with the generic customer location. This helps businesses keep transfers routed to the intended customer stock location and avoids manual corrections or delivery mistakes.
Original PR description
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver…
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver Super Customer" and set its default destination location to "Customer stock" - Go to Inventory > Overview > Deliver Super Customer > New - Set a contact on the transfer #### > The destination location switches from "Customer stock" to "Customers" ### Cause of the issue: The `location_dest_id` of `stock.picking` depends on its `partner_id`. So that changing the partner recomputes the locations of the transfer. However, as soon as the destination of the operation type has a `customer` usage, the `property_stock_customer` of the contact replaces it unconditionally: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L949-L963 However, the `property_stock_customer` falls back to an `ir.default` pointing at the default `Customers` location when nothing is set on the contact: https://github.com/odoo/odoo/blob/1c40fab04b71def8f3645c4c4bb0c1441057f307/addons/stock/data/stock_data.xml#L71-L72vs The override comes from 8a0775aa1dd9, which replaced an `elif` fallback on the contact by an "unconditional" substitution as this fallback had become unreachable once `default_location_src_id` and `default_location_dest_id` were made required: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L34-L41 opw-6421090 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283886 Forward-Port-Of: odoo/odoo#280016
The Turkish reporting journal form now places the return from sales account field in the correct position. This prevents account labels and values from appearing under the wrong captions, reducing confusion during journal setup.
Original PR description
The journal form renders `default_account_id` as six standalone labels followed by two `nolabel="1"` fields, one for bank, cash and credit journals and one for sale, purchase and general ones. The xpath matched the first of those two fields, so the return from sales account was inserted between them. Its own label then landed in the middle of the label run, shifting the group grid: both labels rendered side by side with their values underneath, each next to the wrong caption. Anchor on the second field instead, so the new field follows the whole label and field run. Task-6438412 Forward-Port-Of: odoo/enterprise#128083
This fixes a rare website caching issue that could cause pages to fail when an expired cached page was refreshed. The update makes refreshed cached pages behave consistently, improving reliability for visitors without changing normal website behavior.
Original PR description
**Problem:** Normally, the cached response for an HTTP request will be 'flattened', meaning the QWEB template is force-rendered and stored as `response.response[0]`. However, when the cached response is too old, a new cache value is set which is not flattened. This is inconsistent with the value normally returned by `_get_response_cached()` which will cause a traceback when accessing `response.response[0]`. This issue is rarely reproducible because `flatten()` is usually called on the response later (outside `_get_response()`), and because the returned response points to the same object as in the cache, the cache gets flattened as well. **Solution:** When the cached response is too old, flatten the new response before caching it. opw-6382359 Forward-Port-Of: odoo/odoo#277590
This fix ensures reinvoiceable project costs are correctly linked to the appropriate sales order, even when projects share analytic accounts or costs involve multiple accounts. Businesses are less likely to miss billable expenses, improving billing accuracy and reducing revenue leakage.
Original PR description
### Before this fix --- The `_get_so_mapping_from_project()` method returns a mapping where the key is the move line ID and the value is a `sale.order` record (or `None`). Because of the issues…
### Before this fix
---
The `_get_so_mapping_from_project()` method returns a mapping where the key is
the move line ID and the value is a `sale.order` record (or `None`).
Because of the issues described below, a valid `sale.order` could be available
for reinvoicing, but the corresponding move line might still not be mapped to
that sale order. As a result, the move line is not added to the reinvoiceable
sale order.
However, the implementation has two issues:
#### 1. Projects are overwritten when they share the same analytic account
`project_per_accounts` is built as a dictionary mapping an analytic account ID
to a single project. If multiple projects reference the same analytic account,
each new assignment replaces the previous one. As a result, only the last
project associated with a given analytic account is retained.
**Example:**
* Analytic Account **AA1** is linked to **Project A** and **Project B**.
* The dictionary becomes `{AA1: Project B}`.
* **Project A** is lost, even though it also references **AA1**.
**Steps to reproduce:**
1. Create an analytic account **AA1**.
2. Create **Project A** and **Project B**, both linked to **AA1**.
3. Create **Sale Order SO1** linked only to **Project A**.
4. Create a vendor bill (or expense) that generates an AML using **AA1** for a
product configured with **Reinvoice Costs = At Sales Price**.
5. Validate the document.
**Expected behavior:**
The product should be added to **SO1** for reinvoicing.
**Actual behavior:**
The move line is not mapped to **SO1**, so no sale order line is created.
#### 2. Previously found projects are overwritten during iteration
The `project` variable is reassigned on every iteration of the loop. After the
loop completes, it only contains the project (or lack of one) corresponding to
the last processed analytic account. This can cause valid projects found earlier
in the loop to be discarded.
**Example:**
* Move line has analytic accounts **AA1** and **AA2**.
* **AA1** maps to **Project A**.
* **AA2** has no linked project.
* After the loop, `project` is `None`, even though **Project A** was found.
**Steps to reproduce:**
1. Create analytic accounts **AA1** and **AA2**.
2. Create **Project A** linked to **AA1** only.
3. Create **Sale Order SO1** linked to **Project A**.
4. Create a vendor bill (or expense) whose AML is distributed between **AA1**
and **AA2**, where **AA2** is processed after **AA1**.
5. Validate the document.
**Expected behavior:**
The move line should still be mapped to **SO1** because **AA1** references
**Project A**.
**Actual behavior:**
The last processed analytic account (**AA2**) overwrites the previously found
project, causing the move line not to be linked to **SO1**.
### After this fix
---
* `project_per_accounts` stores **all** projects associated with each analytic
account instead of keeping only the last one.
* The project lookup preserves all valid project candidates instead of
overwriting previously found results during iteration.
* As a result, the method can resolve the related `sale.order` in more cases,
improving the overall accuracy of the mapping.
> **Note:** This change prevents valid project associations from being lost
> when multiple projects share an analytic account or when multiple analytic
> accounts are processed for the same move line.
**OPW:** 6294615
Forward-Port-Of: odoo/odoo#277110This fixes an issue where default initials-based avatars could be downloaded by the browser instead of shown on screen on some server setups. The generated avatar format was adjusted so it is consistently recognized as an image, improving contact, user, chatter, and Discuss displays.
Original PR description
**Description of the issue/feature this PR addresses:** `avatar.mixin._avatar_generate_svg()` generates an auto-initial SVG avatar for any record without a real uploaded image (used by `res.partner`,…
**Description of the issue/feature this PR addresses:** `avatar.mixin._avatar_generate_svg()` generates an auto-initial SVG avatar for any record without a real uploaded image (used by `res.partner`, `res.users`, and anything else inheriting `avatar.mixin`). The generated SVG opens with a single-quoted XML declaration: `<?xml version='1.0' encoding='UTF-8' ?>`. When this content is served via `/web/image/...`, `guess_mimetype()` needs to determine its Content-Type since it's a computed value with no stored attachment metadata. On systems where the installed `libmagic` library classifies that specific single-quoted byte pattern as `text/xml` rather than `image/svg+xml`, the wrong Content-Type reaches the browser. **Current behavior before PR:** On affected `libmagic` versions/databases, an auto-generated avatar (a contact or user with no uploaded photo) gets served with `Content-Type: application/octet-stream` (or `text/xml`) instead of `image/svg+xml`. Browsers can't render that inline as an image, so instead of showing the colored-initial avatar, the browser downloads it as an unrecognized file. This affects any place these avatars are displayed: contact/user form and kanban views, chatter message authors, Discuss, etc. Confirmed reproducible with `libmagic` 538 (`python-magic`), where the single-quoted declaration is classified as `text/xml`, while the exact same content with double-quoted attributes is correctly classified as `image/svg+xml`. **Desired behavior after PR is merged:** `_avatar_generate_svg()` now generates its markup with double-quoted attributes throughout, which is correctly sniffed as `image/svg+xml` regardless of the installed `libmagic` version. Auto-generated avatars render inline in the browser as intended. No other code depends on the exact quoting of this generated SVG - `res_users.py` and `hr_employee.py` both only assign the returned bytes to an image field without inspecting their content. Avatar fields are computed and non-stored, so nothing needs to be migrated - every record gets the corrected markup on its very next read, with no backfill required. Updated the two existing `test_avatar_mixin.py` tests that asserted the exact (single-quoted) SVG string, and added `test_generated_partner_avatar_mimetype` to assert the generated avatar is actually sniffed as `image/svg+xml`, so this can't silently regress. Ran locally: all 6 tests in `TestAvatarMixin` pass. Forward-Port-Of: odoo/odoo#283149
The Point of Sale product information popup now shows accurate tax details for combo products. It also displays the minimum combo price based on selecting an item for each combo choice, helping staff give customers clearer pricing and tax information.
Original PR description
Product info popup was not showing the correct tax details for combo products. This commit fixes the issue. The popup display now the minimal price of a combo with an item selected for each combo choice; even if the combo choice isn't including any item. Forward-Port-Of: odoo/odoo#282870
Users can now interact with the "Search More..." dialog when editing document details without it closing unexpectedly. This makes it possible to search, sort, and select related contacts or customers from the modal reliably.
Original PR description
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the…
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the field dropdown, click "Search More..." to open a modal dialog. 5. Click inside the "Search More..." modal (e.g., to sort columns or resize headers). Issue: - The modal dialog immediately closes, and the contact cannot be selected. Root cause: - When an inspector field is edited, the record row is put into edit mode. While in edit mode, the documents list renderer listens for global clicks. Clicking inside the "Search More..." modal dialog targets elements that have `.o_list_renderer` (since the modal dialog renders a list view). Because the click target is within a list renderer but is not a document row, `DocumentsListRenderer.onGlobalClick` executes and clears the selection of the main list view. Clearing the selection unmounts the edited field in the inspector, thereby destroying the modal dialog stack. Solution: - Modify DocumentsListRenderer.onGlobalClick to scope click handling to the current Documents list renderer. Ignore clicks outside this.root.el, so interactions in nested UI such as Search More... do not clear the main selection and destroy the inspector field. opw-6253360 Forward-Port-Of: odoo/enterprise#127717 Forward-Port-Of: odoo/enterprise#119262
Swedish ISO20022 vendor payment batches now generate files that correctly match the selected pain.001.001.09 format. This prevents banks from rejecting payment files because their structure did not match the declared format version.
Original PR description
**Steps to reproduce:** - Install Accounting and l10n_se - Switch to a Swedish company (e.g. SE Company) - In Accounting settings, set "Identification" with anything - In Bank journal: * Set an…
**Steps to reproduce:** - Install Accounting and l10n_se - Switch to a Swedish company (e.g. SE Company) - In Accounting settings, set "Identification" with anything - In Bank journal: * Set an account number * Make sure "Swedish ISO20022" is available in "Outgoing Payments" * Set "pain.001.001.09" as "XML Format" in "Outgoing Payments" - Create a vendor payment: * Vendor: [a vendor with a trusted bank account] * Payment Method: Swedish ISO20022 * Amount: [any] - Confirm the payment - From the payments list, select the payment and create a batch - Validate the batch payment **Issue:** When the batch is validated, a `pain.001.001.09` file should be generated. However, its content is that of a `pain.001.001.03` file, even if the version reported in the file is `pain.001.001.09`. For example, `<ReqdExctnDt>` should contains a subnode `<Dt>` in `001.001.09`, which is not the case. It leads to the file being rejected as non-compliant to `pain.001.001.09`. opw-6472050 Forward-Port-Of: odoo/enterprise#129234 Forward-Port-Of: odoo/enterprise#128598
Kenyan POS refunds sent to eTIMS now reference the original sale's KRA invoice number instead of the refund's own order number. This helps eTIMS correctly match refunded items and amounts, preventing refund rejections while leaving normal sales unchanged.
Original PR description
Steps to reproduce: - Set up a company in Kenya with eTIMS. - Sell an order from the POS and send it to eTIMS. - Refund that order and send the refund to eTIMS. Cause of the issue: When we build the…
Steps to reproduce: - Set up a company in Kenya with eTIMS. - Sell an order from the POS and send it to eTIMS. - Refund that order and send the refund to eTIMS. Cause of the issue: When we build the JSON for eTIMS, the "orgInvcNo" field (the KRA invoice number of the order we are refunding) was always set to `self.sequence_number`, which is just the order's own number in its session. This is wrong for a refund: eTIMS then can't find the item on "the original invoice" (since it's looking at the wrong invoice), and also can't check the amounts, since it's comparing them to the wrong order. eTIMS then rejects the refund with a 910 error, like "item sequence ... does not exist on the original invoice" or "amount is incorrect for item ...". The invoice-based flow (account_move.py) already does this the right way, using `reversed_entry_id.l10n_ke_oscu_invoice_number`, but the POS order flow was not doing the same thing. Solution: For a refund order, use the KRA invoice number of the refunded order (`refunded_order_id.l10n_ke_oscu_order_number`) instead of the refund's own sequence number. Normal sales keep working like before. opw-6445174 Forward-Port-Of: odoo/enterprise#128387
This change rolls back a recent GIF resizing update because it caused slow performance and memory errors when pages displayed multiple GIFs. Users should see more reliable loading in views such as Kanban where animated images are shown.
Original PR description
Revert commit d9fae40571c4f10c17fe00efc087cb25b30b85ab as it's slow on odoo.com and the call to `frame.copy()` is raising a MemoryError when loading a KanbanView with multiple gifs. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284479
Colombian electronic invoicing now handles missing or invalid ZIP attachments when building DIAN commercial event history. This prevents users from seeing a crash when acknowledging receipt of vendor bills after a prior DIAN response attachment was corrupted or stored as plain XML.
Original PR description
When building the commercial event history for DIAN documents, the system crashes if an intermediate document's attachment is missing or is not a valid ZIP file. Steps to reproduce: - Create a vendor bill and send it to DIAN to generate a commercial event document. - Manually alter or corrupt the attachment of the first response document (e.g., save plain XML instead of a ZIP). - Click "Acusar Recibo" (Acknowledge Reception) on the bill. Issue: The system crashes when attempting to unzip a malformed, plaintext, or missing attachment while iterating through past documents to build the event history. Analysis: The system aggregates the XML of previous events to maintain the history trail. Doing so, the system assumes that all past attachments are ZIP archives. However, interacting with the DIAN API can occasionally result in plaintext XML, that raises `zipfile.BadZipFile` when unzipping. opw-5467690 Forward-Port-Of: odoo/enterprise#121494
This update corrects several SAF-T/FAIA reporting issues for Luxembourg, including negative tax amounts, software version length limits, foreign currency tax amounts, and mutually exclusive customer/supplier invoice details. These fixes help businesses generate compliant audit files and reduce validation errors during tax reporting.
Original PR description
This is one of many errors fixing the FAIA report, which is the SAF-T report for Luxembourg. See PR #113316 for a list of similar PRs. ### Error 1: Negative tax amounts Auditors from Luxemborg…
This is one of many errors fixing the FAIA report, which is the SAF-T report for Luxembourg. See PR #113316 for a list of similar PRs. ### Error 1: Negative tax amounts Auditors from Luxemborg provided one Odoo user with analysis files of their FAIA xml report. The following discrepancy was present in more than 300 lines: `[TaxInformation/TaxAmount/Amount] # is negative. Only postive values are admitted. The sign is automatically determined by the corresponding CreditAmount (-) Or DebitAmount (+) on the same Line.` This discrepancy was caused by two different scenarios. The first was a negative `unit_price` line, such as a Discount product. The second was a tax with negative and positive repartition lines, such as a tax with xml ID `lu_2015_tax_AP-EC-17`. Luxembourg officials confirmed the following behavior: 1. The TaxInformation/TaxAmount/Amount element must be positive. 2. The TaxInformationTotals/TaxAmount/Amount element may be negative. 3. There may only be one TaxInformationTotals element per TaxCode in an Invoice element. This commit ensures that these conditions are met for the FAIA report. I'm not sure if the TaxInformation changes should also be applied to the base `account_saft saft_report.xml` file. ### Error 2: SoftwareVersion The SoftwareVersion element is limited to 18 characters. The relevant error from a customer's analysis file is below. Error: Value exceeds maxLength of "18". ### Error 3: CurrencyAmount The `account_saft` method `GeneralLedgerCustomHandler._saft_fill_report_tax_details_values()` does not report the amount of tax in foreign currency, instead replacing this value with the amount in company currency. No errors prompted this change; it just seems wrong on its face. ### Error 4: PR #113720 ensured that the TaxType element is always TVA. This means that the TaxType should no longer should be ignored in our example documents. ### Error 5: Schema validation failure The elements Inovice/CustomerInfo and Invoice/SupplierInfo are defined with the element `<xs:choice>` in the XSD file linked below. Only one can be present at any time, not both. https://pfi.public.lu/dam-assets/backup/FAIA/FAIA/XSD_Files.zip. note: currently the link is broken. PR #100749 allowed many parts of SAF-T code to display both customer and supplier data, including these elements. This commit ensures that the elements are mutually exclusive. opw-6344914 [Link](https://www.odoo.com/odoo/project.task/6344914) Forward-Port-Of: odoo/enterprise#129043 Forward-Port-Of: odoo/enterprise#126121
Non-admin staff can now add extra attendees to appointment-linked meetings without hitting an access error. This ensures invitation emails are generated correctly so the attendee is saved and receives the expected meeting link.
Original PR description
Root cause: Since https://github.com/odoo/odoo/commit/b5aba76b6f0570baed611ffa48c64cc859e6f07b the access_token field on calendar.attendee is only readable by system administrators. The attendee…
Root cause: Since https://github.com/odoo/odoo/commit/b5aba76b6f0570baed611ffa48c64cc859e6f07b the access_token field on calendar.attendee is only readable by system administrators. The attendee invitation template of appointment reads that field with sudo everywhere except in its View button, where https://github.com/odoo/enterprise/commit/a6824395c3f62317a5a1e5136c087bdf857bb379 dropped the sudo while reworking the accept and decline buttons into a form. When a user outside the administrators group adds an attendee to a meeting linked to an appointment type, the invitation mail is rendered as that user. The render crashes with an access error and the attendee cannot be added. The View button is only rendered for attendees that are neither the organizer nor the customer of the meeting, so only adding those attendees triggers the crash. Fix: Put the sudo back on the access_token read in the View button of attendee_invitation_mail_template in mail_template_data.xml, like every other read of that field in the same file. Steps to reproduce: 1. Install appointment and website_appointment 2. Go to /appointment as a visitor and book an appointment with a non-admin internal user, filling in a new name and email 3. Log in as that internal user 4. Open the booked meeting in the Calendar app 5. Add an attendee that is not the customer of the booking and save => an access error dialog about the field access_token on calendar.attendee shows up and the attendee is not added Ticket [link](https://www.odoo.com/odoo/project.task/6379888) opw-6379888
Deleting an active project no longer mistakenly moves document folders from archived projects to the trash. This prevents users from losing access to documents that still belong to archived projects and keeps project records consistent.
Original PR description
Deleting a project also sends the folders of every archived project to the trash. ### Steps to reproduce - Install `documents_project`, where each project has its own Documents folder linked through…
Deleting a project also sends the folders of every archived project to the trash.
### Steps to reproduce
- Install `documents_project`, where each project has its own Documents folder linked through `project.project.documents_folder_id`.
- Create `Project 1`, `Project 2`, and `Project 3`, then archive the first two.
- Delete `Project 3`.
- The folders of `Project 1` and `Project 2` are moved to the trash with their contents, although both projects still exist and still reference them.
### Cause
`_archive_folder_on_projects_unlinked` only archives folders that are no longer used by any project. This was checked through a `documents.document` domain on `project_ids`.
The domain mixed two conditions on the same relation:
- `('project_ids', '!=', False)` checks that a folder has users,
- `('project_ids', 'not any', [('id', 'not in', self.ids)])` checks that it has no users outside the projects being deleted.
Those conditions are not evaluated the same way by the ORM. The first one keeps archived projects visible by disabling `active_test` internally, while the second one searches `project.project` normally and hides archived projects.
An archived project can therefore be counted as a folder user by one condition and ignored by the other, causing its folder to be archived.
### Fix
Check remaining users directly on `project.project` with `active_test=False`, so archived projects are included. Since only folders of deleted projects can become unused, the search starts from those folders instead of scanning all Documents.
opw-6442976
Forward-Port-Of: odoo/enterprise#127217The Point of Sale now handles cancelled return orders more safely when staff reopen the original sale. This prevents the ticket screen from crashing, helping cashiers continue serving customers without interruption.
Original PR description
When a return order is created from the front end and later cancelled without being deleted from the backend, opening the original order from the POS ticket screen causes the UI to crash. The issue…
When a return order is created from the front end and later cancelled without being deleted from the backend, opening the original order from the POS ticket screen causes the UI to crash. The issue occurs because the refundedQty getter in `addons/point_of_sale/static/src/app/models/pos_order_line.js` assumes that every refunded order line has a valid order_id. For cancelled return orders, line.order_id is no longer available, resulting in the following runtime error: `TypeError: can't access property 'state', line.order_id is undefined` As a result, selecting the original paid order from the Ticket Screen breaks the POS interface. **Steps to Reproduce** 1. Create a normal sale in the POS. 2. Keep the POS session open. 3. In the backend, open the POS order and click Return Products. 4. Cancel the generated refund order (do not delete it). 5. In the POS, navigate to Ticket Screen → Orders → Paid Orders. 6. Select the original order. Desired behavior after PR is merged: The original order should open normally without any errors, even if a related refund order has been cancelled. Issue Ticket: https://github.com/odoo/odoo/issues/260079 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279942
Reinstalling the Project app with demo data after using Sales no longer causes an error. The demo setup now avoids reconfirming sales orders that are already confirmed or cancelled, making demo database setup more reliable.
Original PR description
### **Steps to reproduce:** - Install the sale_management and project modules with demo data. - Uninstall the project. - Try to install it again. ### **Issue:** When we installed the sales and project applications with the demo data and then tried to reinstall the project application, it was throwing a traceback. ### **Cause:** In the sale_project module, there was a file for demo data, 'sale_project_demo.xml', which tried to confirm the SO that had already been confirmed the first time; that's why getting the traceback. ### **Fix:** Need to check that the sale order is not in cancel or sale state; otherwise, it will fail. Task-6347927
Activity filters that rely on today's date now match the user's local calendar day instead of UTC. This prevents activities due today from being incorrectly shown as future activities for users in time zones ahead of UTC.
Original PR description
#### Description of the issue: Activity filters using context_today() bucket against the UTC date instead of the user's local date, off by one for part of the day. Partial revert of #265250 (e048bb5), scoped to PyDate: UTC getters are right for PyDateTime, wrong for a calendar day. #### Current behavior before PR: A Perth (UTC+8) user finds an activity due today under "Future Activities" from 00:00 to 08:00 local, while the chatter labels the same activity "Today". #### Desired behavior after PR is merged: context_today(), today and current_date return the user's local calendar day, so filters agree with the chatter. PyDateTime and PyTime keep the UTC getters; now and time.strftime() are unchanged. opw-6415985 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281453 Forward-Port-Of: odoo/odoo#278761
This update replaces an older loop style with the preferred clearer wording in a few core server files. It helps keep automated quality checks passing and supports maintainability, with no expected change for users or business workflows.
Original PR description
Ruff checks on runbot flagged `while 1:` Preferred syntax is to use `while True` [UP048](https://docs.astral.sh/ruff/rules/while-one) runbot-945983 Forward-Port-Of: odoo/odoo#284490 Forward-Port-Of: odoo/odoo#283962
The Japanese localization now uses the correct wording for domestic and overseas fiscal positions. This avoids misleading labels for businesses configuring Japanese accounting and improves clarity in setup screens.
Original PR description
Japanese translation "海外取引先" for domestic was clearly wrong.
Also fixed the misspelling ("Oversea" -> "Overseas") and removed the unnecessary "Customer" context from the name.
@qrtl
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#284608The French e-invoicing module now shows a clearer message when a credit note cannot be sent during EDI document generation. This helps users understand what went wrong and reduces confusion when working with French electronic invoicing in demo mode.
Original PR description
Steps to reproduce: - Install `l10n_fr_pdp` module > Switch to `FR Company` - Activate `French e-invoicing` (Demo mode) - Create a New `Credit Note` with `FR Customer` > Send Issue: The system currently displays a confusing error message during EDI document generation. We are making the error message clearer and more user-friendly. opw-6412521 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284189
Users can now update rental start or end dates on a sales order even if they do not have direct access to planning entries. The related planning schedule is still updated automatically, reducing errors and avoiding unnecessary workarounds.
Original PR description
This commit prevents a potential access error, if a user changes the rental start date and/or end date of a sale order without the access rights to the 'planning.slot' model. In this case, we want the write to be executed and changes repercuted to the associated slots. Forward-Port-Of: odoo/enterprise#128365
Fixed an issue where a signer who appeared more than once on a document could be prompted to sign again before other required signers had completed their step. This ensures signing requests follow the configured order, reducing process errors and keeping approvals compliant with the intended workflow.
Original PR description
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But…
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But make the User and Employee the same contact 3. Send and sign the request > Notice that (1) is able to sign for (3) immediately after, (2) has not signed yet. ### Description of the issue/feature this PR addresses: **Issue:** The signing order is ignored when the same user has to sign multiple times on a document, even if it is configured for a different person to sign in between. This happens because all signature request items are initialized in the 'sent' state upon creation, rather than strictly advancing based on the order. As a result, the system prematurely allows users to sign out of order and prompts them with their next turn too early. **Solution:** To resolve this, the controller was updated to include an `is_mail_sent = True` domain filter. This ensures that the UI's post-sign popup only displays documents where it is explicitly the user's active turn, rather than prompting a premature sign. ### Current behavior before PR: Users are able to sign prematurely, and the system will disregard the configured signing order. ### Desired behavior after PR: Users will only be prompted and able to sign a document when it is explicitly their turn, per the `mail_sent_order`. This way, documents are signed in order. opw-6417327 Forward-Port-Of: odoo/enterprise#128487 Forward-Port-Of: odoo/enterprise#125573
The Brazilian Avalara sales localization now includes the dependency it needs so installation no longer fails when automatic installation is skipped. This helps businesses using Brazilian tax integrations avoid setup errors and get the sales tax module running reliably.
Original PR description
When installing l10n_br_avatax_sale with --skip-auto-install, you'll get an error about the l10n_br fields listed in views/sale_order_views.xml, because these fields don't fully exist without sale_external_tax. This happens because they're defined on a mixin, which is an abstract model. Abstract models only add their fields to a model that actually lists them in `_inherit`. sale.order should list this mixin, but currently doesn't. Adding that dependency is an unstable fix, so it will be added in master (20.0, or 20.1) runbot-237866 Forward-Port-Of: odoo/enterprise#128142
A small configuration mistake prevented two accounting report templates from being individually protected against deletion. This fix corrects the list so both reports are properly safeguarded, reducing the risk of accidental removal.
Original PR description
On `ir.actions.report` we want to block the unlinking of specific reports in odoo. However, when the list was created a comma was missed between `action_account_original_vendor_bill` and `account_invoice_without_payment` which means we were actually protecting against people unlinking `action_account_original_vendor_billaccount_invoice_without_payment`. Adding in that comma will allow these two records to be properly protected. task-none Forward-Port-Of: odoo/odoo#283323
Submitting expenses for multiple companies no longer creates duplicate email notifications. This keeps expense communication cleaner and helps employees and approvers avoid confusion from repeated messages.
Original PR description
Fix a small issue resulting in mail duplication when submitting expenses from multiple companies that appeared in the infamous 704a5a19 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283013