Tuesday, August 11, 2026
20 changes · 19.0
Resolved issues and error corrections
This update fixes how Odoo evaluates certain user-provided search filters so results are generated with the correct context. It helps avoid incorrect filtering in areas such as events, expenses, and mail follower tests, improving consistency without changing day-to-day workflows.
Original PR description
The value often comes from the user and may be a Domain, the search implementation may incorrectly handle it by using the wrong context. For most cases, transform 'any' Domain into a Query object before calling `Field.search` to freeze the context used the generate the query. task-6446206 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280368
Fixed an issue where absence-related overtime was not recalculated after an employee later recorded attendance for the same day. This helps ensure attendance records and overtime balances stay accurate across employee time zones.
Original PR description
Steps to reproduce: 0. Create an employee with a fixed working schedule and an overtime ruleset, then enable Absence Management in attendances 1. Go to Attendances and make sure that the employee has…
Steps to reproduce: 0. Create an employee with a fixed working schedule and an overtime ruleset, then enable Absence Management in attendances 1. Go to Attendances and make sure that the employee has no attendances recorded for the previous working day 2. Run the "Attendance: Detect Absences for employees" scheduled action 3. Observe the absence attendance created on the previous working day, along with the amount of overtime hours calculated 4. Create an attendance on the day that the absence was recorded 5. Observe that the overtime hours on the absence attendance are not updated 5a. If a full day attendance is recorded on that day, the overtime hours on the absence attendance should change to 0, since we've now recorded hours worked for the day When an absence attendance is created from the Absence Management feature, overtime hours are calculated to represent the time the employee was unjustifiably absent on a given day. If an attendance is later created on the day the absence was recorded, the overtime hours should update to reflect the hours the employee actually worked. Changes were introduced in [this PR](https://github.com/odoo/odoo/pull/272447) that broke this functionality due to the use of `pytz`, which incorrectly calculated timezone offsets. This caused absence attendances to not be picked up by `_get_overtimes_to_update_domain()`, and overtime was not correctly updated. This commit ensures that we get proper time calculations with respect to the employees timezone. [opw-6380343](https://www.odoo.com/odoo/my-tasks/6380343?debug=assets)
Point of Sale returns now link back to the original sale so returned stock is valued using the original cost. This prevents incorrect inventory values and accounting imbalances for products using average cost or FIFO valuation.
Original PR description
Currently, returning a product via the PoS does not populate the `origin_returned_move_id` on the generated incoming stock move. For products using AVCO or FIFO valuation, this causes the stock valuation engine to fall back to the product's current standard price instead of using the historical cost of the original sale, resulting in stock valuation errors and accounting imbalances. This commit fixes the issue by updating `_prepare_stock_move_vals` to evaluate the `refunded_orderline_id`. It traces the refund back to the original PoS order and dynamically links the original completed outgoing stock move. This ensures the valuation waterfall correctly intercepts the return and applies the original historical cost. opw-6216531 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents an error when users receive subcontracted products with expired serial or lot numbers and create a backorder. It ensures the expiry warning does not incorrectly affect the backorder process, so inventory and manufacturing teams can complete receipts without disruption.
Original PR description
Currently, when the user receives a product with an expiry date whose removal date is before the current date, creating a backorder for the corresponding subcontracting operation causes the system to…
Currently, when the user receives a product with an expiry date whose removal date is before the current date, creating a backorder for the corresponding subcontracting operation causes the system to crash. ## Steps to produce: - Install Manufacturing - Go to settings and enable: - Subcontracting - Lots & Serial Numbers - Expiration dates - Create a product named Vegetable Salad and set Tracking to By Unique Serial Number. - In the inventory section, enable Expiration date and configure: - Expiration Date: 10 days after receipt - Removal Date: 8 days before expiration date - Create a BoM for Vegetable salad - BoM Type: Subcontracting - Subcontractor: Chef - Component: Vegetable - Create a receipt for 2 units of Vegetable Salad from Chef - Mark it as Todo > Details > Assign serial to both lines - Set the Removal Date of the second serial number to a date earlier than today, then Save and Validate the receipt. - In the expiry warning wizard, click Proceed Except Expired and create backorder ## Observed Behavior: Creating a backorder after proceeding with the expiry warning wizard fails with the following error: `ValueError: Expected singleton: stock.move(12, 13)` ## Root cause: This issue occurs because, when the user confirms the backorder, the current picking is validated at [1]. This calls `_action_done` at [2], which in turn calls `_action_done` on the todo moves at [3]. As part of this process, `todo_moves` creates backorders at [4] and then confirms those backorder moves at [5]. During confirmation, the subcontracting manufacturing order (MO) with a serial number that has expired is split at [6], creating a new MO. This new MO then creates two backorder moves at [7]: one for the finished product and one for the component. The problem arises because `default_lot_ids` are added to the context at [8] for the expiry wizard. That same context is unintentionally propagated to the backorder wizard during backorder confirmation. As a result, the ORM assigns those lot IDs to both the component and finished product moves. This triggers the `_set_lot_ids` inverse method, which calls `_prepare_move_line_vals` at [9]. However, `_prepare_move_line_vals` is an `ensure_one` method, while `self` now contains two moves (the component move and the finished product move). Because the method expects a single record but receives two, it raises a singleton error. [1]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/wizard/stock_backorder_confirmation.py#L64 [2]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/models/stock_picking.py#L1428-L1429 [3]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/models/stock_picking.py#L1273 [4]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/models/stock_move.py#L2267-L2268 [5]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/models/stock_move.py#L2332 [6]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/mrp_subcontracting/models/stock_picking.py#L158 [7]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/mrp/models/mrp_production.py#L2055-L2075 [8]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/product_expiry/models/stock_picking.py#L33-L38 [9]: https://github.com/odoo/odoo/blob/c744f123ae4c7a4969e9885ee1efa99be7775340/addons/stock/models/stock_move.py#L679 ## Solution: Sanitize the context before opening the backorder wizard so that `default_lot_ids` from the expiry wizard are not propagated. This prevents the ORM from incorrectly assigning lot IDs to the component and finished product backorder moves, avoiding the singleton error in `_prepare_move_line_vals`. With this change, users can successfully create backorder pickings and subcontracting manufacturing orders for products that are being removed without encountering any errors opw-6390571
The time off request form now shows the correct available allocation for the selected request date. This helps employees and managers choose the right time off type without being misled by balances that are not yet valid or are calculated for the wrong date.
Original PR description
Problem: When creating a time off request, the time off type dropdown displays incorrect allocation for the employee. Steps to reproduce: 1. Allocate a time off type to an employee starting from a…
Problem: When creating a time off request, the time off type dropdown displays incorrect allocation for the employee. Steps to reproduce: 1. Allocate a time off type to an employee starting from a specific date later than today. 2. Create a time off request for the employee. 3. Set the start date of the time off request to a date after the allocation date (a date where the allocation can be used). 4. Check the time off type dropdown. 5. Notice that the allocation displayed in the dropdown is incorrect and does not reflect the allocation that can be used for the selected date. Cause: The start date of the time off request is sent from the view in the context with the key 'default_date_from'. This key gets removed when the context is cleaned because all keys starting with default_ get removed. Solution: Send the start date of the time off request in the context with a different key that does not get removed when the context is cleaned. leave_date_from is used as the key to send the start date of the time off request in the context, because it is already used in the code to get the start date of the time off request.
This fix stops users from marking a main website menu as a mega menu when it already contains submenu items. It prevents migration failures and keeps website navigation rules consistent, especially for sites using recruitment pages.
Original PR description
Issue: ------- After the fix: https://github.com/odoo/odoo/commit/f1557211d9e7f83761bb36e4800e4c2f62b234c5 we can't create a child menu for a mega menu or a menu can't be a mega menu when there's an…
Issue:
-------
After the fix:
https://github.com/odoo/odoo/commit/f1557211d9e7f83761bb36e4800e4c2f62b234c5 we can't create a child menu for a mega menu or a menu can't be a mega menu when there's an existing child menu except the case of top level menu i.e; (url: /default-main-menu) and that menu will have no parent_id obviously...
Now, as per the above pr conditions the top level can be set as mega menu since it has no parent id. And in version 17.3 in the pr https://github.com/odoo/odoo/commit/47af533e9f5f721b63570d3b301951f3855384a1 a 'Jobs' menu is being created and its parent_id refers to that top level menu which we have set as mega menu. And when the records gets validated during migration the database will get blocked.
Solution:
-----------
Restrict the user by throwing the same user error, when checking/selecting the top level menu as mega menu since it has existing child menus.
Step to reproduce:
-----------------------
1. Create a database in version 17.0 with 'website_hr_recruitment' installed.
2. Go to website menus, set a top level menu(/default-main-menu) as mega menu.
3. Migrate the database to version 18.0 or more.
Traceback:
```
File "/home/odoo/src/odoo/18.0/odoo/models.py", line 5297, in _create
records._validate_fields(name for data in data_list for name in data['stored'])
File "/home/odoo/src/odoo/18.0/odoo/models.py", line 1636, in _validate_fields
check(self)
File "/home/odoo/src/odoo/18.0/addons/website/models/website_menu.py", line 95, in _validate_parent_menu
raise UserError(_("A mega menu cannot have a parent or child menu."))
odoo.exceptions.UserError: A mega menu cannot have a parent or child menu.
File "/home/odoo/src/odoo/18.0/odoo/tools/convert.py", line 603, in _tag_root
raise ParseError('while parsing %s:%s, somewhere inside\n%s' % (
odoo.tools.convert.ParseError: while parsing /home/odoo/src/odoo/18.0/addons/website_hr_recruitment/data/config_data.xml:13, somewhere inside
<record id="website_menu_jobs" model="website.menu">
<field name="name">Jobs</field>
<field name="url">/jobs</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">59</field>
</record>
```
Ref Images:
Before Fix:
<img width="1598" height="599" alt="image" src="https://github.com/user-attachments/assets/ef719945-a11b-4134-97f8-4b583c4ea6bc" />
After Fix:
<img width="1582" height="633" alt="image" src="https://github.com/user-attachments/assets/da326e45-0de8-4d42-ad47-845bfaedc84e" />
OPW - 6094298
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#263025When invoicing timesheets for a selected period, refunded hours are no longer accidentally billed again. This helps keep customer invoices accurate after partial refunds and avoids overcharging for service work.
Original PR description
### Steps to reproduce 1. Install *Sales* and *Timesheets* 2. Create a service product: Invoicing Policy = *Based on Timesheets*, Create on Order = *Task* 3. Create a sale order for it (quantity 1)…
### Steps to reproduce 1. Install *Sales* and *Timesheets* 2. Create a service product: Invoicing Policy = *Based on Timesheets*, Create on Order = *Task* 3. Create a sale order for it (quantity 1) and confirm it 4. On the generated task, log **4.5 h on 15/06** and **3.5 h on 23/07** 5. *Create Invoice* with no timesheet period → 8 h, and post it 6. On that invoice: *Reverse* → *Partial Refund*, set the quantity to **3.5 h** and post it → 4.5 h invoiced 7. Log **1 h on 31/07** → 9 h delivered 8. *Create Invoice* again, with a **Timesheets Period of 01/06 → 31/07** ### Current behavior The invoice bills **9 h**: the 4.5 h that were invoiced and not credited are billed a second time. ### Expected behavior The invoice bills **4.5 h** — the quantity delivered minus the quantity invoiced. ### Cause of the issue Posting a partial credit note clears `timesheet_invoice_id` on every timesheet the reversed invoice had linked (`sale_timesheet/models/account_move.py`, `action_post`), because a credit note carries a quantity and never a set of timesheets, so there is no way to tell which hours it credited. All of those hours therefore become candidates again in `_recompute_qty_to_invoice`, which assigns their sum to `qty_to_invoice` without comparing it to what is still due on the line. ### Fix Timesheet links cannot express a partially invoiced timesheet, so they are used only to select the hours a period concerns, while the quantity that may still be billed is `qty_delivered - qty_invoiced`. The period lookup is capped by that remainder, and kept at zero or above so that an over-invoiced line is corrected by a deliberate credit note rather than as a side effect of invoicing a period. ### Tests Five tests are added to `addons/sale_timesheet/tests/test_sale_timesheet.py`. Three of them fail without the fix: | test | without the fix | | --- | --- | | `test_period_invoice_does_not_rebill_refunded_invoice_hours` | `9.0 != 4.5` | | `test_period_invoice_after_refund_is_computed_per_line` | `4.0 != 1.5` | | `test_period_invoice_after_refund_of_an_over_invoiced_line` | `8.0 != 1.0` | The other two cover behaviour that is not exercised today and that the fix must not break: an over-invoiced line (which must be left out rather than credited, and must not prevent the other lines of the order from being invoiced) and the reversed invoice's own `invoice_date`, which must not influence the quantity billed for a period. The full `sale_timesheet` suite passes (86 tests). Forward-Port-Of: odoo/odoo#281065 Forward-Port-Of: odoo/odoo#280536
Self-order takeout details are now saved before customers are sent to the online payment page. This prevents draft orders from losing customer information or pickup times if the customer uses the browser Back button, reducing order errors for restaurants.
Original PR description
**Setup** * Increase the debounce time of `debouncedSynchronizeLocalDataInIndexedDB` to **5 seconds** to reproduce the issue deterministically. * Configure a restaurant with **Self Ordering** enabled…
**Setup** * Increase the debounce time of `debouncedSynchronizeLocalDataInIndexedDB` to **5 seconds** to reproduce the issue deterministically. * Configure a restaurant with **Self Ordering** enabled (`QR Menu + Ordering`). * Configure **Mollie** as the **only** online payment method. **Reproduction** 1. Place a **takeout** order through the mobile menu. 2. Select a pickup time, enter the required customer information (including a mobile number), and proceed to the payment page. 3. Verify from the backend that the draft order contains the expected data (customer/partner and `preset_time`). 4. Press the browser **Back** button to return from the payment page. 5. Check the draft order in the backend again. [video](https://drive.google.com/file/d/1kNWpYuo79mYMV3eMelwJ5IDFeWUc7zsD/view) **Observed result** * The draft order loses its previously synced information. In particular, the **partner/customer** data (and other synced fields such as `preset_time`) are removed. **Expected result** * Returning from the payment page should not modify the draft order. All previously synced data should remain intact. **Cause** - When there's only a single payment method, it's [auto-selected](https://github.com/odoo/odoo/blob/161715c850496d3683baa5d1600380470d0b5ff5/addons/pos_self_order/static/src/app/pages/payment_page/payment_page.js#L21-L22) and `checkAndOpenPaymentPage` immediately opens the payment page via[ window.open()](https://github.com/odoo/odoo/blob/161715c850496d3683baa5d1600380470d0b5ff5/addons/pos_online_payment_self_order/static/src/app/pages/payment_page/payment_page.js#L35). - The order's local data is saved to IndexedDB on a 300ms debounce. If the redirect fires before that debounce completes, the save is cancelled, leaving IndexedDB out of sync with the in-memory order **Fix** - Before opening the payment URL, explicitly flush the order to IndexedDB using the `synchronizeLocalDataInIndexedDB`, ensuring the local data is persisted before the page navigates away. opw-6231478 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update refreshes the spreadsheet component to the latest version, improving stability during collaborative chart editing and fixing keyboard movement for visual elements. It also speeds up spreadsheet formula calculations, which should make larger or formula-heavy spreadsheets feel more responsive.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/b721d1faa2 [REL] 19.0.46 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/b721d1faa2 [REL] 19.0.46 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/b3edb3e1e2 [FIX] carousel: crash on multiuser when deleting chart [Task: 6445004](https://www.odoo.com/odoo/2328/tasks/6445004) https://github.com/odoo/o-spreadsheet/commit/c172359f80 [PERF] vectorization: specialize formula call for common arities [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/622f0c679c [PERF] vectorization: inline generateMatrix [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/8c83c2a2c4 [PERF] vectorization: skip non-vectorized args in inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/887898a2ed [PERF] vectorization: hoist argDefinitions out of vectorized inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/3634c949cf [PERF] vectorization: hoist per-arg getter resolution out of inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/34802dc157 [PERF] vectorization: reuse args buffer across cells [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/4f221b3b4d [FIX] figures: fix movement issue with arrow keys [Task: 6374091](https://www.odoo.com/odoo/2328/tasks/6374091) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
Manufacturing orders can now be marked as done in bulk without crashing when they belong to different companies. Labour costs are posted using the correct production order details, helping avoid incorrect work-in-progress accounting.
Original PR description
Before this commit, marking several manufacturing orders as done at once crashed or could post labour costs on the wrong account, because the labour posting loop read the product and the company from…
Before this commit, marking several manufacturing orders as done at once crashed or could post labour costs on the wrong account, because the labour posting loop read the product and the company from the whole recordset instead of the manufacturing order being processed. Steps to reproduce: - activate a second company, e.g. My Company (Chicago) - create a manufacturing order in each company and confirm them - in the Manufacturing Orders list view, select both orders and mark them as done A "ValueError: Expected singleton: res.company(...)" traceback is raised and none of the orders can be closed, even though each one can be marked as done individually. With same-company orders of different products, the production location resolved from the union of products, so the labour entry could be posted against another product's WIP account. Use the manufacturing order of the current loop iteration to resolve the production location, as the rest of the loop already does. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixes an attendance issue where overtime could be counted twice when records were regenerated for employees checking out shortly after local midnight. This keeps extra hours accurate and prevents duplicate overtime entries in HR attendance records.
Original PR description
When regenerating overtime entries for an attendance whose local check-out time falls shortly after the employee's local midnight, the previously generated overtime lines is not found and so not…
When regenerating overtime entries for an attendance whose local check-out time falls shortly after the employee's local midnight, the previously generated overtime lines is not found and so not deleted before the new one is created. This results in two overtime lines existing for the same attendance, both linked back to it, so the attendance's `overtime_hours` is doubled.
### **Steps to reproduce:**
1) Install hr_attendance.
2) Create an employee with tz = Europe/Brussels, no contract, and an
Overtime Ruleset assigned.
3) Enable "Display Extra Hours" and set Overtime Validation to
"Automatically Approved".
4) Create an attendance for that employee from 3:30 AM to 3:40 AM
(or any check-in between local 00:00 and ~01:42 the following day
in Brussels time).
5) Open the employee's Attendances, note the Extra Hours (0:10), then
change the version date which should be less than attendance date.
and remove the contract date to keep the employee status to not
employeed.
6) Navigate to Overtime Ruleset and open `Employee Schedule Rule` set
'differs: from a specific duration' save the record and click on
'Regenerate Overtimes'.
### **Observed Behavior:**
Extra Hours for the attendance shows 0:20 (0:10 counted twice) instead of 0:10. Overtime lines contains two records for the same attendance/date instead of one.
### Expected Behavior:
Extra Hours should remain 0:10 after regeneration, the old overtime line should be found and replaced, not duplicated.
### Root Cause:
On regenerating overtimes, [action_regenerate_overtimes](https://github.com/odoo/odoo/blob/869c750f978b1b00a4a04bd61226f0e20d2e7729/addons/hr_attendance/models/hr_attendance_overtime_ruleset.py#L50-L51)
calls `_update_overtime`, which calls [_get_overtimes_to_update_domain](https://github.com/odoo/odoo/blob/b4f01111807a12977991d28acb3bf482bc05d248/addons/hr_attendance/models/hr_attendance.py#L268-L292)
to build a time window and search for existing overtime records to
delete before recomputing them. This window is built by converting a
local date/time (e.g. "July 20, midnight") to UTC using the employee's
timezone.
The [conversion used](https://github.com/odoo/odoo/blob/b4f01111807a12977991d28acb3bf482bc05d248/addons/hr_attendance/models/hr_attendance.py#L287-L288),
`.replace(tzinfo=tz)`, does not calculate the correct offset for the
date - `for Europe/Brussels` it applies `+0:18` instead of the correct
`+2:00`, an error of `1h42m`:
```
Check-out (current, wrong):
datetime.datetime(2026, 7, 20, 0, 0, tzinfo=<... LMT+0:18:00 STD>)
Check-out (should be):
datetime.datetime(2026, 7, 20, 0, 0, tzinfo=<... CEST+2:00:00 DST>)
```
Because of this, the search window is shifted by `1h42m`. Any attendance
whose check-out falls in that gap right after local midnight has its
existing overtime record missed by the search, so it isn't deleted,
and a new, duplicate one gets created next to it.
### FIX:
Use `tz.localize(...)` instead of `naive_datetime.replace(tzinfo=tz)` when building the local day-range boundaries in `_get_overtimes_to_update_domain()`, so the correct UTC offset is applied and the existing overtime line is always found and replaced instead of duplicated.
**opw-6373929**Peruvian invoices no longer get stuck when SUNAT has received them but the confirmation file is not ready yet. Odoo now keeps retrying automatically until the confirmation becomes available, reducing manual follow-up and delays in invoice processing.
Original PR description
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError),…
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError), even though SUNAT actually finishes registering the document on its side a moment later. - Odoo retries sending the same invoice (either automatically through the EDI cron, or manually). SUNAT now replies with a "document already exists" SOAP fault (code 1033/4000), since it processed the previous attempt. - Odoo tries to recover from this by fetching the CDR through getStatusCdr, but SUNAT has not finished generating it yet, so the lookup also fails. Cause of the issue: _l10n_pe_edi_post_invoice_web_service() already has recovery logic for error codes 1033/4000: it calls _l10n_pe_edi_retrieve_cdr() to fetch the CDR and treat the invoice as sent. But when that lookup itself fails (CDR not generated yet), the resulting error keeps the 'blocking_level' set to 'error' from the original SOAP fault. Documents with blocking_level 'error' are excluded from the automatic EDI cron retries (see account.edi.document._cron_process_documents_web_services), so the invoice gets stuck needing a manual retry, which can lose the same race against SUNAT again and again. Solution: When the CDR can't be retrieved yet after a 1033/4000 duplicate error, mark the result as 'blocking_level': 'warning' instead of leaving it at 'error'. This keeps the invoice eligible for the automatic EDI cron retries, so Odoo keeps polling SUNAT until the CDR becomes available, instead of requiring manual intervention every time this race is lost. opw-6393231
UrbanPiper delivery orders can now be accepted reliably even when the same print request is triggered twice at nearly the same time. Instead of causing an error, duplicate preparation ticket requests are safely ignored, reducing intermittent failures in point-of-sale order flows.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
Fixes an issue where editing a worksheet design template could fail for companies sharing upgraded worksheet data. Businesses can now customize worksheet templates in Studio without encountering an error caused by duplicate company-specific records.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190
Forward-Port-Of: odoo/enterprise#126773Odoo now recognizes five new response codes introduced by Chile's SII for supplier electronic tax documents. This prevents affected documents from getting stuck during processing and keeps the acceptance or claim workflow aligned with the latest regulation.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
The UK CIS report now correctly includes payment information for receipts that use CIS tax, matching the behavior already available for vendor bills. This helps businesses get a complete and accurate CIS view across both bills and receipts.
Original PR description
With the l10n_uk_reports_cis module installed: - Create a vendor bills and add a CIS tax --> This vendor's bills appear correctly in the report. - Create a receipt and add a CIS tax --> This type of bill appears in the report, but the payment is not showing up. opw-6282548 Forward-Port-Of: odoo/enterprise#124043
This fix ensures Belgian payroll calculations handle employees working on two-week calendars correctly. It helps prevent incorrect payroll-related amounts for affected employees and adds test coverage for this scenario.
This fix improves how Odoo detects fiscal country codes for Avalara tax settings, so relevant tax and address validation fields appear only when appropriate. It helps reduce incorrect or missing Avalara-related options after an underlying country-code logic change.
Original PR description
**Changes:** - Updated the logic for showing address validation in `res_partner.py` to handle fiscal country codes more robustly. - Modified visibility conditions for `is_avatax`, `avatax_category_id`, `avatax_unique_code`, `avalara_partner_code`, and `avalara_exemption_id` fields in XML views to correctly parse and check fiscal country codes. **Purpose:** These changes ensure that the application correctly identifies when to display certain fields based on the fiscal country codes, enhancing the accuracy of the Avatax integration. This is made necessary because of changes to the _compute_fiscal_country_codes method introduced in commit https://github.com/odoo/odoo/commit/c518589716ebde6fb418d907ee01799dd7b889e9.
Refunding Ecuador POS orders made with the "Consumidor Final" customer now shows the intended error message instead of crashing. This prevents a confusing checkout failure and helps staff understand why the refund cannot proceed.
Original PR description
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to…
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to reproduce: ------------------- In POS with l10n_ec_edi module activated: * Create a new order with "Consumidor Final" as customer * Add products and pay the order * Validate the order * Attempt to refund this order > Observation: The refund validation would crash with: TypeError: Cannot read properties of undefined (reading 'add') at OrderPaymentValidation.isOrderValid Why the fix: ------------ The code was trying to access `this.dialog` which is undefined in the OrderPaymentValidation class context. The dialog service should be accessed via `this.pos.dialog`, which is the correct pattern used throughout the base OrderPaymentValidation class. This fix ensures the error dialog is properly displayed when attempting to refund orders for the anonymous final consumer, instead of crashing with a TypeError. opw-6427113
Accounting users in Spanish companies can now export VAT record books that include Point of Sale data without needing POS access rights. The report safely reads the necessary POS information internally, avoiding access errors during tax reporting.
Original PR description
Steps to reproduce:
- With an ES Company
- Open a POS session, add product with tax and pay
- As a user with only accounting access
- Go to Accouting > Reporting > Tax report
- Select Generic Tax report
- Print "VAT record Books"
Issue:
An AccessError will raise
```
Access Error
You are not allowed to access 'Point of Sale Session' (pos.session) records.
This operation is allowed for the following groups:
- Point of Sale/User
Contact your administrator to request access if necessary.
```
Analysis:
Vat Record Books handler for POS needs to read pos.session and pos.order records. Currently, the action is performed with the rights of the user running the report, so accounting-only user face an error.
As POS records are only read internally to build the report, we add sudo call to get the data.
opw-5862529
Forward-Port-Of: odoo/enterprise#126254
Forward-Port-Of: odoo/enterprise#125980