Monday, August 24, 2026
24 changes · 19.0
Resolved issues and error corrections
Bulk emails to multiple applicants now create a missing contact only for the intended applicant. This prevents other applicants in the same email batch from being incorrectly linked to that contact or having their email details overwritten.
Original PR description
**Problem:** When sending an Email to multiple Applicants when one Applicant does not have a `partner_id` set, all Applicants set to receive the email will copy the `email_from` of the Applicant…
**Problem:** When sending an Email to multiple Applicants when one Applicant does not have a `partner_id` set, all Applicants set to receive the email will copy the `email_from` of the Applicant without a `partner_id`. **Cause:** When sending an Email to an Applicant record that does not have a `partner_id` set on it, a new Contact is created to be set to the Applicant. When this Contact is created, its `applicant_ids` value defaults to the Applicants in the context. This causes all Applicants in the current context (i.e. the recipients of the Email) to be set on the Contact, also causing that Contact to be set as the `partner_id` of all the Applicants, rather than just the one missing a `partner_id`. This then causes the `email_from` of the Contact to be set on all the Applicants. https://github.com/odoo/odoo/blob/ccd167e23ea80545e4f09e2302c1b2489e8c42d8/addons/hr_recruitment/wizard/applicant_send_mail.py#L40-L42 https://github.com/odoo/odoo/blob/ccd167e23ea80545e4f09e2302c1b2489e8c42d8/odoo/orm/models.py#L1292-L1293 **Purpose:** Modify the `create` call used to create the new Contact to define `applicant_ids` as only the Applicant that the Contact is being created for. **Steps to Reproduce in Runbot:** 1. Delete the Contact corresponding to the `partner_id` of any given Applicant. 2. Select multiple Applicants, including the one whose `partner_id` was deleted, then send an email to them using the Actions menu. opw-6383263
Stock transfers recorded with zero demand are now included when calculating past forecasted inventory. This prevents historical forecasts from incorrectly showing negative quantities after unplanned physical transfers.
Original PR description
**Problem:** When creating a transfer that moves out a product with zero demand quantity, it will change the forecasted quantity of that product in the past. **Cause:** The query filtered out the stock move with zero demand quantity, which preventing the system from accounting for unplanned physical transfers when retroactively calculating past inventory balances **Steps to reproduce the issue:** 1. Create a stock picking with 0 demand quantity that moves a product from an internal location to a virtual location or production location. 2. The forecasted quantity of the product becomes negative in the past. **Fix:** Add another check in the query to include stock moves with zero demand quantity. **Notes:** Since the forecast report is made from a SQL view, this will require a -u to update the report. opw-6462883 Forward-Port-Of: odoo/odoo#283577
The Point of Sale variant selection popup now shows the truly free stock quantity, excluding items already reserved by confirmed sales orders. This avoids misleading cashiers into thinking stock is available when it has already been committed elsewhere.
Original PR description
## Steps to reproduce: - Create another warehouse - Create a product with a variant, like Color, values black and white - Track the product, add a qty on hand of 50 on the black product - Go to the sales app, make a quotation of 50 for the black product - Confirm the quotation - Go to the PoS, click on the product, check the available qty in the popup - It is still 50, even though the forecasted is correct at 0 ## Why the fix: Having the actual free qty was added in this commit 682bc82 to be able to check the qty that was really free instead of the available qty. This means that we subtract the reserved_qty from the qty_available to get the free_qty. The variant popup was forgotten in this commit, so it was still displaying the qty_available. This is why there was a difference in the qty if we press the product normally or if we long press it, because the variant popup was forgotten in said commit. opw-6382845 Forward-Port-Of: odoo/odoo#280330
This fix ensures delivery charge lines keep the correct unit of measure when they are created. It prevents Odoo from recalculating related sales amounts incorrectly, helping avoid wrong delivery pricing or order totals in specific sales setups.
Original PR description
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the…
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the delivery line is created. This causes `product_uom_id` to be recomputed. This commit reintroduces `product_uom_id` in the values to prevent the field from being recomputed. **Description of the issue/feature this PR addresses:** For a strange reason, when a module inherits from `sale.order.line` and adds some computed fields with `precompute=True`. `price_unit`, `price_subtotal`, and `price_total` are computed incorrectly. I have attached a module to demonstrate the issue. https://github.com/user-attachments/assets/ebdd8695-c9d8-477b-b5cf-ba6d8d41e84a Without this change, the test fails, and Odoo incorrectly recomputes the fields, as shown in the video. <img width="1232" height="515" alt="image" src="https://github.com/user-attachments/assets/27370f1f-e7b7-4102-a606-0181b9d1a97a" /> When the ORM computes fields marked as `precompute=True`, in this function `_add_precomputed_values` https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/odoo/orm/models.py#L4836, `price_unit` is 0, but the records get `price_unit` from the product. Therefore, when [_compute_amount](https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/addons/sale/models/sale_order_line.py#L855) is called, the values are computed with an incorrect `price_unit`. <img width="1087" height="940" alt="image" src="https://github.com/user-attachments/assets/92feadf0-7f93-4acc-8f1a-931db0655fcc" /> **Steps to reproduce the issue:** - Install the attached module. [sale_precompute.zip](https://github.com/user-attachments/files/31265993/sale_precompute.zip) - Configure a delivery carrier as free for orders over 1, and set the fixed price to 5, for example. - Create a sales order and add a product with a value greater than 1. - Add the shipping method. The price should be 0. In the sales order line, `price_unit` is 0, but `price_subtotal` and `price_total` are equal to 5 (the product's sale price). For more context, this module is a simple example extracted from the OCA `product_secondary_unit` module, which adds a mixin with these fields: https://github.com/OCA/product-attribute/blob/18.0/product_secondary_unit/models/product_secondary_unit_mixin.py. In the `sale_order_secondary_unit` module, `sale.order.line` inherits from this mixin. You can see the error in this PR: https://github.com/OCA/sale-workflow/pull/4535. https://github.com/OCA/sale-workflow/actions/runs/32259842816/job/96090194021?pr=4535#step:8:509 I understand that this requires a deeper investigation into precompute to solve the underlying issue, but I propose setting `product_uom_id` in the `_prepare_delivery_line_vals` method as a temporary solution while the final solution is being investigated. I understand that this field should not have been removed from `_prepare_delivery_line_vals`; the referenced PR only renamed the field and did not intend to remove the value from this method. @Tecnativa @pedrobaeza @kcv-odoo @Feyensv coudl you please review this? --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Vendor bills created from employee expenses in Mexico now correctly display their CFDI XML attachment when one exists. This helps users access required tax documents directly from the bill instead of having attachments hidden in the system.
Original PR description
**Current behavior:** Currently, account moves created from expenses (type receipt) don't include the XML when it is a CFDI. Causing users cannot see their attachment even though it is created on DB. https://docs.google.com/videos/d/1eFSAA-wvUzPDwIJDeiS2dkne94lGM7QBxRfjN3HxaLw/play **Versions:** 19+ **Fix:** Implementing a new helper to identify those moves that can actually hold a CFDI document (for now, all `is_invoice()` documents + vendor bill receipt, `in_receipt`), so now, we include 'in_receipts' in _compute_l10n_mx_edi_cfdi_state_and_attachment, _compute_l10n_mx_edi_document_ids, _compute_l10n_mx_edi_update_sat_needed and l10n_mx_edi_cfdi_try_sat. As these documents might need to fetch SAT services as well. Task-id: [6397080](https://www.odoo.com/odoo/project/49/tasks/6397080)
This fixes an issue where companies that cannot receive Peppol invoices through Documents, such as French companies using electronic invoicing, could lose their required incoming invoice journal setting. Incoming Peppol documents will now continue to be handled as vendor bills when Documents reception is not allowed, preventing missing settings and misplaced invoices.
Original PR description
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal…
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal stays required. This is the case of French companies (via l10n_fr_pdp). The onchange of the settings and the import did not check this method. So on a French company with the mode set to 'documents': - the Settings cleared the journal on each opening, while it was still required - the incoming documents were saved in Documents instead of vendor bills Steps to reproduce: - Create a Belgian company, with a purchase journal, and register it on Peppol as receiver - Set the reception mode to "Receive in Documents" - Change the fiscal position to France, and install l10n_fr_pdp, the Peppol part is replaced by "French Electronic Invoicing", so the radio button is not visible anymore, but the company still has peppol_reception_mode == 'documents'. - Open the Settings again: the field "Incoming Invoices Journal" is empty. opw-6429691 Forward-Port-Of: odoo/enterprise#126462
Odoo now correctly reads Colombian vendor bill XML files that contain withholding taxes. This ensures ReteRenta, ReteIVA, and ReteICA amounts are added to invoice lines, reducing manual corrections and improving tax accuracy.
Original PR description
Currently, Odoo doesn't detect Withholdings taxes for Colombia while uploading vendor bill XML, causing ReteRenta, ReteIVA, ReteICA withholdings to not being added in invoice lines, for versions under 19.2 Fix: Backport commit https://github.com/odoo/enterprise/commit/edc67858350d1b8408019e51751240026d931dab Versions: 18.0 -> 19.1 Task-id: [6417175](https://www.odoo.com/odoo/project/49/tasks/6417175) Forward-Port-Of: odoo/enterprise#128158 Forward-Port-Of: odoo/enterprise#126186
This fixes an issue where some incoming Chilean electronic invoices could fail to import when they included foreign currency details on lines but did not include a foreign-currency total in the header. The import now falls back to the standard total, helping automated mail-based invoice processing continue reliably.
Original PR description
When importing an incoming DTE through the fetchmail server, the total amount is read from the MntTotOtrMnda as soon as a Moneda node is present in the document. Steps to reproduce: - Set up a CL company with a DTE mail server - Fetch a DTE that includes the line-level Moneda node but does not include the header OtraMoneda block, so no MntTotOtrMnda - Run the fetchmail cron and check the logs Issue: The DTE fails to import Analysis: Occurs since https://github.com/odoo-dev/enterprise/commit/5805a92f91411846fdffa245cb047397cfc9b1f3 Moneda is defined at line level while MntTotOtrMnda in the optional header block Encabezado/OtraMoneda. Instead of assuming MntTotOtrMnda is always present whenever the document carries a foreign currency, fall back to the base-currency total MntTotal when it is missing. opw-6432612 Forward-Port-Of: odoo/enterprise#128631 Forward-Port-Of: odoo/enterprise#126869
Sales users without accounting permissions can now view invoices created from sales orders that use cash rounding. This prevents an unnecessary access error while keeping invoice access aligned with the user's existing permissions.
Original PR description
**Behavior:** When a sales user without accounting access rights tries to view an invoice created from a sale order where a cash rounding is applied, an AccessError is raised. This occurs because…
**Behavior:** When a sales user without accounting access rights tries to view an invoice created from a sale order where a cash rounding is applied, an AccessError is raised. This occurs because loading the invoice view triggers `_compute_tax_totals()`, which then passes the invoice's `invoice_cash_rounding_id` to `_get_tax_totals_summary()`. Which then ends up failing when trying to access fields on the `cash_rounding` record due to missing accounting rights, even though the user is allowed to view the parent invoice. This is fixed by ensuring reading fields on `cash_rounding` during tax total computation bypasses the access check using `sudo()`, as the user already has legitimate access to the invoice itself. **Steps to reproduce:** - As an admin, enable cash rounding then create one. - Create an invoice and set the Cash Rounding Method - In debug mode, go to 'Set Default Values' in the debug dropdown and set Cash Rounding Method = your rounding for all users - Go to users, and ensures that Demo has no accounting rights but has sales user rights - As Demo, create a sale order, confirm it, then create the related invoice. - When trying to acces said invoice, you should get an Access Error opw-6379781
When tasks are duplicated, created from templates, or generated as recurring tasks, their sub-task dependency chains are now preserved correctly. This prevents copied project work from having tasks linked in the wrong order, reducing manual cleanup and scheduling confusion.
Original PR description
**Problem:** Duplicating a task, creating a task from a task template, or generating the next occurrence of a recurring task scrambles the dependencies between its sub-tasks: each copied sub-task…
**Problem:** Duplicating a task, creating a task from a task template, or generating the next occurrence of a recurring task scrambles the dependencies between its sub-tasks: each copied sub-task carries the dependencies of a different sub-task instead of its own. **Steps to reproduce:** 1. Enable Task Dependencies on a project. 2. Create a task with three sub-tasks and chain them: the second depends on the first, the third depends on the second. 3. Duplicate the task, or use "Create from template" if the task is a template. 4. Open the sub-tasks of the new task and look at their dependencies. **Current behavior:** The dependencies of the copied sub-tasks are shifted: the chain runs in the reverse order of the original one. **Expected behavior:** Each copied sub-task depends on the copy of the sub-task its original depended on, so the new task reproduces the original chain. **Cause of the issue:** `_create_task_mapping` builds the original to copy mapping by pairing `original_task.child_ids` with `copied_task.child_ids` positionally, on the assumption stated in its docstring that both recordsets share the same index order. They do not. `project.task._order` ends with `id desc`, so `child_ids` is read newest-first, while the copies are created by iterating the original `child_ids` in that same order. The copies' ids therefore ascend along the original list, and reading them back through `child_ids` returns them in the exact reverse order. `zip` then pairs each original with the copy of the sub-task at the mirrored position, and `_resolve_copied_dependencies` writes every `depend_on_ids` and `dependent_ids` onto the wrong copy. This affects every caller of that method: `copy`, the task template action, and the creation of the next occurrences of a recurring task. **Fix:** Sorting the copied children by id restores the correspondence because id order is the order in which the copies were created from the original list, an invariant that holds whatever `_order` does, whereas the previous code silently depended on `_order` producing the same sequence on both sides. `test_duplicate_project_with_subtask_dependencies` and `test_recurrence_copy_task_dependency` were reading the copies by `child_ids` index too, which the mirrored mapping happened to satisfy, so they passed on a wrong result; they now index them in creation order as well. opw-6386578
This fixes a notification issue where mentions could be sent to an archived user account linked to the same contact, causing the intended person to miss the message. The system now selects an active user for each recipient, making inbox notifications more reliable.
Original PR description
Before this commit, mentioning a partner that has an archived user sent the inbox notification to that archived user, so the mentioned person never saw the mention. This happens because the query picking the user of a recipient joins res_users without filtering on active, and keeps one row per partner with DISTINCT ON and no ORDER BY, so which row survives is arbitrary. One solution could have been to keep every active user of the partner, which is what we want as each of them has its own notification type, but a notification is stored per partner, so the type of a single user applies to all of them. Picking one user is a current limitation. This commit fixes the issue by taking the first active user of each partner in a lateral join, ordered as mail.followers._get_recipient_data already does: internal users first, then the lowest id. Forward-Port-Of: odoo/odoo#283914 Forward-Port-Of: odoo/odoo#283806
The Colombian DIAN workflow now handles missing or malformed document attachments when preparing commercial event history. This prevents users from hitting a crash when acknowledging receipt of vendor bills and keeps the process moving even if a prior DIAN response was not stored as a valid ZIP file.
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
Partial barcode receipts no longer incorrectly remove pending operation-level quality checks when users return to the transfer. This helps ensure required receipt quality controls remain visible and enforceable until the full transfer is properly processed or cancelled.
Original PR description
Steps to reproduce --- 1. Create a quality control point on the Receipts operation type with Control per set to Operation. 2. Confirm a receipt of 2 units of the product: one pending operation…
Steps to reproduce --- 1. Create a quality control point on the Receipts operation type with Control per set to Operation. 2. Confirm a receipt of 2 units of the product: one pending operation quality check is created. 3. In the Barcode app, receive 1 unit and go back to the transfer with the back button. 4. The pending operation quality check is gone. Issue --- Going back from the Barcode app calls `post_barcode_process`, which on a partial reception splits the picked move into a done move and a remaining move, then merges the transient duplicate back with `_merge_moves`. https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/stock_barcode/models/stock_move.py#L57-L60 `_merge_moves` cancels that transient duplicate through `_action_cancel` before unlinking it. https://github.com/odoo/odoo/blob/8f3100ca597559945cc42d9ef9517edbb40a900b/addons/stock/models/stock_move.py#L1400-L1401 The `quality_control` override of `_action_cancel`, picks the pending checks to drop from `is_product_canceled`, a `defaultdict(lambda: True)` keyed by `(picking, product_id)`. An operation check has no `product_id`, so its key is never computed by the loop and reads back the `True` default, so it is deleted even though the transfer still has a live move. Since an operation check covers the whole transfer, it must be dropped only when every move of its picking is cancelled. https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/quality_control/models/stock_move.py#L68-L76 opw-6439179 Forward-Port-Of: odoo/enterprise#128654 Forward-Port-Of: odoo/enterprise#127427
This fix allows users who are assigned as editors on a shared document folder to update access settings as expected. It removes an incorrect restriction that prevented legitimate editor members from adjusting internal user access, helping teams manage folder permissions more reliably.
Original PR description
1. Create a non-company root folder 2. Edit rights as follows: * add Marc Demo as editor member * access for internal users and link to None 3. As Marc Demo, try updating Internal users access to "editor" ⮕ You can't. Task-6410610
This fix ensures that when users translate text from a record opened in a related-record dialog, Odoo first saves the edits made in that dialog. As a result, the translation window shows the current text instead of outdated or missing content, while preserving existing behavior in editable lists.
Original PR description
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened…
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened in an x2many form dialog keeps its changes for itself until the dialog is saved, see https://github.com/odoo/odoo/blob/242f6d3cf7288853f163ac6986a3b7aa4279efaf/addons/web/static/src/model/relational_model/static_list.js#L193. Its pending changes are not part of the root record changes, so saving the root sends nothing to the server, and the translation dialog then shows the stored terms instead of the current content, or no terms at all when the stored value is empty. The fix changes openTranslationDialog in translation_button.js, the place that decides which record to save. When the record keeps its changes for itself (record._noUpdateParent), the record is saved directly, like the button did before the commit above. The root record is still saved in the other cases, so the editable list case that commit fixed keeps working. Steps to reproduce: 1. Activate a second language in Settings > Translations > Languages 2. Open the Surveys app, open a survey and click a question in the Questions tab 3. In the Description tab, change the description 4. Click the EN button on the description field => the translation dialog shows the terms of the previous description, not the current one Ticket [link](https://www.odoo.com/odoo/project.task/6237291) opw-6237291 Forward-Port-Of: odoo/odoo#269507
This fixes an issue where pressing Shift+Enter in Safari created a full paragraph break instead of a simple line break in the HTML editor. Users editing Knowledge articles on Mac Safari can now format text as intended without unwanted spacing or splits.
Original PR description
**Steps to reproduce:** - Use a Mac with Safari - Install Knowledge app - Go to any article - Press Shift+Enter to try to enter a soft line break - Hard split is done instead **Issue:** Shift+Enter causes a `insertParagraph` event instead of `insertLineBreak` in Safari, which triggers the `SplitPlugin` instead of the `LineBreakPlugin`. **Fix:** Check if the browser is Safari and call `insertLineBreak` from the `SplitPlugin` (when needed) by listening to the "keydown" events. (note: I was not able to find any other key combination to properly trigger the `insertLineBreak` event in Safari) opw-6413507 Forward-Port-Of: odoo/odoo#281458
This fixes several mislabeled entries in the Mexican chart of accounts so their names match the official SAT catalogue. The correction helps electronic accounting exports show the proper descriptions, reducing confusion and compliance review issues without changing accounting balances or calculations.
Original PR description
Seven entries of the Mexican chart of accounts template carry a name belonging to a **different** group, copied from a neighbouring entry. Each record's XML ID still states the intended name, which…
Seven entries of the Mexican chart of accounts template carry a name belonging
to a **different** group, copied from a neighbouring entry. Each record's XML ID
still states the intended name, which is what this restores.
| Code | Field | Before | After |
|---|---|---|---|
| `6` | `name@es` | Gastos generales | Gastos |
| `252.07` | `name@es` | `account_subgroup_hipotecas_por_pagar_a_largo_plazo_nacional` | Hipotecas por pagar a largo plazo nacional |
| `602` | `name`, `name@es` | Cost of sales / Costo de venta | Selling expenses / Gastos de venta |
| `613` | `name@es` | Amortización contable | Depreciación contable |
| `614` | `name` | Accounting depreciation | Accounting amortisation |
| `701.06` | `name`, `name@es` | Interest on foreign bank charges / Intereses a cargo bancario extranjero | Interest payable by national natural persons / Intereses a cargo de personas físicas nacional |
| `702` | `name@es` | Utilidad cambiaria | Productos financieros |
### Why it is not cosmetic
The electronic accounting Chart of Accounts XML takes the `Desc` attribute of
every `<Ctas>` element from the *account group name* — `cfdicoa.xml`
(`t-att-Desc="account.get('name')"`), fed by `trial_balance.py`
`_l10n_mx_get_coa_values()`. Any `es_*` database therefore declares:
```xml
<catalogocuentas:Ctas CodAgrup="702" NumCta="702" Desc="Utilidad cambiaria" Nivel="1" Natur="A"/>
```
whereas the SAT catalogue (Anexo 24) publishes `702` as *Productos financieros*,
with `702.01 Utilidad cambiaria` … `702.10 Otros productos financieros` beneath
it. `CodAgrup` comes from `code_prefix_start` and stays correct, so the file
still validates against the XSD, but the declared description does not match the
official nomenclature. Trial Balance and Pólizas are unaffected — neither
exports group names.
### Evidence
- `252.07` contains its own XML ID as the Spanish name.
- `602` duplicates `501.01`, yet its children are `Sueldos y Salarios`,
`Compensaciones`, `Tiempos extras`.
- `613` and `614` are swapped in one language each: `613`'s children are
depreciations, `614`'s are amortisations.
- `701.06` duplicates `701.05` in both languages; the correct name is symmetric
to `701.07` and to `702.06`.
- `6` is the only single-digit root group whose Spanish name does not match its
XML ID (`account_group_gastos`).
### Notes
Introduced in d782b8b92557; correct in 15.0, where the names lived in
`account.account.tag.csv`. Still present in 18.0, 19.0 and master, hence
targeting 17.0. Template data only — existing databases are unaffected until the
chart is (re)installed, and renaming a group moves no balance.
Forward-Port-Of: odoo/odoo#277426Sales users limited to their own documents can now cancel orders that include loyalty programs without hitting an access error. The fix ensures temporary loyalty point records are cleaned up correctly during cancellation, reducing workflow interruptions for sales teams.
Original PR description
Steps to produce: --- - Install `sale_management` and `sale_loyalty` module without demo. - From sales > products > discounts & loyalty, create new loyalty card program and save. - Now create new…
Steps to produce: --- - Install `sale_management` and `sale_loyalty` module without demo. - From sales > products > discounts & loyalty, create new loyalty card program and save. - Now create new product of 100$. - Create a user which have sales rights as `user: own documents only`. - With that user, create new sale order with product and confirm. - Try to cancel the order. Issue: --- - It shows the access error: ```py You are not allowed to delete 'Sale Order Coupon Points - Keeps track of how a sale order impacts a coupon' (sale.order.coupon.points) records. This operation is allowed for the following groups: - Sales/Administrator Contact your administrator to request access if necessary. ``` Root cause: --- - Users with the `Sales: Own Documents Only` access right only have read permissions ([1]). When they cancel a Sales Order, the `_action_cancel` method attempts to clean up the temporary pending points allocated to the order by calling `self.coupon_point_ids.unlink()`. Because this call is executed without elevated privileges, the system blocks the deletion and raises an Access Error Solution: --- - Added `.sudo()` to the `unlink()` call for `coupon_point_ids` in the `_action_cancel` method. This ensures the pending point records are cleaned up with the necessary elevated privileges. [1]https://github.com/odoo/odoo/blob/23af2b443735c6d3a2f64e44f9ea5da45638b052/addons/sale_loyalty/security/ir.model.access.csv#L16 opw-6453016 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283535 Forward-Port-Of: odoo/odoo#281477
This fix prevents invoice recalculations from creating incorrect inventory valuation differences when average-cost products are affected by landed costs. Businesses get more reliable cost of goods sold and stock variation figures after resetting and confirming invoices.
Original PR description
**Issue** Recalculating an invoice after applying a landed cost could lead to a wrong stock variation. **Steps to reproduce** - Create 2 products: - Product A: valuated using AVCO tracked by quantity…
**Issue** Recalculating an invoice after applying a landed cost could lead to a wrong stock variation. **Steps to reproduce** - Create 2 products: - Product A: valuated using AVCO tracked by quantity - Product B: service with landed cost enabled - Create and confirm a PO for 10 units @ 500 of product A and validate the receipt - Create and confirm a SO for 3 units of the product A and validate the delivery - Create and validate the invoice - Create a Bill for the PO and add 1 unit @ 100 of product B, using the expense account - Create the landed cost and confirm it and the Bill - Reset the invoice to draft and confirm it again -> In inventory valuation, Stock Variation is 30 instead of 0 **Cause** Each time an invoice is validated, it recompute the cogs values: https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/account_move.py#L29-L37 https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/account_move.py#L122 which needs the cogs unit price: https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/account_move_line.py#L68 https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/stock_move.py#L275-L280 This returns the standard price (and not the move value) since the product is average-costed but not lot-valuated. The `move.value` originating from the SO is 500*3 = 1500, since it has been computed before adding the landed cost and stay constant afterwards. However, the `move.value` originating from the PO becomes 5000 + 100 = 5100. Indeed, when confirming the landed cost, it recomputes the value of the move: https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_landed_costs/models/stock_landed_cost.py#L152 https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/stock_move.py#L323 https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/stock_move.py#L441 https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_landed_costs/models/stock_move.py#L24 Which updates the standard price to 510: https://github.com/odoo/odoo/blob/791e5423a73acbef69a5ffe26b3f6036269395f2/addons/stock_account/models/stock_move.py#L356-L359 So there is a discrepancy between the `stock.value` (1500) and the cogs values (510*3=1530), which explains why the Stock variation is 30. opw-6416072
This fix restores the official SAT names for seven Mexican chart of accounts groups that had incorrect labels copied from neighboring entries. It improves the accuracy of electronic Chart of Accounts XML reports so their descriptions match the official Mexican tax authority catalogue, while Trial Balance and Policies reports remain unaffected.
Original PR description
Seven entries of the Mexican chart of accounts template carry a name belonging to a **different** group, copied from a neighbouring entry. Each record's XML ID still states the intended name, which…
Seven entries of the Mexican chart of accounts template carry a name belonging
to a **different** group, copied from a neighbouring entry. Each record's XML ID
still states the intended name, which is what this restores.
| Code | Field | Before | After |
|---|---|---|---|
| `6` | `name@es` | Gastos generales | Gastos |
| `252.07` | `name@es` | `account_subgroup_hipotecas_por_pagar_a_largo_plazo_nacional` | Hipotecas por pagar a largo plazo nacional |
| `602` | `name`, `name@es` | Cost of sales / Costo de venta | Selling expenses / Gastos de venta |
| `613` | `name@es` | Amortización contable | Depreciación contable |
| `614` | `name` | Accounting depreciation | Accounting amortisation |
| `701.06` | `name`, `name@es` | Interest on foreign bank charges / Intereses a cargo bancario extranjero | Interest payable by national natural persons / Intereses a cargo de personas físicas nacional |
| `702` | `name@es` | Utilidad cambiaria | Productos financieros |
### Why it is not cosmetic
The electronic accounting Chart of Accounts XML takes the `Desc` attribute of
every `<Ctas>` element from the *account group name* — `cfdicoa.xml`
(`t-att-Desc="account.get('name')"`), fed by `trial_balance.py`
`_l10n_mx_get_coa_values()`. Any `es_*` database therefore declares:
```xml
<catalogocuentas:Ctas CodAgrup="702" NumCta="702" Desc="Utilidad cambiaria" Nivel="1" Natur="A"/>
```
whereas the SAT catalogue (Anexo 24) publishes `702` as *Productos financieros*,
with `702.01 Utilidad cambiaria` … `702.10 Otros productos financieros` beneath
it. `CodAgrup` comes from `code_prefix_start` and stays correct, so the file
still validates against the XSD, but the declared description does not match the
official nomenclature. Trial Balance and Pólizas are unaffected — neither
exports group names.
### Evidence
- `252.07` contains its own XML ID as the Spanish name.
- `602` duplicates `501.01`, yet its children are `Sueldos y Salarios`,
`Compensaciones`, `Tiempos extras`.
- `613` and `614` are swapped in one language each: `613`'s children are
depreciations, `614`'s are amortisations.
- `701.06` duplicates `701.05` in both languages; the correct name is symmetric
to `701.07` and to `702.06`.
- `6` is the only single-digit root group whose Spanish name does not match its
XML ID (`account_group_gastos`).
### Notes
Introduced in d782b8b92557; correct in 15.0, where the names lived in
`account.account.tag.csv`. Still present in 18.0, 19.0 and master, hence
targeting 17.0. Template data only — existing databases are unaffected until the
chart is (re)installed, and renaming a group moves no balance.Updating a contact linked to more than one WhatsApp signature request no longer causes an error. The system now handles email and WhatsApp signature notifications separately, making contact updates more reliable.
Original PR description
Steps to reproduce: ---------------------------------------- 1. Install `whatsapp_sign` and Contact modules 2. Go to Sign > templates 3. Make two sign requests for the same partner 4. Go to partner…
Steps to reproduce:
----------------------------------------
1. Install `whatsapp_sign` and Contact modules
2. Go to Sign > templates
3. Make two sign requests for the same partner
4. Go to partner record and try to change the email (remove or add a character)
Observation:
----------------------------------------
Traceback occurs:
```
File '/home/odoo/odoo/enterprise/whatsapp_sign/models/sign_request_item.py', line 101, in _send_signature_access_message
is_whatsapp = self.sign_request_id.send_channel == 'whatsapp'
File '/home/odoo/odoo/community/odoo/orm/fields.py', line 1657, in __get__
record.ensure_one()
File '/home/odoo/odoo/community/odoo/orm/models.py', line 5942, in ensure_one
raise ValueError('Expected singleton: %s' % self)
ValueError: Expected singleton: sign.request(3, 4)
```
Issue:
----------------------------------------
In `_send_signature_access_message()` which accessed `self.sign_request_id.send_channel` directly on the full multi-record recordset. When `res_partner.write()` detects an email change, it searches for all sent sign request items of that partner and calls `send_signature_accesses()` on the combined recordset. Accessing `sign_request_id` on this multi-record set resolved to multiple sign requests, and the subsequent field access triggered `ensure_one()`.
Solution:
----------------------------------------
* Refactored `_send_signature_access_message()` to iterate over each item individually, checking `send_channel` per item's own `sign_request_id`. Items are partitioned into `email_items` and `whatsapp_items` batches. Email items are delegated to `super()` and WhatsApp items are processed individually.
* Refactored to cleaner and more readable code and correctly handles mixed-channel recordsets where items may belong to different sign requests with different send channels.
opw-6387586This fixes a problem where Android users could not download files from the Odoo mobile app, such as images opened from Discuss. Downloads are now sent through the mobile app's supported download path, preventing the error message and allowing users to save files normally.
Original PR description
Steps to reproduce: - send an image in a Discuss channel - click the image to open the file viewer - click the download button => Android shows "The Odoo Mobile Apps only supports file downloads…
Steps to reproduce: - send an image in a Discuss channel - click the image to open the file viewer - click the download button => Android shows "The Odoo Mobile Apps only supports file downloads using the HTTP protocol." downloadFile()'s GET-by-URL case fetches the URL via XHR, then saves the Blob response by clicking a hidden <a download> anchor on a blob: URL. Android's DownloadManager only accepts http(s) URLs, so it rejects that blob: URL instead of downloading anything. Patch downloadFile._download to hand the URL directly to a new mobile.methods.saveFile bridge method when available, the same way download._download already delegates to mobile.methods.downloadFile. Blob/string content downloads aren't handled here — the only such call site (spreadsheet JSON export) is debug-mode only, so this is left as a console.warn for now. Related to odoo/odoo@e83fd8c08c879f5e262d39f24edcb3f81238ea82 Code made by Claude Changes supervised by HUVW Forward-Port-Of: odoo/enterprise#128471 Forward-Port-Of: odoo/enterprise#127693
This fix restores delegation support in Studio approval workflows, helping teams keep approval processes moving when responsibilities are assigned to someone else. It reduces the risk of blocked business operations caused by approvals not reaching the delegated person.
Original PR description
opw-6321766 Forward-Port-Of: odoo/enterprise#122441
This fix lets users enter and compare budget amounts on the Moroccan profit and loss report, even though it uses multiple columns. Budget figures now stay visible and are compared against the correct balance total, improving reporting accuracy for Moroccan accounting users.
Original PR description
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons: - The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of…
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons:
- The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of the two others.
=> We remove that requirement, and make sure to always select the 'balance' column as the reference for the budget comparison.
- When trying to input a budget amount in the report, the amount disappeared entirely.
=> This was because the total column of report was not using 'balance' as its expression label. We fix that by rewriting the expression labels of that report.
The fact we hardcode the use of 'balance' is arguable. It is however not possible here to rely on some custom handler to change a specific option key that would be used to generate the budget comparison data, since some of those data need to be generated in the get_options, before _custom_options_initializer even gets called. This is the simplest approach, and this case is rare enough for us to deem it acceptable.
opw-6385229
Forward-Port-Of: odoo/enterprise#128816
Forward-Port-Of: odoo/enterprise#128266