Monday, August 24, 2026
23 changes · saas-19.2
Resolved issues and error corrections
This fix prevents manufacturing orders from failing to save after quantity changes trigger background recalculations. It improves reliability for users editing production orders by ensuring temporary form data is handled safely before saving.
Original PR description
Version: --------- - saas-19.2+ Steps to reproduce: ------------------------ - Install `mrp` module - Create a BoM containing a component and a 60-minute operation duration - Create an MO for that…
Version:
---------
- saas-19.2+
Steps to reproduce:
------------------------
- Install `mrp` module
- Create a BoM containing a component and a 60-minute operation duration
- Create an MO for that BoM with a quantity of 1 and save it.
- Change the quantity to 2.
- Click somewhere in the form to trigger the onchange.
- Change the quantity to 5.
- Try to Save the MO.
Issue:
Saving the Manufacturing Order can raise:
AssertionError: Invalid falsy real id
Cause:
-------
Clicking somewhere after setting the quantity to 2 triggers an onchange; it does not save the MO yet.
During this onchange, Odoo works with virtual copies of the MO and its one2many move records. Recomputing the quantity can replace or remove one of these virtual component or finished moves.
When the onchange response is generated, `RecordSnapshot.diff()` compares the one2many value before and after the onchange:
https://github.com/odoo/odoo/blob/15dc1c48f2eeffaf444c88e94d16a952faa830e8/addons/web/models/models.py#L2280-L2283
For a removed one2many line, it generates the following command:
Command.delete(id_.origin or id_.ref or 0)
A persisted move has an `origin`, so the command contains its database ID. However, a virtual move created during the onchange has neither an `origin` nor a `ref`. Its identifier therefore falls back to `0`.
For example, the onchange response can contain:
'move_raw_ids': [
(2, 0, 0),
(1, 123, {'product_uom_qty': 2}),
]
In these commands:
- `(2, 0, 0)` means DELETE the virtual move whose ID became `0`.
- `(1, 123, {...})` means UPDATE the persisted move with ID `123`.
The web relational model applies and accumulates these commands in the form state while processing successive onchanges:
https://github.com/odoo/odoo/blob/15dc1c48f2eeffaf444c88e94d16a952faa830e8/addons/web/static/src/model/relational_model/static_list.js#L574-L691
After changing the quantity again to 5, the valid update is refreshed, but the deletion command for the discarded virtual record can remain. The final save payload can consequently be:
{
'product_qty': 5,
'move_raw_ids': [
(2, 0, 0),
(1, 123, {'product_uom_qty': 5}),
],
}
Saving the form calls `web_save()`, which forwards this payload to `mrp.production.write()`:
https://github.com/odoo/odoo/blob/15dc1c48f2eeffaf444c88e94d16a952faa830e8/addons/web/models/models.py#L79-L86
The resulting call flow is:
mrp.production.web_save()
-> mrp.production.write()
-> BaseModel.write()
-> One2many.write_real()
-> flush()
-> stock.move.browse([0])
-> AssertionError: Invalid falsy real id
For a DELETE command, the one2many writer adds the command ID to `to_delete`. During `flush()`, it browses all collected IDs before unlinking them:
https://github.com/odoo/odoo/blob/15dc1c48f2eeffaf444c88e94d16a952faa830e8/odoo/orm/fields_relational.py#L1031-L1058
PR https://github.com/odoo/odoo/pull/227477 made browsing collections containing falsy real IDs invalid. This is intentional because `0` or `False` cannot identify a persisted database record.
FIX:
----
BackPort PR: https://github.com/odoo/odoo/pull/274755
it Fix payload.
After fix Payload:
<img width="612" height="517" alt="image" src="https://github.com/user-attachments/assets/856543b1-a466-44ff-bef8-df476234ac78" />
Before Fix Payload:
<img width="791" height="684" alt="image" src="https://github.com/user-attachments/assets/5be4a766-0c9d-4c80-8e16-2b6035f086cc" />
------
opw-6471907
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prCorrects the Mexican chart of accounts so accumulated depreciation and amortization accounts are treated as asset-related accounts. This ensures asset depreciation entries calculate and record proper values instead of showing zero, improving financial accuracy for Mexican companies.
Original PR description
Issue: When creating an asset using an "154.01.01 Vehicles", the depreciation values of the journal entries created are zero Steps to reproduce: 1. Install l10n_mx and enter a Mexican company 2.…
Issue: When creating an asset using an "154.01.01 Vehicles", the depreciation values of the journal entries created are zero Steps to reproduce: 1. Install l10n_mx and enter a Mexican company 2. Create an asset and setting the fixed asset account as 154.01.01 Vehicles 3. Click on "Compute Depreciation" and in the "Depreciation Board" page, all the journal entries created will not have any depreciation values Cause: In the l10n_mx chart of accounts, all accumulated depreciation accounts are set as "expense_depreciation" whereas they should be asset accounts because accumulated depreciation accounts are used to credit asset accounts to decrease asset values. If an account of type "expense_depreciation" is used, then when the depreciation account is credited and the expense account is debited, all financial events will occur within expense accounts, which is why no depreciation values were recorded as the asset balances stay the same. Additionally, the "asset_depreciation_account_id" field on "account.account" has a domain restricting selection to "account_type" of "asset_fixed" or "asset_non_current" Solution: Change the "account_type" of l10n_mx accumulated depreciation accounts from "expense_depreciation" to "asset_fixed" and l10n_mx accumulated amortization accounts from "expense_depreciation" to "asset_non_current" Updrade PR: [odoo/upgrade/pull#10936](https://github.com/odoo/upgrade/pull/10936) opw-6359618 Forward-Port-Of: odoo/odoo#278185
This fixes an issue where product costs could be overstated after a sale was delivered, returned, credited, and sold again. Credit notes are now included when calculating previously posted cost of goods sold, so financial reporting reflects the correct cost amount.
Original PR description
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS…
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS should be 10) - return the delivery and validate - create a credit note from the invoice for 1 and confirm (COGS should be 10) - return the return and validate - change the standard price to 100 - create an invoice from the SO for 1 and confirm **Current behavior:** cogs are 190 **Expected behavior:** cogs should be 100 **Cause of the issue:** _get_posted_cogs_value doesn't take into account the credit notes (only the account moves with type 'out_invoice' are taken into account in the sum) https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L185-L186 So in our case the first invoice and the credit note don't cancel out each other. The same goes for _get_cogs_qty (which returns the total cogs past + current), in the past cogs it doesn't take into account the quantities of the credit note. https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L172-L174 So the quantity of the first invoice and the one of the credit note don't cancel out each other. As a result, the return value from _get_cogs_value() for the second invoice is : price unit = 100 returned by _get_cogs_price_unit() https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L68 which returned the standard price because the product has an average cost method https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/stock_move.py#L275-L280 cogs_qty = 2 (instead of 1 if credit was taken into account as -1 in the sum) self._get_posted_cogs_value() = 10 (instead of 0 if credit note cogs were taken into account in the sum as -10) return value = (100 * 2 -10)/1 = 190 https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L75 **fix:** if we take into account the credit note the return value will be : (100 * 1 - 0)/1 = 100 the mechanism of the already posted cogs value is there for cases where we only delivered a part of the quantity and then delivered the rest, but in the case where we delivered and then returned (with credit notes) it shouldn't have an impact. Thus the idea to include the credit note so that it can cancel out the first invoice opw-6426111 Forward-Port-Of: odoo/odoo#282893
This fixes seven mislabeled entries in the Mexican chart of accounts so exported electronic accounting files use the official SAT descriptions. The change helps Mexican companies avoid mismatches between Odoo's account group names and government catalogue terminology, while leaving trial balance and journal policy exports unchanged.
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#277426
Forward-Port-Of: odoo/odoo#277891This fix restores breadcrumb navigation on customer portal list pages where users previously saw only the page title. It improves navigation clarity across several portal-facing business areas, including sales, purchases, projects, timesheets, accounting, loyalty, subcontracting, and partner assignment.
Original PR description
*=account, hr_timesheet, loyalty, mrp_subcontracting, project, purchase, sale, website_crm_partner_assign Steps to reproduce: 1. install sale 2. Create and confirm a sale order for a portal user 3. Login as a portal user 4. Open sale orders Issue: - Breadcrumbs are not visible beside the title. Cause: - After this commit https://github.com/odoo/odoo/commit/bba2fc505f5d0b4770eacc6877155b1aeda6d772 Variables are passed as attributes directly on the element, but breadcrumbs_searchbar was passed to portal_layout, but the nested portal_searchbar no longer received it. As a result, portal list pages rendered their title instead of the breadcrumb home link. Solution: - Pass breadcrumbs_searchbar directly to portal_searchbar Alternative: - An alternative would be to propagate t-call parameters to slot content in QWeb or changes the condition for breadcrumbs visibility related enterprise pr: https://github.com/odoo/enterprise/pull/118352 opw-6232899
Shipping charges are now recalculated when a customer confirms a recovered cart after product prices have changed. This prevents orders from incorrectly keeping free shipping when the updated cart total no longer qualifies, improving pricing accuracy at checkout.
Original PR description
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the…
Steps to reproduce ================== 1. Configure a delivery method with free shipping above a threshold 2. Add a product to the cart above that threshold, select the delivery method and leave the cart unfinished 3. Lower the product price below the threshold 4. Recover the cart and confirm the order from /shop/checkout => The product prices are refreshed, but shipping stays free although the new total is below the threshold. Root cause ========== Since [1], nothing re-rates the carrier after /shop/confirm_order refreshes the cart prices: the delivery method is selected before the confirmation. In 17.0, the payment page auto-clicked the selected carrier on load, which re-rated the shipping cost and masked the issue. Fix === Re-rate the selected delivery method in `shop_confirm_order` after the prices have been recomputed, as `_cart_update` already does. [1]: https://github.com/odoo/odoo/commit/8e2b6cede55b51f7ccdbe7601aa7e6035fd6f9fe opw-6383849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282963 Forward-Port-Of: odoo/odoo#276905
New onsite learning events created from the Onsite view or an employee resume now remain visible immediately after they are saved. This avoids confusion for HR users by automatically registering the current employee and relaxing overly strict visibility rules.
Original PR description
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the…
Onsite events created from the "Onsite" view or the employee resume selector do not appear immediatlely after creation This occurs because currently the domain for onsite events requires that the event to have multiple slots as well as to have at least one employee registered to it. Therefore, newly created records often fail these criteria and remain hidden. In further versions, this pr: https://github.com/odoo/odoo/pull/246285/ changes the domain of the event selector in the employee resume by removing the dependency on the multiple slots and filtering by the specific employee for registration. This change is not stable to backport as it indroduces the `employee_id` field as an invisible field in the xml to be able to compare in the domain. This commit partly changes both domains to not require the multiple slots anymore, while still showing all events for which an employee is registered. This commit also ensures that when an event is created from the Onsite view or selector, the current user's employee will be registered to it. Steps to reproduce - Go to employees->Learning->Onsite - Select New and create an event - Go back to Onsite Courses - You will not see the created event (unless it is multi_slot and an employee was registered) opw-5915686 Forward-Port-Of: odoo/odoo#258952
Inventory users can now validate dropship transfers for average-cost products when landed costs are enabled. This prevents an access error in a normal sales and purchasing flow, reducing operational delays without changing the business process.
Original PR description
# How to reproduce - Activate the stock_landed_costs module - Enable Dropshipping - Create a product with : - Category : - Costing Method : AVCO - Inventory Valuation : Perpetual - Routes : Dropship…
# How to reproduce - Activate the stock_landed_costs module - Enable Dropshipping - Create a product with : - Category : - Costing Method : AVCO - Inventory Valuation : Perpetual - Routes : Dropship - Atleast one vendor - Create a SO for that product - Confirm the SO & then Confirm the associated PO - Login as an user with "User" rights for Inventory - Try to validate the Dropship transfer # The issue You get an access error. If the same flow is done with a product with a Standard Price costing method, then the Dropship is properly validated # Cause When validating the Dropship, we'll call `_action_done` on the moves. This will trigger an update of the standard price of the product : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/stock_move.py#L177 https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/stock_move.py#L345-L349 Since we're in avco, this will run the `_run_average_batch` method : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/product.py#L675 That will fetch the value of each moves. For the Dropship moves, it'll do so by calling the `_get_value()` method : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/product.py#L486 This method will compute the value of the move, notably by using the associated landed costs : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/stock_move.py#L431 https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_landed_costs/models/stock_move.py#L14 Now the issue is that this computation calls `_read_group` on 'stock.valuation.adjustment.lines' that are retricted to inventory administrators : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_landed_costs/models/stock_move.py#L11 https://github.com/odoo/odoo/blob/5f6fb63d5d7585805642c702d096b2f882e73761/addons/stock_landed_costs/security/ir.model.access.csv#L4 # Proposed solution Get the value of the move in sudo like previously done in the flow : https://github.com/odoo/odoo/blob/60bc7ae38e335958589c172df88e059bf0738cac/addons/stock_account/models/stock_move.py#L314 opw-6323645 Forward-Port-Of: odoo/odoo#273102
This update cleans and validates message posting data before it is accepted, using the current user's permissions as the guide. It helps prevent inappropriate or unexpected data from being included in messages, improving reliability and control in the messaging flow.
Original PR description
This change sanitizes some post data before allowing the post, making sure the data received by `message_post` is clean based on the current user. part of task-6452761 Forward-Port-Of: odoo/odoo#282812 Forward-Port-Of: odoo/odoo#280894
The translation button now saves edits made inside related-record pop-up dialogs before opening translations. This ensures users translate the current text they just entered, rather than outdated or empty saved content, while preserving existing behavior for 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
Restaurant staff can now split bills for combo meals with repeated choices more accurately. When the same combo option is selected multiple times, the split screen now selects the full quantity instead of only one item, helping avoid billing mistakes.
Original PR description
Steps to reproduce: --- - Install `pos_restaurant` demo data. - Open a session for `Restaurant`. - Go to any table. - Add a Sushi Lunch Combo line with the same sushi choice multiple times. - Click the "More" button and select "Split". - Click on any combo product line. Issue: --- - Only one quantity is selected instead of the full combo choice quantity. Cause: --- - Combo child lines were incremented by a fixed value of `1` during split, without considering the quantity ratio between the combo root line and combo child lines. Fix: --- - Compute the selection step based on the combo line quantity relative to the combo root line quantity. - Properly update split quantities for repeated combo choices. - Added test coverage for combo lines with repeated quantities. task-6197879 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282717 Forward-Port-Of: odoo/odoo#264049
Dragging an unscheduled planning slot onto the calendar now works reliably when Studio scheduling is enabled. The fix restores required scheduling details and prevents a missing-date error, avoiding disruptions for users planning work.
Original PR description
Steps to reproduce: ------------------------- 1. Install `sale_planning` and `web_studio` with demo data. 2. Open the Planning calendar view. 3. Enable the "Scheduling" option from Studio and close…
Steps to reproduce:
-------------------------
1. Install `sale_planning` and `web_studio` with demo data.
2. Open the Planning calendar view.
3. Enable the "Scheduling" option from Studio and close it.
4. Drag an unscheduled slot onto the calendar.
Issues:
-----------
**Issue 1:**
```python
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 142, in write
self.assign_slot(vals)
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 159, in assign_slot
new_vals, tmp_sale_order_slots_to_plan, resource = slot._get_sale_order_slots_to_plan(vals, slot_vals_list_per_employee)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 228, in _get_sale_order_slots_to_plan
)._get_resource_work_info(vals, slot_vals_list_per_resource)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 366, in _get_resource_work_info
assert self.env.context.get('default_end_datetime')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
```
**Issue 2:**
```python
UncaughtPromiseError > TypeError
Uncaught Promise > Cannot read properties of undefined (reading 'endOf')
TypeError: Cannot read properties of undefined (reading 'endOf')
```
Cause:
----------
Since commit 1ce0dc8, the scheduling/unscheduling logic has been moved to the generic calendar implementation. However, the generic scheduling flow does not provide the `default_end_datetime` context required by sale_planning. As a result, sale_planning raises an `AssertionError` while scheduling a slot.
Additionally, when no date is available, attempting to call `endOf()` raises a `TypeError`.
Solution:
------------
Introduce a generic scheduling context hook in the calendar model and override it in Planning to provide the `default_end_datetime context when scheduling a slot.
This restores the context expected by` sale_planning`, prevents the `AssertionError`, and avoids calling `endOf()` on an undefined date to resolve `TypeError`.
**NOTE:**
This issue has already been resolved in the later versions (saas-19.4) as part of the scheduling/unscheduling refactoring. This commit backports the minimal changes required to fix the issue in this version.
References: f0f7b34 & https://github.com/odoo-dev/enterprise/commit/f82d073d17ce61a2ff39496364d3dced814ee90a
Related enterprise pr: https://github.com/odoo/enterprise/pull/127196
opw-6442889
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281178Sales users limited to their own documents can now cancel sales orders that include loyalty rewards without hitting an access error. The fix ensures temporary loyalty point records are cleaned up correctly during cancellation, reducing disruption in the sales workflow.
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 ensures Brazilian point-of-sale tax adjustment entries select the correct sales tax instead of sometimes using a matching purchase tax. It prevents accounting entries from becoming unbalanced, improving reliability of POS fiscal records.
Original PR description
The chart template gives the same Avatax code and price_include_override to the sale and the purchase tax, and creates both in the same transaction. Without an explicit type_tax_use the lookup used to return either of them at random, and picking the purchase one left the entry unbalanced. The purchase taxes got their Avatax code in 18.4+. https://github.com/odoo/enterprise/pull/101072 runbot-945969 Forward-Port-Of: odoo/enterprise#128818 Forward-Port-Of: odoo/enterprise#128476
This fixes an issue that prevented users from entering and comparing budget amounts on the Moroccan Profit and Loss report. Businesses using Moroccan accounting reports can now rely on budget comparisons even when the report has multiple columns, including totals.
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#128861
Forward-Port-Of: odoo/enterprise#128266This fix prevents the incoming invoice journal from being cleared for companies that cannot receive Peppol documents through the Documents app, such as French companies using electronic invoicing rules. Incoming Peppol documents will now continue to create vendor bills in the required journal instead of being incorrectly routed to Documents.
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
Belgian fixed monthly salaries are now prorated using the employee's expected hours for the full payslip period, rather than a single week's schedule. This prevents incorrect salary deductions and ensures employees are paid correctly when they work more or less than half of the period.
Original PR description
### Problem - Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against `hours_per_week` (single week) instead of 50% of the theoretical hours for the…
### Problem
- Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against
`hours_per_week` (single week) instead of 50% of the theoretical hours for the payslip period.
This led to incorrect salary deductions in all cases and the wrong computation path being taken when less than
50% of the month was worked.
### Solution
- Fix `_l10n_be_has_enough_paid_hours` to compare paid hours against
50% of theoretical hours instead of `hours_per_week` (which are calculated for the employee's `working schedule`)
### How it works
- For fixed salary (`wage_type = 'monthly'`), the quarterly hourly rule
is applied as follows:
- **Hourly rate** = `fixed_salary × 3 / 13 / theoretical_hours`
- **50% rule**:
- If more than 50% of theoretical hours were worked → deduct absences
from fixed wage
- If less than 50% of theoretical hours were worked → pay only the
hours worked
For variable salary (`wage_type = 'hourly'`), the employee is simply
paid for the number of hours worked during the period.
Task-6260378Android users can now download files opened from the mobile file viewer, such as images shared in Discuss. The change routes downloads through the mobile app's native download handling so Android accepts the file link instead of rejecting it.
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
Cancelling a payslip now correctly resets related time off so it can be included when the payslip is recalculated. This prevents validated leave from being missed in payroll after a payslip is cancelled and returned to draft.
Original PR description
How to reproduce: - Create a payslip for an employee and validate it - Create a new time off for said employee during the same period as the payslip and validate it - Go back to the payslip, cancel it and reset it to draft - The new time off is not included in the payslip Reason: When a payslip is cancelled, if there are time off during the same period as the payslip, their state is not reset to "to compute in next payslip" and instead stays in "to defer to next payslip", causing the issue How it was fixed: Now, when a payslip is cancelled, the new function "return_time_off_to_normal" will catch all leaves that are in the same time frame as the payslip to reset their state to "to compute in next payslip". Task ID: 6431576
Social media users can now like stream posts without encountering permission errors. The update ensures the like action is handled safely in the background, improving reliability for Facebook and Twitter/X social workflows.
Original PR description
Bug === When a social user like a stream post, an access error is raised because he has no write access on it. Task-6425391 Forward-Port-Of: odoo/enterprise#128677 Forward-Port-Of: odoo/enterprise#125973
This fix prevents errors when users drag unscheduled planning slots onto the calendar after enabling scheduling options. It keeps planning workflows stable by ensuring the needed scheduling date information is provided and avoiding a crash when a date is missing.
Original PR description
Steps to reproduce: ------------------------- 1. Install `sale_planning` and `web_studio` with demo data. 2. Open the Planning calendar view. 3. Enable the "Scheduling" option from Studio and close…
Steps to reproduce:
-------------------------
1. Install `sale_planning` and `web_studio` with demo data.
2. Open the Planning calendar view.
3. Enable the "Scheduling" option from Studio and close it.
4. Drag an unscheduled slot onto the calendar.
Issues:
-----------
**Issue 1:**
```python
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 142, in write
self.assign_slot(vals)
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 159, in assign_slot
new_vals, tmp_sale_order_slots_to_plan, resource = slot._get_sale_order_slots_to_plan(vals, slot_vals_list_per_employee)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 228, in _get_sale_order_slots_to_plan
)._get_resource_work_info(vals, slot_vals_list_per_resource)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/enterprise/sale_planning/models/planning_slot.py", line 366, in _get_resource_work_info
assert self.env.context.get('default_end_datetime')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
```
**Issue 2:**
```python
UncaughtPromiseError > TypeError
Uncaught Promise > Cannot read properties of undefined (reading 'endOf')
TypeError: Cannot read properties of undefined (reading 'endOf')
```
Cause:
----------
Since commit [1ce0dc8,](https://github.com/odoo/odoo/commit/1ce0dc86f8ae52b21a5a889aacd1efce0e27c722) the scheduling/unscheduling logic has been moved to the generic calendar implementation. However, the generic scheduling flow does not provide the `default_end_datetime` context required by sale_planning. As a result, sale_planning raises an `AssertionError` while scheduling a slot.
Additionally, when no date is available, attempting to call `endOf()` raises a `TypeError`.
Solution:
------------
Introduce a generic scheduling context hook in the calendar model and override it in Planning to provide the `default_end_datetime context when scheduling a slot.
This restores the context expected by` sale_planning`, prevents the `AssertionError`, and avoids calling `endOf()` on an undefined date to resolve `TypeError`.
**Note:**
This issue has already been resolved in the later versions (saas-19.4) as part of the scheduling/unscheduling refactoring. This commit backports the minimal changes required to fix the issue in this version.
References: f82d073 & https://github.com/odoo-dev/odoo/commit/f0f7b342895734d2151c0c106940006e37a5fd86
Related community pr: https://github.com/odoo/odoo/pull/281178
opw-6442889
Forward-Port-Of: odoo/enterprise#127196Projects linked to both regular sales and rental orders now show the same orders when opening the Sales button as they count in the total. This avoids confusion and ensures rental-related sales are visible from the project view.
Original PR description
Steps to Reproduce --- 1. Install sale_renting_project. 2. Create a Project linked to 1 standard Sales Order and 1 Rental Order. 3. Observe the "Sales" stat button counts 2 Sales. 4. Click the stat button. Only the standard Sales Order is displayed. Issue --- In saas-18.4, the project Sales stat button calls action_view_sos without the from_embedded_action context key. As a result, _get_sale_orders_domain applies the non-rental filter by default, causing rental orders to be excluded from the action even though they are included in the displayed counter. Expected Behavior --- The Sales stat button should display all orders linked to the project, including both standard and rental orders, matching its total counter. Fix --- Return the base project domain unmodified when from_embedded_action is not set in the context. task-6140201 Forward-Port-Of: odoo/enterprise#128468 Forward-Port-Of: odoo/enterprise#121449
When a Shopee shop is re-authorized in Odoo using a different API account, the shop will now correctly switch to that account. This prevents shops from staying linked to an outdated account and reduces setup issues during re-authentication.
Original PR description
Context: when a user re-authenticate a shop, they might use different shopee.account (API key). Currently Odoo will not change the shopee.account when they re-auth with another shopee.account. Enable a shopee.shop switches to another shopee.account when we run `create_or_update_shop` function. Forward-Port-Of: odoo/enterprise#128469 Forward-Port-Of: odoo/enterprise#92446