Daily updates from Odoo
Monday, August 3, 2026
281 changes
25 changes
Enhancements to existing features
Steps to reproduce: 1. Create a sales order. 2. Confirm or cancel the order. 3. Share the quotation link. 4. Open the quotation from an incognito window or the customer portal. Issue: - A 'Quotation Viewed by Customer' notification is sent even though the document is no longer an active quotation. Fix: - Only send the notification while the order is in quotation or quotation sent state. opw-6419364 Forward-Port-Of: odoo/odoo#279925 Forward-Port-Of: odoo/odoo#278633
Original PR description
Steps to reproduce: 1. Create a sales order. 2. Confirm or cancel the order. 3. Share the quotation link. 4. Open the quotation from an incognito window or the customer portal. Issue: - A 'Quotation Viewed by Customer' notification is sent even though the document is no longer an active quotation. Fix: - Only send the notification while the order is in quotation or quotation sent state. opw-6419364 Forward-Port-Of: odoo/odoo#279925 Forward-Port-Of: odoo/odoo#278633
Resolved issues and error corrections
Before this commit: - The `routing_identifier` field was reset on save because `_get_all_identifiers` checked it prematurely before `routing_scheme` and `routing_endpoint` were stored. - Fixing this exposed a French PDP test failure: the test expected `routing_identifier` to stay empty when entering a SIREN, which previously passed only due to the reset bug. After this commit: - `_get_all_identifiers` checks scheme and endpoint directly so `routing_identifier` persists properly on save. -
Original PR description
Before this commit: - The `routing_identifier` field was reset on save because `_get_all_identifiers` checked it prematurely before `routing_scheme` and `routing_endpoint` were stored. - Fixing this exposed a French PDP test failure: the test expected `routing_identifier` to stay empty when entering a SIREN, which previously passed only due to the reset bug. After this commit: - `_get_all_identifiers` checks scheme and endpoint directly so `routing_identifier` persists properly on save. - French PDP suggests SIREN for lookups without auto-filling `routing_identifier` on partner creation. - Removed an unused line in tests. > These fixes were discussed and confirmed with @clbr-odoo no-task
Running more than one gevent worker relies on every worker binding the same address, which the kernel only allows when SO_REUSEPORT is set on the socket before bind(2). The regression was introduced by 17164382feeeb3d081aabe398442f8a8f319e507, which replaced the hand-rolled socket setup with socket.create_server. That helper binds and listens on the spot, so the option was left applying to an already bound socket, where it does nothing. Only the prefork master spawns those processes, so i
Original PR description
Running more than one gevent worker relies on every worker binding the same address, which the kernel only allows when SO_REUSEPORT is set on the socket before bind(2). The regression was introduced…
Running more than one gevent worker relies on every worker binding the same address, which the kernel only allows when SO_REUSEPORT is set on the socket before bind(2). The regression was introduced by 17164382feeeb3d081aabe398442f8a8f319e507, which replaced the hand-rolled socket setup with socket.create_server. That helper binds and listens on the spot, so the option was left applying to an already bound socket, where it does nothing. Only the prefork master spawns those processes, so it takes --workers together with more than one gevent worker for the breakage to show. When it does show, the first worker binds the gevent port and the others exit with EADDRINUSE, only to be respawned in a loop by the master. create_server already applies the option between socket creation and bind when asked for it, so let it do so. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to produce: --- - Install the Sales module. - Enable `Pricelists` from Sales settings. - Create a new product. - Go to Sales > Products > Pricelists and open an existing pricelist. - Add the following rules for a product: - min qty: 1 > price: 100 - min qty:10 > price: 80 - Create a new quotation > add section > add the same product. - Set the section as optional > Preview the quotation. - Change its quantity to 10. Added test covering the fix introduced in [com
Original PR description
Steps to produce:
---
- Install the Sales module.
- Enable `Pricelists` from Sales settings.
- Create a new product.
- Go to Sales > Products > Pricelists and open an existing pricelist.
- Add the following rules for a product:
- min qty: 1 > price: 100
- min qty:10 > price: 80
- Create a new quotation > add section > add the same product.
- Set the section as optional > Preview the quotation.
- Change its quantity to 10.
Added test covering the fix introduced in [commit], ensuring
that pricelist rules are correctly reapplied when the quantity of an
optional product is changed from the quotation preview.
[commit]: https://github.com/odoo/odoo/commit/93b6bdd6a4909bc0b45b90ab6a2d0734a218292d
opw-6241183
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#279135
Forward-Port-Of: odoo/odoo#266597Version: -------- - 19.0+ Steps to reproduce: ------------------- - Install `mrp` module - Go to the setting enable `Lots & Serial Numbers` and `Storage Locations` - Create a storable product tracked by Lots - Configure a Putaway Rule for the product so it is stored in a sub-location - Create a Bill of Materials for the product with at least one component - Create and confirm a Manufacturing Order - Increase the production quantity (e.g. using the "Change Production Quantit
Original PR description
Version: -------- - 19.0+ Steps to reproduce: ------------------- - Install `mrp` module - Go to the setting enable `Lots & Serial Numbers` and `Storage Locations` - Create a storable product tracked…
Version:
--------
- 19.0+
Steps to reproduce:
-------------------
- Install `mrp` module
- Go to the setting enable `Lots & Serial Numbers` and `Storage Locations`
- Create a storable product tracked by Lots
- Configure a Putaway Rule for the product so it is stored in a
sub-location
- Create a Bill of Materials for the product with at least one
component
- Create and confirm a Manufacturing Order
- Increase the production quantity (e.g. using the "Change Production
Quantity" wizard)
- Click **Generate Lot/Serial Number**
- Click **Produce All**
Issue:
------
Completing the Manufacturing Order raises:
Invalid Operation
You need to supply a Lot/Serial Number for product:
- Product
even though a single lot should be sufficient for a lot-tracked
product.
Cause:
------
When the production quantity is increased, `change_prod_qty()` updates
the finished move's demanded quantity and re-reserves it through
`_update_finished_moves()`:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/mrp/wizard/change_production_qty.py#L77
which calls `_action_assign()` on the finished move:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/mrp/wizard/change_production_qty.py#L49
Since finished moves originate from the production location, they
bypass the normal reservation flow:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/stock_move.py#L2070
`_action_assign()` then tries to reuse the move's existing move line,
but the lookup requires `location_dest_id` to still match the move's
generic destination:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/stock_move.py#L2092-L2106
That existing line's `location_dest_id` was already redirected to the
putaway sub-location by the previous `_apply_putaway_strategy()` call
(at MO confirmation), so the lookup no longer matches and a second,
distinct move line is created and appended instead of the first one
being reused:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/stock_move.py#L2170
Later, clicking **Generate Lot/Serial Number** creates a single lot and
stores it on the production order's `lot_producing_ids`:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/mrp/models/mrp_production.py#L1602
When **Produce All** is clicked, which trigger `button_mark_done()` it calls
`_post_inventory()`:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/mrp/models/mrp_production.py#L2227
which assigns that lot to the finished move through `move.lot_ids`:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/mrp/models/mrp_production.py#L1925
Since `lot_ids` is declared with `inverse='_set_lot_ids'`, this write
triggers that inverse method:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/stock_move.py#L192
The current implementation of `_set_lot_ids()` only assigns the lot to
a single available move line, regardless of tracking type:
https://github.com/odoo/odoo/blob/154b49ec6be6230989ee4eb420e7f83b681ff520/addons/stock/models/stock_move.py#L656-L668
Since only one lot is ever generated for a lot-tracked product, only
the first finished move line receives a `lot_id`. The second move line
created after increasing the production quantity is left without one.
When `button_mark_done()` validates the finished move lines, it
detects that one of them still has no lot assigned and raises the
"Invalid Operation" error, even though a single lot is valid for the
entire production of a lot-tracked product.
Fix:
----
`action_generate_serial` produces a single lot for the whole production.
In `_post_inventory()`, right after the generated lot is set on
the finished move, propagate it to any remaining lot-less move lines
of a **lot**-tracked finished move.
---
opw-6366060
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277819
Forward-Port-Of: odoo/odoo#275000Description of the issue this commit addresses: The settlement tour expects an invoice named with the year 2026. On time-shifted test instances, invoices use a later year, so the tour cannot find the invoice and fails at the settlement selection step. --- Desired behavior after this commit is merged: This commit matches settlement invoices using the stable journal prefix, so the tour works regardless of the year in which it runs. --- runbot-[242206](https://runbot.odoo.com/odoo
Original PR description
Description of the issue this commit addresses: The settlement tour expects an invoice named with the year 2026. On time-shifted test instances, invoices use a later year, so the tour cannot find the invoice and fails at the settlement selection step. --- Desired behavior after this commit is merged: This commit matches settlement invoices using the stable journal prefix, so the tour works regardless of the year in which it runs. --- runbot-[242206](https://runbot.odoo.com/odoo/error/242206) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279756 Forward-Port-Of: odoo/odoo#278573
account/tests/test_audit_trail.py::TestAuditTrail.test_content was failing whenever l10n_fr_pdp was installed: posting/resetting a move produced an extra "E-Reporting Status" tracking value on top of the expected name/state ones. l10n_fr_pdp_status and l10n_fr_pdp_last_flow_id are tracked fields but are meant to be excluded from generic tracking and reported via a dedicated chatter message instead. The exclusion was implemented by overriding _message_track(), a method that no longer exi
Original PR description
account/tests/test_audit_trail.py::TestAuditTrail.test_content was failing whenever l10n_fr_pdp was installed: posting/resetting a move produced an extra "E-Reporting Status" tracking value on top of the expected name/state ones. l10n_fr_pdp_status and l10n_fr_pdp_last_flow_id are tracked fields but are meant to be excluded from generic tracking and reported via a dedicated chatter message instead. The exclusion was implemented by overriding _message_track(), a method that no longer exists in the mail tracking API (replaced by _track_get_fields()/_track_prepare() some time ago), so the override was dead code and never ran. Override _track_get_fields() instead, which is the actual hook the framework uses to build the set of auto-tracked fields. runbot error - 941346 Forward-Port-Of: odoo/odoo#278095
Forward-Port-Of: odoo/odoo#279378
Original PR description
Forward-Port-Of: odoo/odoo#279378
Steps: - Install marketing_card & website_event_track - Create a marketing card campaign for "Event Track" - Create an event with tracks - Click on "Send Cards" in event form - Choose your campaign for the mailing - Update X cards for the mailing Actual result: - Recipients is "Event Track" - Card Campaign Mailing should target model Event Track - Mailing model is still the default one during the validation Expected result: - No error - Card are updated - User will be able to s
Original PR description
Steps: - Install marketing_card & website_event_track - Create a marketing card campaign for "Event Track" - Create an event with tracks - Click on "Send Cards" in event form - Choose your campaign for the mailing - Update X cards for the mailing Actual result: - Recipients is "Event Track" - Card Campaign Mailing should target model Event Track - Mailing model is still the default one during the validation Expected result: - No error - Card are updated - User will be able to send mailing after update Forward-Port-Of: odoo/odoo#279288
**Steps to reproduce:** 1. Create a new SO and enable "Online Signature" and "Online Payment" in the "Other Info" tab 2. Click on Preview and click on "Sign & Pay" from the portal (demo payment should be enabled from the settings to proceed) 3. Once the transaction is processed and the order confirmed, check the confirmation email/PDF sent to the customer in the chatter **Issue:** - The order confirmation sent to the customer after paying online does not include the signature they provide
Original PR description
**Steps to reproduce:** 1. Create a new SO and enable "Online Signature" and "Online Payment" in the "Other Info" tab 2. Click on Preview and click on "Sign & Pay" from the portal (demo payment…
**Steps to reproduce:** 1. Create a new SO and enable "Online Signature" and "Online Payment" in the "Other Info" tab 2. Click on Preview and click on "Sign & Pay" from the portal (demo payment should be enabled from the settings to proceed) 3. Once the transaction is processed and the order confirmed, check the confirmation email/PDF sent to the customer in the chatter **Issue:** - The order confirmation sent to the customer after paying online does not include the signature they provided when accepting the quotation - Even if "Online Payment" is not enabled and Signing will directly confirm the order, the signature is still missing. **Why this happens:** - The signature block in `sale.report_saleorder_document` is gated by the `sale_include_signature` context key rather than solely by doc.signature. This was introduced by commit ef8246a4daf6146da2ed3cb78c37c7bf0937a4df to retain signature integrity. - `portal_quote_accept` only sets this context right after the customer signs on the pdf rendered for us (company), and was not passed through `_validate_order()` when there was no online payment - When online payment is required, `_has_to_be_paid()` defers the order confirmation which happens later, and the context is never set elsewhere **Fix:** - Pass the context when online payment is not required - If online payment is required, the sale quotation can be modified after being signed. However, since the customer previews the quotation when Paying, we can say the signature integrity is retained opw-6389733 Forward-Port-Of: odoo/odoo#278854
Current behavior before PR ----- The "unit cost" footer line was showing up on each page of the bom overview, overlapping with other lines. Desired behavior after PR is merged ----- The "unit cost" footer line should only appear at the bottom of the overview. Forward-Port-Of: odoo/odoo#262995
Original PR description
Current behavior before PR ----- The "unit cost" footer line was showing up on each page of the bom overview, overlapping with other lines. Desired behavior after PR is merged ----- The "unit cost" footer line should only appear at the bottom of the overview. Forward-Port-Of: odoo/odoo#262995
Steps to reproduce: - Install `project`, open a project and switch to the Gantt view - Start dragging a task while holding Ctrl (copy mode), then switch browser tab with Ctrl+Tab (or Ctrl+Shift+Tab, Ctrl+PgUp/PgDn) - Come back to the Gantt tab and drop the task without holding Ctrl Issue: The task is duplicated instead of rescheduled. Cause: The copy/reschedule behavior is tracked through window keydown/keyup listeners on the Control key. While the document is hidden, the keyup for Co
Original PR description
Steps to reproduce: - Install `project`, open a project and switch to the Gantt view - Start dragging a task while holding Ctrl (copy mode), then switch browser tab with Ctrl+Tab (or Ctrl+Shift+Tab,…
Steps to reproduce: - Install `project`, open a project and switch to the Gantt view - Start dragging a task while holding Ctrl (copy mode), then switch browser tab with Ctrl+Tab (or Ctrl+Shift+Tab, Ctrl+PgUp/PgDn) - Come back to the Gantt tab and drop the task without holding Ctrl Issue: The task is duplicated instead of rescheduled. Cause: The copy/reschedule behavior is tracked through window keydown/keyup listeners on the Control key. While the document is hidden, the keyup for Control is never received, so the drag sequence resumes with a stale "Ctrl pressed" state and the drop is treated as a copy. Fix: Keyboard and pointer states cannot be reliably tracked while the document is hidden, so cancel any ongoing drag sequence from `makeDraggableHook` as soon as the tab is no longer visible (through the `visibilitychange` event). This applies to every drag and drop instance built on the hook builder. opw-6298440 Forward-Port-Of: odoo/odoo#277014 Forward-Port-Of: odoo/odoo#276859
The amount in words split the total with int(decimal * 100), which truncates. 3989.33 is held in binary as 3989.3299..., so the kuruş/cents came out one short (32 instead of 33). The written amount then disagreed with the numeric total on the same invoice and Nilvera rejects it. Round to the currency precision and round the subunit instead. Task-6383690 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: ---
Original PR description
The amount in words split the total with int(decimal * 100), which truncates. 3989.33 is held in binary as 3989.3299..., so the kuruş/cents came out one short (32 instead of 33). The written amount then disagreed with the numeric total on the same invoice and Nilvera rejects it. Round to the currency precision and round the subunit instead. Task-6383690 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277748 Forward-Port-Of: odoo/odoo#277180
When selling a product with the Ship Later option the COGS for the product were not recorded properly. Steps to reproduce: ------------------- * Create a product with fifo and real_time valuation * Open PoS and add the product to the order * Validate the order and set the Ship Later option * Go to the backend and validate the picking of the order * Go check the journal entries of the session > Observation: It doesn't contain the COGS line. Why the fix: ------------ Since the stock
Original PR description
When selling a product with the Ship Later option the COGS for the product were not recorded properly. Steps to reproduce: ------------------- * Create a product with fifo and real_time valuation * Open PoS and add the product to the order * Validate the order and set the Ship Later option * Go to the backend and validate the picking of the order * Go check the journal entries of the session > Observation: It doesn't contain the COGS line. Why the fix: ------------ Since the stock_valuation refactoring the COGS are not created anymore when validating the picking of the order. opw-5965021 Forward-Port-Of: odoo/odoo#278308 Forward-Port-Of: odoo/odoo#259714
The navigator class, which adds event handlers, is instanciated on the setup of components using the useNavigation hook. However, before this commit, it was destroyed when the components were unmounted. It may happen that components are instanciated (setup is executed) but never mounted in the DOM, when the current rendering is cancelled. When this happened, the navigator cleanup wasn't executed, resulting in a small memory leak. This commit fixes the issue by using onWillDestroy instead.
Original PR description
The navigator class, which adds event handlers, is instanciated on the setup of components using the useNavigation hook. However, before this commit, it was destroyed when the components were unmounted. It may happen that components are instanciated (setup is executed) but never mounted in the DOM, when the current rendering is cancelled. When this happened, the navigator cleanup wasn't executed, resulting in a small memory leak. This commit fixes the issue by using onWillDestroy instead. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279657 Forward-Port-Of: odoo/odoo#279322
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, i
Original PR description
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill…
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, it should the port ship to state code but there cases where goods can be transfered to nearby country i.e. Bangladesh, Nepal where good can taken by road from India In that case the state code should be 97 task-6431082 **Second Commit** - [FIX] l10n_in_ewaybill: import/export GSTIN should be URP Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279289
Before this commit, the composer suggestion list could re-open right after the user closed it with Escape, and the next press of Escape would then close the list again instead of being handled by the composer (e.g. discarding a reply). This happened because NavigableList was re-opened on every patch: the useEffect opening the list had `[this.props]` as dependency, and props are a new object on every render. Any unrelated re-render of the composer (e.g. triggered by a late store update) would
Original PR description
Before this commit, the composer suggestion list could re-open right after the user closed it with Escape, and the next press of Escape would then close the list again instead of being handled by the composer (e.g. discarding a reply). This happened because NavigableList was re-opened on every patch: the useEffect opening the list had `[this.props]` as dependency, and props are a new object on every render. Any unrelated re-render of the composer (e.g. triggered by a late store update) would therefore re-open the list, which would then steal the next Escape from the composer. Fix by narrowing the dependency to the content of the options, so the list only opens on mount and when a new set of options arrives. https://runbot.odoo.com/odoo/error/944571 Forward-Port-Of: odoo/odoo#279498 Forward-Port-Of: odoo/odoo#278929
The daily/monthly Inventory Valuation Closing cron currently skips companies using the Perpetual (real_time) valuation method, so the Periodic Valuation frequency setting has no effect for them. The intent of the feature is to keep the inventory valuation continuously updated (e.g. goods received not yet invoiced) whatever the valuation method, so the cron should also post the closing entries for perpetual companies. Remove the real_time exclusion from the cron domain so the configured frequency
Original PR description
The daily/monthly Inventory Valuation Closing cron currently skips companies using the Perpetual (real_time) valuation method, so the Periodic Valuation frequency setting has no effect for them. The intent of the feature is to keep the inventory valuation continuously updated (e.g. goods received not yet invoiced) whatever the valuation method, so the cron should also post the closing entries for perpetual companies. Remove the real_time exclusion from the cron domain so the configured frequency applies to all companies, and skip companies where the closing raises a UserError (e.g. missing valuation journal or account) so one misconfigured company cannot block the cron. Forward-Port-Of: odoo/odoo#278618 Forward-Port-Of: odoo/odoo#276990
Make sure to click on the correct action menu when tryin to delete the selected website page. If we do not specify this we could randomly click on the little gear menu that do not contain the delete option. It's actually already done like this in 19.3 here https://github.com/odoo/odoo/blob/c5d7a6a90be4730e18070826a9394ab10dfc6be8/addons/website/static/tests/tours/page_manager.js#L129 runbot-233357 Forward-Port-Of: odoo/odoo#278503 Forward-Port-Of: odoo/odoo#276897
Original PR description
Make sure to click on the correct action menu when tryin to delete the selected website page. If we do not specify this we could randomly click on the little gear menu that do not contain the delete option. It's actually already done like this in 19.3 here https://github.com/odoo/odoo/blob/c5d7a6a90be4730e18070826a9394ab10dfc6be8/addons/website/static/tests/tours/page_manager.js#L129 runbot-233357 Forward-Port-Of: odoo/odoo#278503 Forward-Port-Of: odoo/odoo#276897
After https://github.com/odoo/odoo/issues/196785; the standard way to build a custom m2o field in JS is to call a method from the Many2one module, instead of extending an object from it. This commit solves issues regarding domain in the view not being passed to the widget opw-6399906 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odo
Original PR description
After https://github.com/odoo/odoo/issues/196785; the standard way to build a custom m2o field in JS is to call a method from the Many2one module, instead of extending an object from it. This commit solves issues regarding domain in the view not being passed to the widget opw-6399906 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278123
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the BOM's form view. 4) Configure the first sequential operation to be blocked by the second 5) Make and confirm an MO using this BOM 6) Uncheck "Operation Dependencies" on the BOM 7) Press "Plan" on the MO, a validation error is thrown stating "You cannot create cyclic dependency." Issue occur
Original PR description
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the…
Steps to produce: 1) Install Manufacturing & enable "Work Order Dependencies" in the settings 2) Make a new BOM with 2 operations 3) Check "Operation Dependencies" in the miscellaneous tab on the BOM's form view. 4) Configure the first sequential operation to be blocked by the second 5) Make and confirm an MO using this BOM 6) Uncheck "Operation Dependencies" on the BOM 7) Press "Plan" on the MO, a validation error is thrown stating "You cannot create cyclic dependency." Issue occurs because after the MO is confirmed the blocked_by_workorder_ids field for mrp.workorder records is set based on the order manually configured on the BOM (operation 1 is blocked by operation 2). After the BOM is edited to have allow_operation_dependencies = false, then Odoo uses the default sequential ordering when planning the operations (operation 2 is blocked by operation 1). Since the old ordering is never cleared, a cycle is created unintentionally. This PR resolves this issue by clearing the blocked_by_workorder_ids field on mrp.workorder records. opw-6334271 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278706 Forward-Port-Of: odoo/odoo#275154
In the `render_task_templates` branch, the `has_template_ancestor` step called `.toList({})`, turning `domain` into a plain list. It was only rebuilt into a `Domain` when a `default_project_id` was in context, so otherwise the trailing `domain.toList({})` threw `TypeError: domain.toList is not a function`. Introduced in 694ea6a2fb60 (odoo/odoo#279015). Fix: drop the premature `.toList({})` so `domain` stays a `Domain` until the single final conversion. Appeared on many clickAll failures
Original PR description
In the `render_task_templates` branch, the `has_template_ancestor` step called `.toList({})`, turning `domain` into a plain list. It was only rebuilt into a `Domain` when a `default_project_id` was in context, so otherwise the trailing `domain.toList({})` threw `TypeError: domain.toList is not a function`.
Introduced in 694ea6a2fb60 (odoo/odoo#279015).
Fix: drop the premature `.toList({})` so `domain` stays a `Domain` until the single final conversion.
Appeared on many clickAll failures assigned to the JS team:
https://runbot.odoo.com/odoo/error/944607## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered. ## Steps to Replicate (runbot v19) 1. Create a route
Original PR description
## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves…
## Problem
When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total.
## Solution
When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered.
## Steps to Replicate (runbot v19)
1. Create a route
- Pull Comp B -> Interco, MTO, Comp B delivery
- Pull Interco -> Comp A, MTS, Comp A receipt
(You can review the test for more info about this route config)
(There is also this video showcasing the issue on runbot: https://drive.google.com/file/d/1YeUie4EhWPyg_RuJkNf40S9zARB4jXWY/view)
2. Attach a product to this new route
3. Create a SO for the product and confirm it
4. You should see 4 pickings, validate the chain
5. The qty_delivered on the sale order is double the demand
opw-6361559
Forward-Port-Of: odoo/odoo#275694## Issue In Attendances > Management, negative overtimes do not appear in the list, even if they need approval from a manager. ## Steps to reproduce 1. Install *Attendances* (`hr_attendance`) 2. In Settings: - Toggle *Attendances from Backend* - Toggle *Absence Management* - Toggle *Display Extra Hours* - Set *Extra Hours Validation* to *Approved by Manager* 3. On an Employee E: - Overtime Ruleset: Default Ruleset 4. In Attendances, create an attendance for emplo
Original PR description
## Issue In Attendances > Management, negative overtimes do not appear in the list, even if they need approval from a manager. ## Steps to reproduce 1. Install *Attendances* (`hr_attendance`) 2. In…
## Issue
In Attendances > Management, negative overtimes do not appear in the list, even if they need approval from a manager.
## Steps to reproduce
1. Install *Attendances* (`hr_attendance`)
2. In Settings:
- Toggle *Attendances from Backend*
- Toggle *Absence Management*
- Toggle *Display Extra Hours*
- Set *Extra Hours Validation* to *Approved by Manager*
3. On an Employee E:
- Overtime Ruleset: Default Ruleset
4. In Attendances, create an attendance for employee E on a day where they are expected to work. The attendance should be shorter than a full day of work (e.g., from 9:00am to 11:00am, which would be -6 hours of overtime if 8 hors are expected).
5. Navigate to Attendances > Management
6. **The attendance created in Step 4 does not appear, even though it should be approved by a manager.**
## Cause
The domain for the `hr_attendance_management_action` is the following:
https://github.com/odoo/odoo/blob/22a94663e4c1672366b7a614943cef843ced3503/addons/hr_attendance/views/hr_attendance_view.xml#L439
The `overtime_hours > 0` condition was added by https://github.com/odoo/odoo/commit/e5067262174725466056e9a6c530439b4845c19b with the intent to remove attendance records with zero extra hours. Instead, it removes all records with less than zero extra hours.
opw-6372360
Forward-Port-Of: odoo/odoo#275643In saas-19.3, a new calendar view was added for the "Monthly Hours" smart button on an Employee record. A test was added to test the flow of creating a time off request through this view. In this test, we attempt to switch to the calendar view. However, in community tests, this was failing as there is no view switcher here. In an enterprise database, you have the option to switch to a Gantt view, so the view switcher will show up. However, this does not exist in community. To remedy this, we sho
Original PR description
In saas-19.3, a new calendar view was added for the "Monthly Hours" smart button on an Employee record. A test was added to test the flow of creating a time off request through this view. In this test, we attempt to switch to the calendar view. However, in community tests, this was failing as there is no view switcher here. In an enterprise database, you have the option to switch to a Gantt view, so the view switcher will show up. However, this does not exist in community. To remedy this, we should ensure that we're in an environment where the view switcher exists first, before trying to change to the calendar view. [runbot-941240](https://runbot.odoo.com/odoo/error/941240?debug=assets) Forward-Port-Of: odoo/odoo#279816
10 changes
Resolved issues and error corrections
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty i
Original PR description
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty in stock is shown for the first variant which is correct. 5- Select 2nd variant. As you see, still 4 available qty is shown which is wrong. As the out-of-stock sale is unchecked, an out-of-stock warning should be shown. Cause and Fix: --- This is due to `isMainProduct` being always False when `product_id` is not set which makes `free_qty` and `out_of_stock` not to be updated. opw-6237602 Forward-Port-Of: odoo/odoo#279572 Forward-Port-Of: odoo/odoo#273104
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal instead of the correct one (Vendor Bills), even though the move_type itself was correct. ### Steps to reproduce the issue: Pre steps: you need to have access to https://iap-services-test.odoo.com/odoo 1. Download Accounting and l10n_it 2. Go to Settings > Companies and set the VAT of IT company
Original PR description
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal…
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal instead of the correct one (Vendor Bills), even though the move_type itself was correct. ### Steps to reproduce the issue: Pre steps: you need to have access to https://iap-services-test.odoo.com/odoo 1. Download Accounting and l10n_it 2. Go to Settings > Companies and set the VAT of IT company the same as the one in the xml 3. Go to Settings > Italian Electronic Invoicing and select Test 4. Go into the code and insert an Exception inside the function _l10n_it_edi_import_invoice after self.move_type = move_type (or create any type of exception from the user interface) 5. Go to IAP service into IT EDI app and see that your company is there as user 6. Click into the record > receive move button > upload your xml > create 7. Go to your DB > Scheduled Actions > filter with IT > IT EDI: Receive invoices from the SdI > Run Manually 8. Go to Journal entries, remove the filter and find your imported bill 9. You can see it was inserted into the Miscellaneous Operations Journal instead of a Vendor Bill Journal ### Cause of the issue: The move is created inside a savepoint context manager, designed so that even if parsing fails, an empty move with the attachment still remains. The problem is that if the exception is raised, the savepoint rollback undoes everything that follows, but the journal was already determined before the correct move_type was known, leaving the move in the wrong default journal. ### Reason to introduce the fix: The fix is needed to ensure that, regardless of where parsing fails, the move's journal is correctly set even if an exception occurs so that it is possible to find the move in the correct section even if not imported correctly. opw-6397712 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279566 Forward-Port-Of: odoo/odoo#278111
Problem: When adding a caption to an image, typing text, then undoing and redoing does not restore the caption content. Cause: The caption used a native `<input>` element whose content is managed by the browser, not the editor's history stack. While redo could restore the input element itself, its content was lost since the editor never tracked it. Solution: Replace the `<input>` with a `contenteditable` `<span>` so the editor manages its content as part of the DOM history, enabling ful
Original PR description
Problem: When adding a caption to an image, typing text, then undoing and redoing does not restore the caption content. Cause: The caption used a native `<input>` element whose content is managed by the browser, not the editor's history stack. While redo could restore the input element itself, its content was lost since the editor never tracked it. Solution: Replace the `<input>` with a `contenteditable` `<span>` so the editor manages its content as part of the DOM history, enabling full undo/redo. Changes: - Use a contenteditable `<span>` instead of `<input>` for caption editing - Prevent pasting HTML inside the span (plain text only) - Limit caption content to 100 characters - Disable power box, toolbar, and paragraph insertion inside the span task-6219868 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279390 Forward-Port-Of: odoo/odoo#267211
### Problem When mass updating the “Analytic Distribution” field on the lines on Analytic Items the values are not timely reflected on the lines. Steps to reproduce the issue: 1. Accounting > Accounting > Analytic Items. 2. Select multiple records (lines) from the list view. 3. Click on the "Analytic Distribution" column for one of the selected lines to mass-update it. 4. Add or adjust a specific analytic account/tag and click away to apply. 5. Click "Update" on the confirmation pop-up.
Original PR description
### Problem When mass updating the “Analytic Distribution” field on the lines on Analytic Items the values are not timely reflected on the lines. Steps to reproduce the issue: 1. Accounting > Accounting > Analytic Items. 2. Select multiple records (lines) from the list view. 3. Click on the "Analytic Distribution" column for one of the selected lines to mass-update it. 4. Add or adjust a specific analytic account/tag and click away to apply. 5. Click "Update" on the confirmation pop-up. 6. The previously existing analytic distribution tags of other plans disappear, showing only the newly updated account/tag. 7. Refresh the page. 8. The "missing" tags reappear alongside the newly updated one. ### Solution We need to trigger a read of the updated values after they are saved on the server side. opw-6045687 Forward-Port-Of: odoo/odoo#275497 Forward-Port-Of: odoo/odoo#261068
After https://github.com/odoo/odoo/issues/196785; the standard way to build a custom m2o field in JS is to call a method from the Many2one module, instead of extending an object from it. This commit solves issues regarding domain in the view not being passed to the widget opw-6399906 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odo
Original PR description
After https://github.com/odoo/odoo/issues/196785; the standard way to build a custom m2o field in JS is to call a method from the Many2one module, instead of extending an object from it. This commit solves issues regarding domain in the view not being passed to the widget opw-6399906 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278123
In saas-19.3, a new calendar view was added for the "Monthly Hours" smart button on an Employee record. A test was added to test the flow of creating a time off request through this view. In this test, we attempt to switch to the calendar view. However, in community tests, this was failing as there is no view switcher here. In an enterprise database, you have the option to switch to a Gantt view, so the view switcher will show up. However, this does not exist in community. To remedy this, we sho
Original PR description
In saas-19.3, a new calendar view was added for the "Monthly Hours" smart button on an Employee record. A test was added to test the flow of creating a time off request through this view. In this test, we attempt to switch to the calendar view. However, in community tests, this was failing as there is no view switcher here. In an enterprise database, you have the option to switch to a Gantt view, so the view switcher will show up. However, this does not exist in community. To remedy this, we should ensure that we're in an environment where the view switcher exists first, before trying to change to the calendar view. [runbot-941240](https://runbot.odoo.com/odoo/error/941240?debug=assets)
In POS receipt and invoice during a B2C transaction, if the vat number was not set/ wrong, QR code was generated. This commit removes the qr code generation when the invoice is in rejected state. Also, in 19.2 in POS we displayed errors after pressing the validation button, this commit brings it back. task-6237427
Original PR description
In POS receipt and invoice during a B2C transaction, if the vat number was not set/ wrong, QR code was generated. This commit removes the qr code generation when the invoice is in rejected state. Also, in 19.2 in POS we displayed errors after pressing the validation button, this commit brings it back. task-6237427
## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered. ## Steps to Replicate (runbot v19) 1. Create a route
Original PR description
## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves…
## Problem
When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total.
## Solution
When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered.
## Steps to Replicate (runbot v19)
1. Create a route
- Pull Comp B -> Interco, MTO, Comp B delivery
- Pull Interco -> Comp A, MTS, Comp A receipt
(You can review the test for more info about this route config)
(There is also this video showcasing the issue on runbot: https://drive.google.com/file/d/1YeUie4EhWPyg_RuJkNf40S9zARB4jXWY/view)
2. Attach a product to this new route
3. Create a SO for the product and confirm it
4. You should see 4 pickings, validate the chain
5. The qty_delivered on the sale order is double the demand
opw-6361559
Forward-Port-Of: odoo/odoo#275694Before this commit: ===================== floating orders could show the same order number twice on the POS receipt: once through the floating order name and once through the tracking number. This happened because the floating order name defaults to the tracking number until a cashier manually sets a custom name. After this commit: =================== The receipt only displays the floating order name when it differs from the tracking number. Task-6394222
Original PR description
Before this commit: ===================== floating orders could show the same order number twice on the POS receipt: once through the floating order name and once through the tracking number. This happened because the floating order name defaults to the tracking number until a cashier manually sets a custom name. After this commit: =================== The receipt only displays the floating order name when it differs from the tracking number. Task-6394222
Miscellaneous changes
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://gith
Original PR description
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://github.com/odoo/odoo/pull/277424 Forward-Port-Of: odoo/odoo#279786 Forward-Port-Of: odoo/odoo#279314
10 changes
Resolved issues and error corrections
**Issue** The package of the selected quant is not proposed as a destination package when adding stock move lines manually before the move line is saved. **Steps to reproduce** - Activate 'Packages' in the settings - Create a tracked product with package - Put 10 units in stock in package `P`. - Create and confirm a sale for 5 units - Open the delivery, make sure the quantity is set to 0 - Click 'Details' - "Add a line" and select the package P -> if you try to select a destination p
Original PR description
**Issue** The package of the selected quant is not proposed as a destination package when adding stock move lines manually before the move line is saved. **Steps to reproduce** - Activate 'Packages'…
**Issue** The package of the selected quant is not proposed as a destination package when adding stock move lines manually before the move line is saved. **Steps to reproduce** - Activate 'Packages' in the settings - Create a tracked product with package - Put 10 units in stock in package `P`. - Create and confirm a sale for 5 units - Open the delivery, make sure the quantity is set to 0 - Click 'Details' - "Add a line" and select the package P -> if you try to select a destination package, the package P is not proposed as it should - save it and reopen 'Details' -> if you try to select a destination package, the package P is now proposed **Cause** The domain of `result_package_id` (destination package) correctly includes `package_id`: https://github.com/odoo/odoo/blob/3a5f7431effd4b2b2eb8ce3eed81aaba42fcd8ea/addons/stock/models/stock_move_line.py#L52-L56 However, before saving, `package_id` is not yet populated into the new `stock.move.line` record. It will only be copied from `quant_id` by `_copy_quant_info()`: https://github.com/odoo/odoo/blob/3a5f7431effd4b2b2eb8ce3eed81aaba42fcd8ea/addons/stock/models/stock_move_line.py#L1016-L1025 which will only be called in the create method, while saving: https://github.com/odoo/odoo/blob/3a5f7431effd4b2b2eb8ce3eed81aaba42fcd8ea/addons/stock/models/stock_move_line.py#L350 opw-6370159 Forward-Port-Of: odoo/odoo#277797
Before this commit, the unread banner could stop showing in a channel until it was left and opened again: - open a channel with unread messages - click "Mark as Read" - read the same channel from another device - receive a new message: the server counter increases, still no banner This happens because an implicit mark as read freezes the local unread state, so the banner stays in place while the user reads. The problem is that it stays frozen even after the banner is gone, and never fol
Original PR description
Before this commit, the unread banner could stop showing in a channel until it was left and opened again: - open a channel with unread messages - click "Mark as Read" - read the same channel from another device - receive a new message: the server counter increases, still no banner This happens because an implicit mark as read freezes the local unread state, so the banner stays in place while the user reads. The problem is that it stays frozen even after the banner is gone, and never follows the server counter again. This commit freezes that state only while something is still unread locally. This also fixes the flaky test "no unread message banner after message is deleted". https://runbot.odoo.com/odoo/error/242776 Forward-Port-Of: odoo/odoo#279601 Forward-Port-Of: odoo/odoo#279195
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, i
Original PR description
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill…
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, it should the port ship to state code but there cases where goods can be transfered to nearby country i.e. Bangladesh, Nepal where good can taken by road from India In that case the state code should be 97 task-6431082 **Second Commit** - [FIX] l10n_in_ewaybill: import/export GSTIN should be URP Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279289
Currently, when using the quick create form to create a calendar.event, if you set up the event for the whole day the show_as field will always be saved as free no matter the value set by the user. Steps to reproduce: ------------------- * Open Calendar app * Select the a slot that corresponds to a whole day * Ensure all day is ticked * Change 'Available' to 'Busy' * Save * Select the slot and select Edit > Observe that the status is showing 'Available' Why the fix: ------------
Original PR description
Currently, when using the quick create form to create a calendar.event, if you set up the event for the whole day the show_as field will always be saved as free no matter the value set by the user.…
Currently, when using the quick create form to create a calendar.event, if you set up the event for the whole day the show_as field will always be saved as free no matter the value set by the user. Steps to reproduce: ------------------- * Open Calendar app * Select the a slot that corresponds to a whole day * Ensure all day is ticked * Change 'Available' to 'Busy' * Save * Select the slot and select Edit > Observe that the status is showing 'Available' Why the fix: ------------ The defaults should be: all day = free, not all day = busy However those values should still remain modifyable. This commit https://github.com/odoo/odoo/commit/62ecdba6dc963ddcdfca9e2e924ae5f6ca34e5de states: > When the user toggles allDay within the edit form, the availability is recomputed (this behavior matches google calendar) For the normal form itself the onchange triggers and behaves as stated. And if the user decides to change the show_as value it will save its choice correctly. For the quick create form, the onchange also triggers correcly. Meaning the velue of show_as is recomputed depending on allday. However upon saving, the value of show_as is bypassed if allday is true but it should keep the value selected by the user. opw-6326575
Steps to reproduce: ------------------- 1. Install hr_holidays. 2. Create a new Time Off Type with "Duration type" set to Hours. 3. Create a time off request. 4. Change the default hour value (e.g., from 12 AM to 10 AM) and observe the formatted value. 5. Switch the user language to Dutch. 6. Create another time off request and change the hour value to 10 AM. Issue: ------ After changing the value to 10 AM, the displayed formatted value remains 12 a.m. Cause: ------ https://gith
Original PR description
Steps to reproduce: ------------------- 1. Install hr_holidays. 2. Create a new Time Off Type with "Duration type" set to Hours. 3. Create a time off request. 4. Change the default hour value (e.g.,…
Steps to reproduce: ------------------- 1. Install hr_holidays. 2. Create a new Time Off Type with "Duration type" set to Hours. 3. Create a time off request. 4. Change the default hour value (e.g., from 12 AM to 10 AM) and observe the formatted value. 5. Switch the user language to Dutch. 6. Create another time off request and change the hour value to 10 AM. Issue: ------ After changing the value to 10 AM, the displayed formatted value remains 12 a.m. Cause: ------ https://github.com/odoo/odoo/blob/c1acf61ab23f231e416354a3504cc3c18170af2c/addons/hr_holidays/static/src/components/float_time_selection/float_time_selection.js#L45-L56 Here, the code attempts to parse the hours and minutes by splitting the already localized formatted string (super.formattedValue) and checking if the formatted value ended with "h" or "m". In Dutch, the localized string ends with "u" instead of "h", causing the string-matching logic to fail silently and default back to 0 (12 a.m.). Example: If we change the value to 10:30 AM, in Dutch the formatted value becomes "10u 30m". Reference: e80750d Solution: --------- Stop parsing the localized formatted string and instead compute hours and minutes directly from the float value stored in the record using `floatToHoursMinutes`, making the formatting independent of translations. opw-6303884 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The amount in words split the total with int(decimal * 100), which truncates. 3989.33 is held in binary as 3989.3299..., so the kuruş/cents came out one short (32 instead of 33). The written amount then disagreed with the numeric total on the same invoice and Nilvera rejects it. Round to the currency precision and round the subunit instead. Task-6383690 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: ---
Original PR description
The amount in words split the total with int(decimal * 100), which truncates. 3989.33 is held in binary as 3989.3299..., so the kuruş/cents came out one short (32 instead of 33). The written amount then disagreed with the numeric total on the same invoice and Nilvera rejects it. Round to the currency precision and round the subunit instead. Task-6383690 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277748 Forward-Port-Of: odoo/odoo#277180
When a format is applied on an unsplittable node, removing it from a wider selection does not dare to touch that format to ensure it won't be split. Because of this, it becomes impossible to remove the format on such nodes. This commit slightly adapts the logic by so that instead of stopping when encountering an unsplittable node, it keeps looking higher in the hierarchy where the format is actually defined. Steps to reproduce: - Go to a "To do" note - Select a word - Apply a style (un
Original PR description
When a format is applied on an unsplittable node, removing it from a wider selection does not dare to touch that format to ensure it won't be split. Because of this, it becomes impossible to remove the format on such nodes. This commit slightly adapts the logic by so that instead of stopping when encountering an unsplittable node, it keeps looking higher in the hierarchy where the format is actually defined. Steps to reproduce: - Go to a "To do" note - Select a word - Apply a style (underscore, strikethrough...) - Type "odoo.com" - Press space to turn it into a link - Select the whole line - Try to remove the style => The style was not removed from the link. task-6322596 Forward-Port-Of: odoo/odoo#279511 Forward-Port-Of: odoo/odoo#273922
Allow to add the district node for the peruvian electronic invoicing. opw-6282314 Forward-Port-Of: odoo/odoo#278201 Forward-Port-Of: odoo/odoo#271364
Original PR description
Allow to add the district node for the peruvian electronic invoicing. opw-6282314 Forward-Port-Of: odoo/odoo#278201 Forward-Port-Of: odoo/odoo#271364
When an internal recipient receives an Out of Office (OOO) notification, the resulting `mail.message` record has `partner_ids` populated with the recipient partner ID, while `outgoing_email_to` is set to `False`. When a second internal user emails the OOO user within the 4-day window, `_notify_thread_with_out_of_office` excutes a search domain with an OR condition: `'|', ('partner_ids', 'in', recipient.ids), ('outgoing_email_to', '=', email_to)` Because `email_to` is `False` for internal p
Original PR description
When an internal recipient receives an Out of Office (OOO) notification, the resulting `mail.message` record has `partner_ids` populated with the recipient partner ID, while `outgoing_email_to` is…
When an internal recipient receives an Out of Office (OOO) notification, the resulting `mail.message` record has `partner_ids` populated with the recipient partner ID, while `outgoing_email_to` is set to `False`.
When a second internal user emails the OOO user within the 4-day window, `_notify_thread_with_out_of_office` excutes a search domain with an OR condition: `'|', ('partner_ids', 'in', recipient.ids), ('outgoing_email_to', '=', email_to)`
Because `email_to` is `False` for internal partners, `('outgoing_email_to', '=', False)` evaluated to `True` against the first recipient's message record. Consequently, the search falsely determined that the second recipient was already notified, suppressing OOO replies for all subsequent contacts across the 4-day window.
## Proposed solution:
We resolve this by dynamically constructing recipient sub-domains conditionally depending if `recipient` or `email_to` are set.
We also extend `test_routing_with_out_of_office` with a corresponding test case.
## How to reproduce:
1. Set up a DB with at least 3 users (User A, User B, User C).
2. Configure User A to be out of office (in user preferences)
3. Go to any chatter/mail.thread while logged as User B and tag User A in a log note. -> triggers OOO message
4. Log as User C, tag User A in a log note. -> BUG: no OOO message because the "4 day" check falsely believes that User C already received a OOO from User A
OPW-6110300
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#277880Step to reproduce: 1.Go to Website ->Edit Mode 2,Add the snippet : 'AI Live Chat' 3.Don't update any Settings -> Hit 'Save' directly ( Don't add any API key for the chatgpt and/or gemini) 4.Search for something 5.Traceback occurs Before this commit: `RedirectWarningDialog` always displayed the redirect button and expected the `action` service to be available. When a `RedirectWarning` was raised from the website, where this service is not provided, the dialog crashed with an OWL error in
Original PR description
Step to reproduce: 1.Go to Website ->Edit Mode 2,Add the snippet : 'AI Live Chat' 3.Don't update any Settings -> Hit 'Save' directly ( Don't add any API key for the chatgpt and/or gemini) 4.Search for something 5.Traceback occurs Before this commit: `RedirectWarningDialog` always displayed the redirect button and expected the `action` service to be available. When a `RedirectWarning` was raised from the website, where this service is not provided, the dialog crashed with an OWL error instead of being displayed. After this commit: the redirect button is only rendered when the `action` service is available, preventing the crash on the website while keeping the existing behavior in the backend. task-6220145 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
13 changes
Resolved issues and error corrections
When selecting a new microphone or camera in the `Voice & Video settings` outside an active meeting, the browser was not asking for permission immediately. This caused Firefox (which enforces per device permissions) to reprompt when the meeting started. This fix pre-authorizes the selected device, so Firefox prompts at selection time rather than when starting a meeting. task-6175062 Forward-Port-Of: odoo/odoo#277730
Original PR description
When selecting a new microphone or camera in the `Voice & Video settings` outside an active meeting, the browser was not asking for permission immediately. This caused Firefox (which enforces per device permissions) to reprompt when the meeting started. This fix pre-authorizes the selected device, so Firefox prompts at selection time rather than when starting a meeting. task-6175062 Forward-Port-Of: odoo/odoo#277730
## Problem `pttExtensionHookService` registers a global `window.addEventListener("message", ...)` handler that reads `data.from` without checking that `data` is defined first: ```js browser.addEventListener("message", ({ data, origin, source }) => { const rtc = env.services["discuss.rtc"]; if ( source !== window || origin !== location.origin || data.from !== "discuss-push-to-talk" || // <- crashes if data is undefined (!rtc && data.type !== "answer-is-
Original PR description
## Problem `pttExtensionHookService` registers a global `window.addEventListener("message", ...)` handler that reads `data.from` without checking that `data` is defined first: ```js…
## Problem
`pttExtensionHookService` registers a global `window.addEventListener("message", ...)`
handler that reads `data.from` without checking that `data` is defined first:
```js
browser.addEventListener("message", ({ data, origin, source }) => {
const rtc = env.services["discuss.rtc"];
if (
source !== window ||
origin !== location.origin ||
data.from !== "discuss-push-to-talk" || // <- crashes if data is undefined
(!rtc && data.type !== "answer-is-enabled")
) {
return;
}
...
```
Any same-window, same-origin `postMessage` sent by an unrelated browser
extension (a common content-script <-> injected-script pattern) can carry
`data === undefined`. The `source !== window` and `origin !== location.origin`
checks only filter out cross-window/cross-origin messages, so a same-origin
message from any other extension reaches this handler and crashes with:
```
TypeError: Cannot read properties of undefined (reading 'from')
```
This surfaces as an uncaught client error on any page with Discuss loaded,
after some time, unrelated to what the user is doing. The Discuss
push-to-talk extension itself does not need to be installed to trigger it,
since the crash happens before checking whether the message actually
originated from that extension.
## Solution
Use optional chaining (`data?.from`) so unrelated same-origin messages with
no `data` are safely ignored instead of crashing.
## Verification
- Reproduced against the live production `web.assets_web.min.js` bundle
(traceback matches exactly).
- Confirmed the bug is still present in the latest `18.0` of both `OCA/OCB`
and `odoo/odoo` (no newer commit touches this file since
`dc58ef1ad904`, which fixes an unrelated issue).
Forward-Port-Of: odoo/odoo#279645
Forward-Port-Of: odoo/odoo#279476Issue: Outstanding credits/debits from a branch don't appear on the main company and inversely. Cause : From odoo/odoo#255875 outstanding credits/debits are limited by company to prevent different company issues on validation. However, this error is raised for `account.move` having different root companies. Which allow move from different branches of the same company. Steps to reproduce: - create a company and a branch - in the main company create a customer payment and valid it - in
Original PR description
Issue: Outstanding credits/debits from a branch don't appear on the main company and inversely. Cause : From odoo/odoo#255875 outstanding credits/debits are limited by company to prevent different company issues on validation. However, this error is raised for `account.move` having different root companies. Which allow move from different branches of the same company. Steps to reproduce: - create a company and a branch - in the main company create a customer payment and valid it - in the branch, create an invoice for the same customer and confirm it Current behavior: - the outstanding payment from the main company doesn't appear on the branch invoice, However, it's possible to reconcile it from the Journal entry view Expected behavior: - the outstanding payment from the main company appears on the branch invoice, opw-6140689 Forward-Port-Of: odoo/odoo#262260
The current implementation of the Peppol XML export incorrectly populates the <cac:InvoicePeriod> nodes with internal deferred entry dates. These dates are intended for the vendor's revenue recognition process, and the customer has nothing to do with these dates. This commit ensures that: - deferred entries are never created when importing vendor bills. - <cac:InvoicePeriod> is no longer exported in invoices (for now). task-6014315 --- I confirm I have signed the CLA and read the
Original PR description
The current implementation of the Peppol XML export incorrectly populates the <cac:InvoicePeriod> nodes with internal deferred entry dates. These dates are intended for the vendor's revenue recognition process, and the customer has nothing to do with these dates. This commit ensures that: - deferred entries are never created when importing vendor bills. - <cac:InvoicePeriod> is no longer exported in invoices (for now). task-6014315 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279164 Forward-Port-Of: odoo/odoo#265796
Restore the previous behavior by calling `checkAccessRight()` without awaiting it during `PosStore.setup()`. This prevents the POS startup from being blocked while the access check is running. This fixes PoS startup and offline fallback tours timing out while waiting for the "Continue with limited functionality" dialog. Runbot Error-[944421](https://runbot.odoo.com/odoo/error/944421) Forward-Port-Of: odoo/odoo#278628
Original PR description
Restore the previous behavior by calling `checkAccessRight()` without awaiting it during `PosStore.setup()`. This prevents the POS startup from being blocked while the access check is running. This fixes PoS startup and offline fallback tours timing out while waiting for the "Continue with limited functionality" dialog. Runbot Error-[944421](https://runbot.odoo.com/odoo/error/944421) Forward-Port-Of: odoo/odoo#278628
This commit fix the regex used in `street_split` to be more complient. Before: address format was "street_name street_number - street_number2" Now, street_number can be in front of street_name. Format is also less strict, allowing multiple numbers in the street_name without skipping the building number. Linked: https://github.com/odoo/enterprise/pull/121674 task-6317758 Forward-Port-Of: odoo/odoo#279777 Forward-Port-Of: odoo/odoo#271855
Original PR description
This commit fix the regex used in `street_split` to be more complient. Before: address format was "street_name street_number - street_number2" Now, street_number can be in front of street_name. Format is also less strict, allowing multiple numbers in the street_name without skipping the building number. Linked: https://github.com/odoo/enterprise/pull/121674 task-6317758 Forward-Port-Of: odoo/odoo#279777 Forward-Port-Of: odoo/odoo#271855
### 1. Prevent tour failure by waiting on the correct loading class Before this PR, the tour introduced in commit [1] and modified in commit [2] could fail non-deterministically because it waited for `o_we_ui_loading` to disappear. However, this class was added with a delay in `operation.js`, allowing the next tour step to run before the loader was shown. After this commit, the tour waits for `o_loading_screen`, which is added immediately and remains visible until the operation finishes. T
Original PR description
### 1. Prevent tour failure by waiting on the correct loading class Before this PR, the tour introduced in commit [1] and modified in commit [2] could fail non-deterministically because it waited for…
### 1. Prevent tour failure by waiting on the correct loading class Before this PR, the tour introduced in commit [1] and modified in commit [2] could fail non-deterministically because it waited for `o_we_ui_loading` to disappear. However, this class was added with a delay in `operation.js`, allowing the next tour step to run before the loader was shown. After this commit, the tour waits for `o_loading_screen`, which is added immediately and remains visible until the operation finishes. This ensures that the tour waits correctly before proceeding. [1]: https://github.com/odoo/odoo/commit/091b8dee407fe30a115d4bb2e96d4d [2]: https://github.com/odoo/odoo/commit/544a03775119021442d66486c24711 **runbot:** [941508](https://runbot.odoo.com/odoo/error/941508) --- ### 2. Prevent tour failure by clicking the "Close" button instead of pressing "Escape" Before this PR, the tour step introduced in commit [1], which pressed the <kbd>Escape</kbd> key to close the Insert Snippet dialog, could fail non-deterministically with the error: > It is not allowed to do action on an element that's below a modal. After this PR, instead of pressing <kbd>Escape</kbd>, the tour clicks the **Close** (`X`) button to close the dialog. This is a more reliable way to close the dialog and prevents the non-deterministic failure of the sync color shape tour. [1]: https://github.com/odoo/odoo/commit/544a03775119021442d66486c24711d **runbot:** [944543](https://runbot.odoo.com/odoo/error/944543) Forward-Port-Of: odoo/odoo#279395 Forward-Port-Of: odoo/odoo#278743
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read properties of undefined`), blocking the user from saving the page. While an upgrade script exists to remap these shapes ([commit https://github.com/odoo/odoo/commit/f348be018f5740a31754494c905ea2b61bb718be](https://github.com/odoo/upgrade/commit/f348be0dcbc63c1f74f742b562509f81767564c0)), ti
Original PR description
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read…
[FIX] html_builder: prevent crash on legacy image shapes When the Website Editor encounters an image shape that does not exist in the registry, it fatally crashes upon saving (`TypeError: Cannot read properties of undefined`), blocking the user from saving the page. While an upgrade script exists to remap these shapes ([commit https://github.com/odoo/odoo/commit/f348be018f5740a31754494c905ea2b61bb718be](https://github.com/odoo/upgrade/commit/f348be0dcbc63c1f74f742b562509f81767564c0)), timeline gaps leave SaaS databases vulnerable. For example, if a client upgraded their database to 17.0 in Feb 2024, they bypassed the migration script merged in Dec 2024. This leaves the legacy shape permanently orphaned inside their modern views. This commit adds a `getImageShape` fallback. Instead of crashing,the editor now defaults to standard values and renders "None" in the UI, allowing the user to select a new shape and save their work. Steps to Reproduce: 1. Install Website. 2. Go to Site -> HTML / CSS Editor. 3. Add `data-shape="web_editor/basic/bsc_organic_2"` to an <img> tag. 4. Click "Edit" to open the Website Builder. 5. Click the image, OR click "Save". 6. JS traceback. [opw-6286044](https://www.odoo.com/odoo/my-support-tasks/6286044?debug=assets) [opw-6291591](https://www.odoo.com/odoo/my-support-tasks/6291591?debug=assets) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270356
## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered. ## Steps to Replicate (runbot v19) 1. Create a route
Original PR description
## Problem When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total. ## Solution When retreiving the relevant moves…
## Problem
When computing qty_delivered on a sale order line, intercompany 'delieveries' from the other company would count towards the delivery total.
## Solution
When retreiving the relevant moves for quantity calculation (_get_outgoing_incoming_moves), we will filter out any moves not belonging to the SOL's company. This will prevent moves from other companies into the interco location from being counted towards qty_delivered.
## Steps to Replicate (runbot v19)
1. Create a route
- Pull Comp B -> Interco, MTO, Comp B delivery
- Pull Interco -> Comp A, MTS, Comp A receipt
(You can review the test for more info about this route config)
(There is also this video showcasing the issue on runbot: https://drive.google.com/file/d/1YeUie4EhWPyg_RuJkNf40S9zARB4jXWY/view)
2. Attach a product to this new route
3. Create a SO for the product and confirm it
4. You should see 4 pickings, validate the chain
5. The qty_delivered on the sale order is double the demand
opw-6361559
Forward-Port-Of: odoo/odoo#275694*:mrp_subcontracting_purchase Issue before this commit: ======================== - In inter-warehouse transfers with `multi-step delivery` and in multi-step manufacturing flows, demand moves were not correctly counted in the monthly demand. - Also, direct transfers to customer and subcontracting locations generated from `orderpoint` were also not counted correctly (when checked before move scheduled on the same day). This resulted in lower monthly demand values than the actual demand and
Original PR description
*:mrp_subcontracting_purchase Issue before this commit: ======================== - In inter-warehouse transfers with `multi-step delivery` and in multi-step manufacturing flows, demand moves were not…
*:mrp_subcontracting_purchase Issue before this commit: ======================== - In inter-warehouse transfers with `multi-step delivery` and in multi-step manufacturing flows, demand moves were not correctly counted in the monthly demand. - Also, direct transfers to customer and subcontracting locations generated from `orderpoint` were also not counted correctly (when checked before move scheduled on the same day). This resulted in lower monthly demand values than the actual demand and could lead to inaccurate purchase planning. Steps to Reproduce: ========================= - Install `purchase_stock` module and enable multi-step routes. - Set the Outgoing Shipments in the warehouse to 2-step/3-step. - Create a second warehouse and configure it to `resupply from another warehouse`. - Create a storable product and assign a vendor. - Create an orderpoint for the product in the second warehouse, set the route to the warehouse resupply route, and trigger the replenishment. - Go to Purchase → Create RFQ for the vendor and open the catalog. Observation: The replenishment transfer demand is not correctly counted in the monthly demand Cause of the issue: ========================= - In [PR](https://github.com/odoo/odoo/pull/244180), the monthly demand move domain was updated to filter out intermediate customer delivery moves using `move_dest_ids.origin_returned_move_id`. However, inter-warehouse replenishment delivery moves also have `move_dest_ids` linked to receipt moves of the other warehouse, but `origin_returned_move_id is not set` since they are not return move Because of this, these valid demand moves were incorrectly excluded from the monthly demand computation. - Also, in inter-warehouse flows with multi-step delivery, `delivery moves` stay in the `waiting state` since they wait for another operation, so they were also not counted. Additionally, `orderpoint-triggered` moves use a `fixed midday scheduled time`, and since monthly demand was computed using the current timestamp as the limit date, same-day moves could be excluded if checked before midday. After This Commit: ========================= - The monthly demand move domain was updated to correctly count inter-warehouse, manufacturing, and subcontracting resupply demand moves while still avoiding inflated demand from intermediate moves. The move state domain was also updated to `include waiting moves` in multi-step flows, and the limit date now uses the full current day so same day moves are counted correctly. Enterprise PR: odoo/enterprise#115944 TaskID-5490137 Forward-Port-Of: odoo/odoo#262435
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no l
Original PR description
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no longer carries a product_id: it only has a free-text. The Factur-X/CII export template rendered ram:Name directly from line.product_id.name with no fallback. For a line without a product, this produced an empty ram:Name element, which cleanup_xml_node then stripped entirely from the XML, leaving only ram:Description. Solution: Fall back to the line's name when there is no product opw-6391121 Forward-Port-Of: odoo/odoo#277418
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, i
Original PR description
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill…
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, it should the port ship to state code but there cases where goods can be transfered to nearby country i.e. Bangladesh, Nepal where good can taken by road from India In that case the state code should be 97 task-6431082 **Second Commit** - [FIX] l10n_in_ewaybill: import/export GSTIN should be URP Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279289
Miscellaneous changes
Simplified version of https://github.com/odoo/odoo/pull/276689 Forward-Port-Of: odoo/odoo#276696
Original PR description
Simplified version of https://github.com/odoo/odoo/pull/276689 Forward-Port-Of: odoo/odoo#276696
10 changes
Resolved issues and error corrections
When updating mail notifications during `mail.mail._send()`, a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state. As `_send()` continues handling the exception, accessing fields: - https://github.com/odoo/odoo/blob/127f1316540ec6cc6880ea3515e6923ba3903bc7/addons/mail/models/mail_mail.py#L816 So, any subsequent SQL query fails with `InFailedSqlTransaction`, masking the original concurrency error. Avoid acces
Original PR description
When updating mail notifications during `mail.mail._send()`, a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state. As `_send()`…
When updating mail notifications during `mail.mail._send()`,
a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state.
As `_send()` continues handling the exception, accessing fields:
- https://github.com/odoo/odoo/blob/127f1316540ec6cc6880ea3515e6923ba3903bc7/addons/mail/models/mail_mail.py#L816
So, any subsequent SQL query fails with
`InFailedSqlTransaction`, masking the original concurrency error.
Avoid accesing to `mail.message_id` with aborted cursor, preserving the original `SerializationFailure`.
A regression test is added to simulate a concurrency failure during
`flush_recordset()` and verify that the cursor is no longer used dirty
The logger for the unittest without the fix is the following:
```log
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "odoo/addons/mail/models/mail_mail.py", line 719, in _send
notifs.flush_recordset(['notification_status', 'failure_type', 'failure_reason'])
File "<string>", line 3, in flush_recordset
File "unittest/mock.py", line 1139, in __call__
return self._mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "unittest/mock.py", line 1143, in _mock_call
return self._execute_mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "unittest/mock.py", line 1204, in _execute_mock_call
result = effect(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/addons/test_mail/tests/test_message_post_concurrent.py", line 93, in mocked_mail_notification_flush_recordset
return original_flush_recordset(self, *vals, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 6788, in flush_recordset
self._flush(fnames)
File "odoo/odoo/models.py", line 6852, in _flush
model.browse(some_ids)._write_multi(vals_list)
File "odoo/odoo/models.py", line 4938, in _write_multi
self.env.execute_query(SQL(
File "odoo/odoo/api.py", line 993, in execute_query
self.cr.execute(query)
File "odoo/odoo/sql_db.py", line 371, in execute
res = self._obj.execute(query, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.SerializationFailure: could not serialize access due to concurrent update
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "odoo/addons/test_mail/tests/test_message_post_concurrent.py", line 107, in test_mail_send_dirty_cursor
mails.send()
File "odoo/addons/mail/models/mail_mail.py", line 652, in send
self.browse(batch_ids)._send(
File "odoo/addons/mail/models/mail_mail.py", line 818, in _send
mail.id, mail.message_id)
^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 1309, in __get__
self.compute_value(recs)
File "odoo/odoo/fields.py", line 1491, in compute_value
records._compute_field_value(self)
File "odoo/odoo/models.py", line 5302, in _compute_field_value
fields.determine(field.compute, self)
File "odoo/odoo/fields.py", line 113, in determine
return needle(records, *args)
^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 710, in _compute_related
record[self.name] = self._process_related(value[self.related_field.name], record.env)
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 7083, in __getitem__
return self._fields[key].__get__(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 1272, in __get__
recs._fetch_field(self)
File "odoo/odoo/models.py", line 4120, in _fetch_field
self.fetch(fnames)
File "odoo/addons/mail/models/mail_message.py", line 756, in fetch
return super().fetch(field_names)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 4158, in fetch
fetched = self._fetch_query(query, fields_to_fetch)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 4245, in _fetch_query
rows = self.env.execute_query(query.select(*sql_terms))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/api.py", line 993, in execute_query
self.cr.execute(query)
File "odoo/odoo/sql_db.py", line 371, in execute
res = self._obj.execute(query, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block
```
Real error in production:
```log
2023-04-15 10:43:14,978 106451 ERROR my_db odoo.sql_db: bad query: UPDATE "mail_notification" SET "failure_reason" = "__tmp"."failure_reason"::text, "failure_type" = "__tmp"."failure_type"::VARCHAR, "notification_status" = "__tmp"."notification_status"::VARCHAR FROM (VALUES (4426629, 'Error without exception. Probably due to concurrent access update of notification records. Please see with an administrator.', 'unknown', 'exception')) AS "__tmp"("id", "failure_reason", "failure_type", "notification_status") WHERE "mail_notification"."id" = "__tmp"."id" ERROR: could not serialize access due to concurrent update
```
```log
2023-04-14 10:43:14,978 106451 ERROR my_db odoo.sql_db: bad query: UPDATE "mail_mail" SET "failure_reason"='Error without exception. Probably due do sending an email without computed recipients.',"headers"='{''X-SMTPAPI'': ''{"ip_pool": "Transactional"}'', ''X-Odoo-Objects'': ''sale.order-1436960''}',"state"='exception',"write_uid"=1,"write_date"=(now() at time zone 'UTC') WHERE id IN (2548540)
ERROR: current transaction is aborted, commands ignored until end of transaction block
```
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
# UPDATE 2026-07-22
The reviewer requested to remove the large docstring
For record, the docstring was
```python
"""Reproduces a concurrency scenario where `mail_mail._send()` fails with a PSQL SerializationFailure after
flushing `mail.notification` records. After such a failure, the cursor is left in an aborted
(`InFailedSqlTransaction`) state, so any further SQL access (e.g. reading `mail.message_id` like
https://github.com/odoo/odoo/blob/127f1316540ec6cc68/addons/mail/models/mail_mail.py#L816)
would raise a new error masking the original SerializationFailure.
Setup:
- Uses a separate `cursor()` to create and commit a message with its `mail.mail` and `mail.notification`
records, so they are visible to a second, concurrent transaction.
Concurrency simulation:
- `MailNotification.flush_recordset` is patched so that, right before the real flush runs, a second cursor
updates the same `mail.notification` records (`failure_reason`). This forces PSQL to raise a
SerializationFailure when the original transaction tries to flush those rows.
Assertions:
- `SerializationFailure` is raised confirming the concurrency conflict.
- `mail_mail._send()` logs the expected error message containing the mail `id` and `message-id`
Cleanup: created records are unlinked in `finally`
"""
```
# UPDATE 2026-07-23
The reviewer requested to remove the unittest
For record, the unittest was
```diff
diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py
index 53dd5b9eec52..46a3958a5bff 100644
--- a/addons/test_mail/tests/test_message_post.py
+++ b/addons/test_mail/tests/test_message_post.py
@@ -7,17 +7,21 @@ from datetime import datetime, timedelta
from freezegun import freeze_time
from itertools import product
from markupsafe import escape, Markup
+from psycopg2.errorcodes import SERIALIZATION_FAILURE as SERIALIZATION_FAILURE_CODE
+from psycopg2.errors import SerializationFailure
from unittest.mock import patch
-from odoo import tools
+from odoo import SUPERUSER_ID, api, tools
from odoo.addons.base.tests.test_ir_cron import CronMixinCase
-from odoo.addons.mail.tests.common import mail_new_test_user, MailCommon
+from odoo.addons.mail.models.mail_notification import MailNotification
+from odoo.addons.mail.tests.common import mail_new_test_user, MailCommon, MockEmail
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE_PLAINTEXT
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
from odoo.addons.test_mail.tests.common import TestRecipients
from odoo.api import call_kw
from odoo.exceptions import AccessError
-from odoo.tests import tagged
+from odoo.modules.registry import Registry
+from odoo.tests import TransactionCase, get_db_name, tagged
from odoo.tools import mute_logger, formataddr
from odoo.tests.common import users
@@ -2244,3 +2248,49 @@ class TestMessagePostLang(MailCommon, TestRecipients):
self.assertIn('html lang="es_ES"', email['body'])
else:
self.assertIn('html lang="en_US"', email['body'])
+
+
+@tagged('database_breaking')
+class TestMessagePostConcurrent(MockEmail, TransactionCase):
+ """Mail concurrency edge cases that require real, separately committed transactions
+ instead of the usual rollback-based TransactionCase isolation.
+ """
+
+ def test_mail_send_dirty_cursor(self):
+ """Reproduces SerializationFailure `mail_mail._send()` fails,
+ the cursor is left in an aborted state, so any further SQL access would raise a new error
+ (e.g. reading `mail.message_id` like
+ https://github.com/odoo/odoo/blob/127f1316540ec6cc68/addons/mail/models/mail_mail.py#L816)
+ """
+ original_flush_recordset = MailNotification.flush_recordset
+
+ def mocked_mail_notification_flush_recordset(self, *args, **kwargs):
+ with Registry(get_db_name()).cursor() as cr:
+ cr.execute('UPDATE mail_notification SET failure_reason = %s WHERE id IN %s', ('Forced Concurrent Update', tuple(self.ids)))
+ return original_flush_recordset(self, *args, **kwargs)
+
+ recs2unlink = []
+ with Registry(get_db_name()).cursor() as cr:
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ partner = env.ref('base.user_admin').partner_id
+ try:
+ message = partner.message_post(body='Hello', message_type='comment', partner_ids=[partner.id], mail_auto_delete=False, force_send=False)
+ notifs = env['mail.notification'].search([('notification_type', '=', 'email'), ('mail_mail_id', 'in', message.mail_ids.ids)])
+ self.assertTrue(notifs)
+ mails = message.mail_ids
+ recs2unlink.extend([notifs, mails, message])
+ cr.commit()
+
+ mails = self.env[mails._name].browse(mails.ids)
+ with (
+ mute_logger('odoo.sql_db'), self.assertRaises(SerializationFailure) as exc, self.mock_mail_gateway(),
+ patch(f'{MailNotification.__module__}.{MailNotification.__name__}.flush_recordset', autospec=True, side_effect=mocked_mail_notification_flush_recordset),
+ self.assertLogs('odoo.addons.mail.models.mail_mail', level='ERROR') as log_capture,
+ ):
+ mails.send()
+ finally:
+ for rec2unlink in recs2unlink:
+ env[rec2unlink._name].browse(rec2unlink.ids).unlink()
+
+ self.assertEqual(exc.exception.pgcode, SERIALIZATION_FAILURE_CODE)
+ self.assertIn(f'Exception while processing mail with ID {mails.id} and Msg-Id \'{mails.message_id}\'.', [record.message for record in log_capture.records])
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274089**PROBLEM** Before this PR, there was no way to handle invoices sent to clients depending from a JST/LGU (local government unit). This PR add support for it. opw-6095581 Forward-Port-Of: odoo/odoo#260367
Original PR description
**PROBLEM** Before this PR, there was no way to handle invoices sent to clients depending from a JST/LGU (local government unit). This PR add support for it. opw-6095581 Forward-Port-Of: odoo/odoo#260367
Issue: The MyInvois payload computed `cbc:PrepaidAmount` as `amount_total - amount_residual`, treating every reconciled payment as a prepayment. A payment made on or after the invoice date is an ordinary settlement, not a deposit, but the code recognized it as one anyway. The values reported for `PrepaidAmount` were wrong regardless of the invoice date, and for a fully paid invoice this also collapsed `PayableAmount` to 0.00, which LHDN rejects. Root Cause: LHDN only considers a reconciled
Original PR description
Issue: The MyInvois payload computed `cbc:PrepaidAmount` as `amount_total - amount_residual`, treating every reconciled payment as a prepayment. A payment made on or after the invoice date is an…
Issue: The MyInvois payload computed `cbc:PrepaidAmount` as `amount_total - amount_residual`, treating every reconciled payment as a prepayment. A payment made on or after the invoice date is an ordinary settlement, not a deposit, but the code recognized it as one anyway. The values reported for `PrepaidAmount` were wrong regardless of the invoice date, and for a fully paid invoice this also collapsed `PayableAmount` to 0.00, which LHDN rejects. Root Cause: LHDN only considers a reconciled payment a genuine deposit if it was received before the invoice date. The code applied no date condition at all, so any payment reconciled against the invoice was added to `PrepaidAmount` and reduced `PayableAmount` accordingly. Fix: Only sum reconciled payment partials whose date is strictly earlier than the invoice date as prepaid, so regular payments are no longer misclassified as deposits. As a safety net, if the valid prepaid sum still covers the full invoice amount (e.g. a full advance payment), reset it to 0 so `PayableAmount` always reflects the full amount_total instead of being reported as 0. Also omit the `PrepaidPayment` node entirely when there is no genuine prepayment, rather than emitting it with a 0.00 amount. [Task-6404296](https://www.odoo.com/odoo/my-tasks/6404296) Forward-Port-Of: odoo/odoo#279035 Forward-Port-Of: odoo/odoo#278010
Use "VAT" instead of "TAX" for the default UK tax groups. Also set "Subtotal" as their preceding subtotal label so that it replaces "Untaxed Amount". task-[6413495](https://www.odoo.com/odoo/project/967/tasks/6413495) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278351
Original PR description
Use "VAT" instead of "TAX" for the default UK tax groups. Also set "Subtotal" as their preceding subtotal label so that it replaces "Untaxed Amount". task-[6413495](https://www.odoo.com/odoo/project/967/tasks/6413495) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278351
Issue: Outstanding credits/debits from a branch don't appear on the main company and inversely. Cause : From odoo/odoo#255875 outstanding credits/debits are limited by company to prevent different company issues on validation. However, this error is raised for `account.move` having different root companies. Which allow move from different branches of the same company. Steps to reproduce: - create a company and a branch - in the main company create a customer payment and valid it - in
Original PR description
Issue: Outstanding credits/debits from a branch don't appear on the main company and inversely. Cause : From odoo/odoo#255875 outstanding credits/debits are limited by company to prevent different company issues on validation. However, this error is raised for `account.move` having different root companies. Which allow move from different branches of the same company. Steps to reproduce: - create a company and a branch - in the main company create a customer payment and valid it - in the branch, create an invoice for the same customer and confirm it Current behavior: - the outstanding payment from the main company doesn't appear on the branch invoice, However, it's possible to reconcile it from the Journal entry view Expected behavior: - the outstanding payment from the main company appears on the branch invoice, opw-6140689 Forward-Port-Of: odoo/odoo#262260
The AEAT provides different endpoints depending on the type of digital certificate you use. A personal certificate or a seal certificate. The module used always the standard endpoint regardless of the certificate type. Causing authentication failures when a sello certificate was configured. This fix detects the certificate type by checking for the presence of a GIVEN_NAME attribute. task-6169935 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-p
Original PR description
The AEAT provides different endpoints depending on the type of digital certificate you use. A personal certificate or a seal certificate. The module used always the standard endpoint regardless of the certificate type. Causing authentication failures when a sello certificate was configured. This fix detects the certificate type by checking for the presence of a GIVEN_NAME attribute. task-6169935 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274726 Forward-Port-Of: odoo/odoo#271080
Steps to reproduce: 1. In an `en_US` database, install the Arabic (`ar_001`) language and set it as the website's default language. 2. Add a `blog.post` dynamic snippet to a page and select it. 3. Open the snippet options. 4. Notice that the Filter dropdown is displayed in Arabic instead of English. The RPC fetching the available snippet filters targets the `website=True` `/website/snippet/options_filters` route. During the request initialization, website routes inherit the fro
Original PR description
Steps to reproduce: 1. In an `en_US` database, install the Arabic (`ar_001`) language and set it as the website's default language. 2. Add a `blog.post` dynamic snippet to a page and select it. 3. Open the snippet options. 4. Notice that the Filter dropdown is displayed in Arabic instead of English. The RPC fetching the available snippet filters targets the `website=True` `/website/snippet/options_filters` route. During the request initialization, website routes inherit the frontend request language (see: `frontend_pre_dispatch()`), so the ORM context lang is set to the website language. As a result, translated fields such as name are read in that language. Force `request.env.user.lang` in the context when fetching the filters since their names should be displayed in the editor's preferred language. task-5979540 Forward-Port-Of: odoo/odoo#275390
Before this commit: Deleting a record that uses a filterable selection field with `whitelist_fname` raises a traceback because the record field data becomes undefined during deletion. Steps to reproduce: 1. Install Belgium Accounting (l10n_be). 2. Create a contact and set a Peppol scheme and endpoint. 3. Delete the contact -> a traceback is raised. After this commit: The selection field safely handles undefined record field data during record deletion without causing an error. no-t
Original PR description
Before this commit: Deleting a record that uses a filterable selection field with `whitelist_fname` raises a traceback because the record field data becomes undefined during deletion. Steps to reproduce: 1. Install Belgium Accounting (l10n_be). 2. Create a contact and set a Peppol scheme and endpoint. 3. Delete the contact -> a traceback is raised. After this commit: The selection field safely handles undefined record field data during record deletion without causing an error. no-task Forward-Port-Of: odoo/odoo#278808
Miscellaneous changes
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior was previously introduced to match the content of the composer body/subject to the recipient language. If there was only one language among the recipients it automatically adapted the template and changed the rendered language (which also refreshed the content). This logic was trigge
Original PR description
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior…
**Steps to reproduce:** - Install Survey app - Create a survey - Share as e-mail - Add some text in the body/subject - Modify recipients - The added text is automatically reset **Issue:** Behavior was previously introduced to match the content of the composer body/subject to the recipient language. If there was only one language among the recipients it automatically adapted the template and changed the rendered language (which also refreshed the content). This logic was triggered by a depends on `partner_ids` and triggered the compute on every recipient changes which led to the subject/body reset. **Fix:** Revert commit: https://github.com/odoo/odoo/commit/b7bbb7b21f4848323666230b518cad9459726f67 in 18.0+ Also adapt commit: https://github.com/odoo/odoo/commit/c6f19e89cb6019e7dbaadbc7427fbb6ddd5661ed to avoid mixed language in resulting mail when the composer was modified We could also try to prevent the compute when the subject or body is already modified instead of removing its logic. opw-6020245 Forward-Port-Of: odoo/odoo#254090
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279961
Original PR description
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279961
17 changes
New functionality added to Odoo
Adds the Slovak VIES Summary Statement for reporting intra-EU goods, services, and triangular transactions to the Slovak Financial Administration. Users can generate the required XML file directly from a dedicated submission wizard, improving compliance workflows for Slovak VAT reporting.
Original PR description
This commit introduces the VIES Summary Statement (Súhrnný výkaz DPH) as required by the Slovak Financial Administration. The report is built on top of the generic EC Sales List engine and aggregates intra-community supplies by customer VAT number and transaction type. It covers intra-community supplies of goods, services and triangular transactions. Also adds a dedicated return type and a submission wizard with direct XML download. Section II (call-off stock transactions) is exported as empty records, as the required call-off stock events are not tracked by standard Odoo data. Documentation: https://www.financnasprava.sk/sk/podnikatelia/dane/dan-z-pridanej-hodnoty/suhrnny-vykaz-dph see https://github.com/odoo/odoo/pull/271293 task-6041417
Enhancements to existing features
Clicking an unlocated record in the map side panel now opens its form in a dialog instead of taking over the full screen. This keeps users in the map context while they review or edit the record, making Planning map workflows smoother.
Original PR description
Before this commit, clicking on an unlocated record opens the form in fullscreen, which leads to a loss of context. To avoid this behavior, now when a user clicks an unlocated record in the side panel, it's open the form record inside a dialog. Steps to reproduce: - Open Planning - Open the menu Maps > By Resource - Click on an unlocated item on the side panel. task-6369589
The Executive Summary report’s cash row now opens the new cashflow analysis, helping users move directly from headline cash figures to deeper cashflow details. Bank reconciliation labels were also clarified from deposits and payments to cash in and cash out, making the wording easier to understand.
Original PR description
This commit makes the cash row action, of the executive summary report, redirect to the newly added cashflow analysis. task-6373692
The appointment scheduling module was adjusted to stay aligned with related changes in the community version. This helps keep the enterprise feature consistent and easier to maintain, with minimal expected impact for users.
Original PR description
Adjust override definition based on community PR. task-6349421
New appointment types will now automatically use a standard email reminder sent 3 hours before the appointment. This removes manual default reminder configuration, simplifying setup while ensuring external participants still receive reminders.
Original PR description
Removing the possibility to choose the alarm(s) set by default on new appointment types. Using a field on the alarm model for that has been considered a bit weird and overkill. Simplifying things and alarm form by always setting the "email 3 hours" alarm as default for every new appointment types. Using an alarm of type "email" to make sure external users also get the reminder. Also removing the alarm value from the "_prepare_calendar_event_values" method on appointment type to let the compute handle the propagation. Task-6209598
VoIP activity scheduling was simplified behind the scenes so related contacts, leads, tickets, employees, tasks, sales orders, and subscriptions stay aligned more reliably. This reduces maintenance complexity and helps keep the scheduling experience consistent across VoIP-connected business apps.
Original PR description
… fields Remove all @api.onchange in favor of compute/inverse fields for MailActivitySchedule wizards across voip and voip_* modules. Key changes: · Rename res_model_field_selection → res_model_selection, use pure model name keys (e.g. "crm.lead") instead of "model/field" format · Rename _get_res_model_field_selection → _selection_res_model · Convert contact_id, lead_id, ticket_id, employee_id, task_id, sale_order_id, and subscription_id to compute + inverse fields · Replace all onchange logic with compute (default values) and inverse (res_ids sync) methods
Bank statement reconciliation now includes opening balance entries in the reconciliation chain, helping balances stay consistent from the start. The statement error experience has also been improved so accounting users can more easily understand and resolve issues.
Original PR description
This task improves the consistency of the bank statements by adding the opening balance move to the reconciliation chain as well as improving the UX of the statement errors Task ID: 5435413
Resolved issues and error corrections
Attachments now appear consistently in the bank reconciliation list view and kanban view. This helps users see the relevant supporting documents in the right place, reducing confusion during reconciliation.
Original PR description
The aim of this commit is showing the same attachment in the bank reconciliation list view than in the kanban view. Before this commit, the field used to display the attachments was attachment_ids, this field were a related on the attachment_ids from account.move. This fix, removes the related to only keep a domain on the One2Many field. Thanks to the relational database, Odoo is giving us the right attachments when we want to display the field. task-6153002 Forward-Port-Of: odoo/enterprise#124914 Forward-Port-Of: odoo/enterprise#117245
Imported Shopee and Lazada orders now better match the totals shown on each marketplace, including discounts, vouchers, coins, shipping fees, taxes, and minor rounding differences. This reduces reconciliation issues and helps businesses trust that Odoo sales orders reflect the amounts customers paid on the platform.
Original PR description
Marketplace orders with discount lines could not match the platform total: Odoo accumulated a small rounding residue vs Shopee's total_amount / Lazada's order price. sale_shopee ----------- Changes:…
Marketplace orders with discount lines could not match the platform total: Odoo accumulated a small rounding residue vs Shopee's total_amount / Lazada's order price. sale_shopee ----------- Changes: - Fetch buyer-side escrow amounts via `_fetch_order_income` and pass them through `self.env.context` (`order_income`). - Build item lines from the buyer-paid item price with `discount=0` and a recomputed tax-exclusive `price_unit`. - Distribute order-level discounts (seller/platform vouchers and coins) as dedicated negative lines per product tax group via `_prepare_discount_lines_values`. - Append a shipping line from `buyer_paid_shipping_fee` with fiscal-position mapped taxes. - Reconcile any leftover residue with `_adjust_order_total` using a single tax-free amount-adjustment line. - Register `default_discount_product` and configure it on upgrade (v1.1). sale_lazada ----------- - Port the same reconciliation model as shopee: reconciled line specs, discount=0 with discounted unit from paid_price, shipping line from shipping_fee, order-level "Discount line" distributed at order-level. task-6112062 Forward-Port-Of: odoo/enterprise#126004 Forward-Port-Of: odoo/enterprise#117561
Users can now click and edit custom fields directly in the Documents list view. This removes an extra step and makes fields added through Studio behave like standard editable fields.
Original PR description
**Description of the issue/feature this PR addresses:** When adding a custom field (e.g., via Studio) to the Documents list view, clicking the cell directly does not trigger inline edit mode. The…
**Description of the issue/feature this PR addresses:** When adding a custom field (e.g., via Studio) to the Documents list view, clicking the cell directly does not trigger inline edit mode. The user has to first click a standard editable field (like "Owner") to put the row into edit mode before they can modify the custom field. This occurs because we use a hardcoded whitelist (`editableColumns`) of standard fields allowed to trigger edit mode. Custom fields (`x_`) are missing from this static list. This commit resolves the issue by dynamically injecting visible, non-readonly custom fields into the `editableColumns` whitelist. This allows user-created fields to be edited inline as expected. **Steps to reproduce:** - Documents > Studio > List view > Add any field that accepts user input (e.g. Text/char) > save/exit - In the same Documents list view > select a row > click the cell belonging to the newly created field > observe that the row does not enter edit mode - In the same Documents list view > select a row > click a standard editable cell, then click the cell belonging to our newly created field > observe that this then allows us to edit our field **Current behavior before PR:** - Custom fields do not trigger inline edit mode **Desired behavior after PR is merged:** - Custom fields trigger inline edit mode opw-6378102 Forward-Port-Of: odoo/enterprise#125378 Forward-Port-Of: odoo/enterprise#125239
The ESG module now declares a required dependency that it was already using behind the scenes. This prevents automated tests and module loading checks from failing, with no expected change for everyday users.
Original PR description
Before this commit, the esg cog menu imports @base_import/import_records/import_records while esg does not depend on base_import. This goes unnoticed in the backend, where every installed module lands in the same bundle, but a Hoot test file only loads the modules of the dependency closure of its addon, so the first test suite added to esg dies on "error while registering suite". This commit adds the missing dependency. base_import is auto installed on top of web, so it is already there in every database.
This change makes a Swedish Point of Sale test finish its order creation consistently. It reduces random test failures, helping keep automated checks stable without changing day-to-day user behavior.
Original PR description
## Issue The tour `test_l10n_se_pos_01` does not consistently create its `pos_order`. This leads to the following assert failing:…
## Issue The tour `test_l10n_se_pos_01` does not consistently create its `pos_order`. This leads to the following assert failing: https://github.com/odoo/enterprise/blob/0f6f6fac892bc8cec477160a790d52fbf053be99/l10n_se_pos/tests/test_se_pos.py#L40-L42 ## Steps to reproduce 1. Install `l10n_se_pos` 2. Run the test `test_l10n_se_pos_01` 3. **The test fails non-deterministically** ## Fix We use `clickNextOrder()` at the end of the tour to ensure the creation of the order, like other tests already do (e.g., [FinishResidualOrder](https://github.com/odoo/odoo/blob/7fc645e5a3fad9255432e7cc68d47bd0971d3d77/addons/pos_restaurant/static/tests/tours/pos_restaurant_tour.js#L676-L677), [test_name_preset_skip_screen](https://github.com/odoo/odoo/blob/7fc645e5a3fad9255432e7cc68d47bd0971d3d77/addons/pos_restaurant/static/tests/tours/pos_restaurant_tour.js#L1333-L1334), [PosOrderCreationTourPdis](https://github.com/odoo/enterprise/blob/08d5172a8c3310af0c51e18a281c544f83f5aed7/pos_enterprise/static/tests/tours/point_of_sale/pos_tour.js#L141-L142), ...). runbot-238568 Forward-Port-Of: odoo/enterprise#125672
This fix ensures Swiss payroll amounts are rounded directly to the required 0.05 precision instead of using an unreliable manual workaround. It prevents tiny calculation differences from affecting salary declarations and helps payroll reports stay accurate and consistent.
Original PR description
In multiple places within l10n_ch_hr_payroll_elm we use float_round to a precision of 0.01 but then manually round to 0.05 precision. 1. Open a python terminal 2. Enter 1000 % 0.05 >= 0.025 3. See this results to true, even though it shouldn't Fix this by using float_round with a precision of 0.05 instead. https://github.com/odoo/enterprise/blob/7f9cd01ff3dd470b06ae176982fd042243be8f3c/l10n_ch_hr_payroll_elm/models/hr_payslip.py#L102-L105 The change to `ema_declaration.json` is needed as previously it was expected that there was a small difference between salary over the months due to the odd rounding (a difference of like 0.000000000001). Adding the new rounding makes the values equal and test_ema_declaration_2023_01 would fail due to changeSalary no longer being in the computed dict. All the way to master! opw-6322937 Forward-Port-Of: odoo/enterprise#121401
The timesheet overtime indicator now keeps the selected day-based display even when users switch to another language. This prevents confusing changes from days back to hours for multilingual teams using Timesheets.
Original PR description
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet…
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet in the past week for this employee (e.g. 8 hours). 4. Observe the overtime indicator for the employee (it shows 32 hrs) (Click the left arrow to display it). 5. Change the timesheet encoding unit to "Days / Half-Days". 6. Return to Timesheets and observe the overtime indicator (it now correctly shows 4 days). 7. Install a language other than English (e.g. French). 8. Return to Timesheets and observe the overtime indicator again. Issue: -------- The remaining time value changes unexpectedly and displays 32 hours instead of 4 days. Cause: --------- In `get_timesheet_and_working_hours_for_employees`, the code determines whether the timesheet UoM is expressed in days by comparing the UoM name with the string `"days"`. Since UoM names are translatable, this comparison becomes invalid when the user language changes (e.g. `"jours"` in French), causing the logic to skip the day conversion and return values in hours instead. https://github.com/odoo/enterprise/blob/c48290e90fdeadf4f9ca8c44b035e601e7ed380a/timesheet_grid/models/hr_employee.py#L165-L169 Solution: ----------- Compare the timesheet UoM record with the day UoM record directly instead of relying on translated string values. see commit: https://github.com/odoo/enterprise/commit/5fbf194c5056566453a124812fd2614edfe19a82 opw-6279133 Forward-Port-Of: odoo/enterprise#126256 Forward-Port-Of: odoo/enterprise#120595
The batch payment screen now refreshes the online payment status when users move between records. This prevents outdated status information from being shown, helping users see whether each payment has been signed or is still pending.
Original PR description
To display the `payment_online_status` field, we use a widget called `account_online_payment_refresh_button`. The issue is that the widget don't update the field value when switching from one record to another. Steps to reproduce: 1. Create 2 batch payments 2. Do a payment initiation with the first one, and sign it 3. Do another payment initiation with the second one, but don't sign it. 4. Open 1 batch, and try to switch records with the pager 5. You should see the value is not updated task-6420585 Forward-Port-Of: odoo/enterprise#126278 Forward-Port-Of: odoo/enterprise#125643
Code cleanup and technical improvements
This update standardizes how several enterprise modules describe the data they send to the mail interface, making future changes safer and easier to maintain. It also fixes an issue where one AI prompt setting was sent under the wrong name and therefore was not used by the client.
Original PR description
Enterprise counterpart of "[REF] mail, *: declare the store fields a payload fills", which explains the why and the placement rule. This also fixes are_prompt_from_local_storage, sent under a name the client never reads, so the value landed nowhere. https://github.com/odoo/odoo/pull/279131
The Point of Sale routing layer was reworked to use the newer plugin approach across several related POS modules. This is an internal modernization that helps keep POS screens, appointments, IoT, restaurant, settlement, and delivery integrations compatible with the latest frontend framework.
Original PR description
Convert router service owl2 to owl3 plugin.
11 changes
Enhancements to existing features
Before this commit, contains() and its variants gave the client 3 seconds, and the bus helpers 2 seconds. The problem is that the first wait after openDiscuss pays for the whole mount, /mail/data and /discuss/channel/messages. Measured from openDiscuss resolving to the message being in the DOM: - 250 to 460ms on an idle machine; - 867 to 5258ms over 10 runs with the CPU throttled 4x, which is what a busy runbot looks like, 3 of the 10 over 2 seconds; - 1474 to 6912ms with the CPU throttl
Original PR description
Before this commit, contains() and its variants gave the client 3 seconds, and the bus helpers 2 seconds. The problem is that the first wait after openDiscuss pays for the whole mount, /mail/data and…
Before this commit, contains() and its variants gave the client 3 seconds, and the bus helpers 2 seconds. The problem is that the first wait after openDiscuss pays for the whole mount, /mail/data and /discuss/channel/messages. Measured from openDiscuss resolving to the message being in the DOM:
- 250 to 460ms on an idle machine;
- 867 to 5258ms over 10 runs with the CPU throttled 4x, which is what a
busy runbot looks like, 3 of the 10 over 2 seconds;
- 1474 to 6912ms with the CPU throttled 6x, 5 of 6 over 3 seconds.
"Reactions are ordered by id" fails 1 run in 60 at 4x for that reason.
Note that a longer timeout costs nothing on a green build: the timer is cleared as soon as the element is there, so it only delays the report of a test that was going to fail anyway.
This commit raises both to 10 seconds, the delay a tour step already gets in macro.js. test_js.py runs the presets with timeout=15000, so hoot fails the test itself at 15 seconds and 10 leaves room for the rest of the test.
This should also close most of the open runbot errors shaped like:
Failed to find x of "..." (Timeout of 3 seconds). Found 0 instead.
The element does show up in those, just after the wait gave up.
https://runbot.odoo.com/odoo/error/944188
web companion https://github.com/odoo/odoo/pull/279984Slovak entities are identified by three separate numbers: the company registry number (IČO), the income tax ID (DIČ) and the VAT number (IČ DPH). Odoo already stores IČO in company_registry and IČ DPH in vat, both on res.partner. DIČ is only defined on res.company, so it cannot be recorded for customers or vendors at all. This becomes a problem for the upcoming Peppol support in Slovakia. Slovak participants are identified on the network by EAS scheme 0245, which carries the DIČ. A field that
Original PR description
Slovak entities are identified by three separate numbers: the company registry number (IČO), the income tax ID (DIČ) and the VAT number (IČ DPH). Odoo already stores IČO in company_registry and IČ DPH in vat, both on res.partner. DIČ is only defined on res.company, so it cannot be recorded for customers or vendors at all. This becomes a problem for the upcoming Peppol support in Slovakia. Slovak participants are identified on the network by EAS scheme 0245, which carries the DIČ. A field that only exists on res.company therefore cannot be used for it, neither for the sender nor for the recipient. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Resolved issues and error corrections
**Description of the issue/feature this PR addresses:** When adding a product with an analytic distribution to a Sales Order that is already linked to a project, the product's analytic distribution is not automatically applied to the order line. This occurs due to a previous commit that attempted to protect manually entered analytic distributions from being overwritten when an SO is confirmed and a project is generated. To do this, the old code filtered out any non-empty project lines and
Original PR description
**Description of the issue/feature this PR addresses:** When adding a product with an analytic distribution to a Sales Order that is already linked to a project, the product's analytic distribution…
**Description of the issue/feature this PR addresses:** When adding a product with an analytic distribution to a Sales Order that is already linked to a project, the product's analytic distribution is not automatically applied to the order line. This occurs due to a previous commit that attempted to protect manually entered analytic distributions from being overwritten when an SO is confirmed and a project is generated. To do this, the old code filtered out any non-empty project lines and bypassed calling `super()` on them. Consequently, if a line already had an analytic distribution (such as inheriting the project's), the system would skip computing the product's specific distribution rules entirely. This commit resolves the issue by reverting that change, ensuring the base compute method is always called so product-based rules execute correctly. While this means manual analytic entries added before the compute trigger might be overwritten, there is no perfect solution to prevent losing both manual and product distributions. As concluded with the Product Owner in a similar PR for Purchase Orders, we prioritize keeping the product's automated distribution, as it is much harder to manually reconstruct after its removal. The corresponding test is also reverted to its original state to reflect this expected behavior. A small test is added to ensure that the analytic distribution results are unchanged when adding a project to the SO. opw-6279406 **Steps to Reproduce:** - Accounting > Configuration > Settings > Analytics > enable Analytic Accounting - Accounting > Configuration > Analytic Accounting > Analytic Distribution Models - Create a new model with any product (e.g. “Bolt”) and any Analytic Distribution (e.g. “Production”) - Create SO, enable “Analytic Distribution” in filters - Add any customer, add the above product (e.g. “Bolt”), save - Observe that the “Production” Analytic Distribution is automatically populated - On the same SO > Other Info> Project > add (e.g. “Home Construction”) - Then go back to Order Lines and remove the previous SOL and create a new one with the same product > save - Observe that the “Production” Analytic Distribution is not added (although “Home Construction” is) **Current behavior before PR:** - Product analytic distributions are not automatically applied when the Sales Order is already linked to a project **Desired behavior after PR is merged:** - Product analytic distributions are automatically applied even when the Sales Order is linked to a project **Note:** This commit basically ports a fix/revert (https://github.com/odoo/odoo/commit/54852978617cfb2d8c5afdcf80adbf6c0605093c) introduced to the project_purchase module for the same issue. Their commit message is quite detailed in explaining the issue. To quote: >However, due to the agency of the code, we cannot prevent losing *both* manually added analytic distributions and product analytic distribution. After consulting the product owner, we concluded that there was no perfect solution in this case, but we'd rather keep the product analytic distribution, as it is much harder to add it again after its removal. Therefore, this commit reverts the previously mentioned commit, while keeping the refactor it introduced. The referenced initial commit is here: https://github.com/odoo/odoo/commit/3dfa98bd3b9d5ababe3a7548d604e22350023799
Before this fix, if a pivot table with comparison was inserted, it would not be displayed correctly in the spreadsheet. After this fix, the comparison is completely ignored when inserting a pivot into a spreadsheet. The domain of the comparison is ignored too. Task: 6429681 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279150
Original PR description
Before this fix, if a pivot table with comparison was inserted, it would not be displayed correctly in the spreadsheet. After this fix, the comparison is completely ignored when inserting a pivot into a spreadsheet. The domain of the comparison is ignored too. Task: 6429681 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279150
Current behaviour: In the Calendar view (day/week/month scale), when the user's timezone observes a DST transition that starts exactly at local midnight (e.g. Africa/Cairo, since 2023), the day column right after the transition gets the wrong weekday name, duplicating the previous day's name. For ex. it renders "... THU THU FRI ..." instead of "... THU FRI SAT ...", for the week surrounding April 30th 2027. To fix this we add 1 hour to the Date before reading its weekday/day from it, mirrori
Original PR description
Current behaviour: In the Calendar view (day/week/month scale), when the user's timezone observes a DST transition that starts exactly at local midnight (e.g. Africa/Cairo, since 2023), the day…
Current behaviour: In the Calendar view (day/week/month scale), when the user's timezone observes a DST transition that starts exactly at local midnight (e.g. Africa/Cairo, since 2023), the day column right after the transition gets the wrong weekday name, duplicating the previous day's name. For ex. it renders "... THU THU FRI ..." instead of "... THU FRI SAT ...", for the week surrounding April 30th 2027. To fix this we add 1 hour to the Date before reading its weekday/day from it, mirroring the workaround FullCalendar itself adopted for this same bug. It has no effect on any ordinary day (adding 1h to a correct local midnight stays within the same calendar day), and it cannot overshoot into the next day since no real-world DST gap exceeds that margin. Note: This is a known bug (https://github.com/fullcalendar/fullcalendar/issues/7633), fixed in FullCalendar v6.1.17, a major version ahead of the v4.4.0, so the fix can't be applied directly without a full library upgrade. opw-6370140 Forward-Port-Of: odoo/odoo#279343
The SIRET value on the contact form should not be overwritten when registering with the PDP. task-6442186
Original PR description
The SIRET value on the contact form should not be overwritten when registering with the PDP. task-6442186
This commit fixes the verification JSON. For OSS taxes no_sujeto_loc and no_sujeto, CuotaTotal and ImporteTotal must Only include the base amount, not the tax amount. See the chatter in the task for AEAT guidelines. Also removed the validation error blocking no_sujeto_loc taxes with a non-zero amount, since OSS taxes legitimately have one in Odoo accounting (e.g. 22% IT VAT) even though it is excluded from the Veri*Factu json. upgrade :- https://github.com/odoo/upgrade/pull/10799 tas
Original PR description
This commit fixes the verification JSON. For OSS taxes no_sujeto_loc and no_sujeto, CuotaTotal and ImporteTotal must Only include the base amount, not the tax amount. See the chatter in the task for AEAT guidelines. Also removed the validation error blocking no_sujeto_loc taxes with a non-zero amount, since OSS taxes legitimately have one in Odoo accounting (e.g. 22% IT VAT) even though it is excluded from the Veri*Factu json. upgrade :- https://github.com/odoo/upgrade/pull/10799 task-5411766 Forward-Port-Of: odoo/odoo#272068
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in.
Original PR description
Steps to reproduce: - Install employees and attendance app - Make sure there are 2 companies - Make user's employee record for Company B, but not A - Make company A the default company for user - Enable "attendances from backend" setting - Click on the attendance dot (systray) Current Behavior: The dot disappears and you can't check in Expected Behavior: You are able to check in Other bug scenario: If you have employee records in both Company A and Company B, you can check in. However, you can never check in for Company B as the default company is always selected in the server code opw-6392301 Forward-Port-Of: odoo/odoo#278377
### Steps to Reproduce: 1. Navigate to a conversation in the Discuss app 2. Find any attachment or image, or send one 3. Try to download it > 405 error ### Description of the issue/feature this PR addresses: **Issue:** Clicking the download button on an image or attachment within the Discuss module triggers a 405 Method Not Allowed error. This is because the frontend OWL component submits the file request via POST. However, the corresponding Python controllers in `binary.py` are strictly
Original PR description
### Steps to Reproduce: 1. Navigate to a conversation in the Discuss app 2. Find any attachment or image, or send one 3. Try to download it > 405 error ### Description of the issue/feature this PR…
### Steps to Reproduce: 1. Navigate to a conversation in the Discuss app 2. Find any attachment or image, or send one 3. Try to download it > 405 error ### Description of the issue/feature this PR addresses: **Issue:** Clicking the download button on an image or attachment within the Discuss module triggers a 405 Method Not Allowed error. This is because the frontend OWL component submits the file request via POST. However, the corresponding Python controllers in `binary.py` are strictly configured to only accept GET requests. Therefore, the server rejects the download attempt and throws an uncaught RPC exception. **Solution:** I updated the @http.route decorators for the download methods in` binary.py` to explicitly allow POST requests. This allows files to download correctly natively without framework errors. ### Current behavior before PR: Users receive a 405 error saying "Method Not Allowed" whenever they try to download either an image or an attachment from an open conversation in the Discuss app. ### Desired behavior after PR: Users will be able to download any and all images and/or attachments from the Discuss app. opw-6389550 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This RST syntax fix prevents warnings during system update. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
This RST syntax fix prevents warnings during system update. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Miscellaneous changes
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
4 changes
Resolved issues and error corrections
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to 15%. - **Specific foreign-payment categories:** 15% for royalties or rights, software licenses, online advertising, and the use or storage of data. - **General remittances abroad:** remain at 27% when they are outside those specific categories. The DGII's current **IR-17-2026 (July 2026 onward)** al
Original PR description
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to…
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to 15%. - **Specific foreign-payment categories:** 15% for royalties or rights, software licenses, online advertising, and the use or storage of data. - **General remittances abroad:** remain at 27% when they are outside those specific categories. The DGII's current **IR-17-2026 (July 2026 onward)** also confirms that the concepts discussed in review are three distinct reporting rows: - **Row 4 — Transfers of titles and properties:** 2%. - **Row 17 — Other income under Decree 139-98, Article 70(a)/(b):** 3%. - **Row 18 — Other withholdings under General Rule 07-2007, as amended by Law 30-26:** 3%. The two 3% rows must not be conflated with each other or with the separate 2% transfer withholding. ## Implementation The changed-rate template entries use new, rate-explicit XML IDs, as recommended in review: - `ret_15_income_person`: new -15% fee withholding, replacing `ret_10_income_person` in the template; posts to `21030301`. - `ret_15_income_rent`: new -15% rental withholding, replacing `ret_10_income_rent` in the template; posts to `21030302`. - `ret_3_income_person`: new -3% General Rule 07-2007 withholding, replacing `ret_2_income_person` in the template; posts to `21030308`. - `tax_group_person_services_15`: new grouped tax using `ret_15_income_person`. - `tax_group_person_construction_3`: new grouped tax using `ret_3_income_person`. - `position_person_services_15`: new physical-services fiscal position mapped only to the current 15% grouped tax. The remaining legal concepts stay separate: - `ret_3_income_article_70`: new -3% tax for Decree 139-98, Article 70(a)/(b), posted to `21030309`. - `ret_2_income_transfer`: remains at -2% on `21030306`; its misleading “Materials” source metadata is corrected to transfers of titles and properties. - `ret_27_income_remittance`: remains active at -27% on `21030307`, with the general foreign-services fiscal position unchanged. - `ret_15_income_foreign_royalties_technology`: new -15% tax only for the foreign categories covered by Law 30-26. It posts to the new `21030310` Law 30-26 payable account rather than the L253-12 remittance account. - `position_exterior_royalties_technology`: new fiscal position mapping purchases only to that specific 15% tax. The specific foreign tax keeps the short invoice label `-15% ISR (L30-26)` to avoid wrapping in vendor-bill PDFs. The original manifest author entry is unchanged, as requested. The Git history and corporate CLA record this contribution by Grupo de Consultoria Henca. ### Existing-company reload behavior The superseded 10%/2% tax, grouped-tax, and physical-services fiscal-position rows are removed from the template rather than shipped as obsolete entries to new companies. On an existing company, Odoo's chart reload keeps those historical records and generated XML IDs unchanged, then creates the new current-rate records under the new XML IDs. On a newly loaded chart, only the current template entries are created. The physical-services fiscal position also has a new XML ID intentionally. Reload preserves existing fiscal-position mappings as user-configurable data and only appends mappings involving new taxes; reusing `position_person` could therefore leave both the historical and current destinations on one position. `position_person_services_15` keeps the current mapping isolated while the old position remains available for historical operations. `ret_2_income_transfer` keeps its existing XML ID because its legal rate, concept, and account remain 2% / transfers / `21030306`. The corrected label is present for new charts; backfilling label-only metadata on already-loaded charts would require an explicit upgrade migration because standard chart reload does not rewrite that user-visible metadata. ## Official references - DGII, current IR-17-2026 download page (July 2026 onward): https://dgii.gov.do/herramientas/formularios/formularioDeclaraciones/Paginas/impuestosRetencionesyRetribuciones.aspx - Law 30-26: https://www.consultoria.gov.do/Consulta/Home/FileManagement?documentId=3405887&managementType=1 - DGII implementation calendar, notice 10-26: https://dgii.gov.do/publicacionesOficiales/avisosInformativos/Documents/2026/10-26.pdf - DGII CA59, calculation for professional and technical services: https://ayuda.dgii.gov.do/conversations/retenciones-y-retribuciones-complementarias/ca59-qu-porcentaje-del-isr-deben-retener-las-personas-jurdicas-a-las-personas-fsicas-en-la-prestacin-de-servicios/5f3c175f8cd858ce879a130f - DGII clarification for General Rule 07-2007 and the construction sector: https://ayuda.dgii.gov.do/conversations/discusiones/aplicacin-de-la-ley-nm-3026-respecto-a-la-retencin-prevista-en-el-artculo-3-de-la-norma-general-072007-sector-construccin/6a45792cde3c6003da189ff1 - DGII legal basis for the 2% transfer withholding: https://ayuda.dgii.gov.do/conversations/discusiones/base-legal-retencion-2-transferencia-de-titulos-y-propiedades/5f6355928cd858ce872bb35a - Ministry clarification on the specific foreign technology and royalty categories: https://www.hacienda.gob.do/ley-30-26-no-dispone-impuestos-por-suscripciones-de-ciudadanos-a-plataformas-digitales-reduce-de-27-a-15-la-retencion-a-empresas-que-contratan-servicios-tecnologicos-en-el-exterior/ Legal basis cited by DGII: Law 11-92, article 309, as amended by Law 30-26, article 17; Regulation of Title II of the Tax Code, article 70. ## Validation - The branch is rebased on the current 17.0 head and contains one squashed commit. - The account, tax-template, and fiscal-position CSV files have consistent column counts, unique IDs, and valid child/account references. - A fresh Dominican chart contains only the new current-rate IDs; the superseded template IDs are absent. - The fresh chart was verified with fees/rentals at 15%; General Rule 07-2007 at 3% on `21030308`; Article 70(a)/(b) at 3% on `21030309`; transfers at 2% on `21030306`; general remittances at 27% on `21030307`; and the covered foreign categories at 15% on `21030310`. - The 15% services and 3% construction groups contain exactly the current tax children, and the new services fiscal position maps each purchase tax to only the 15% group. - An existing company loaded from the pre-law template was reloaded twice. Its historical 10%/10%/2% taxes, historical groups, and historical fiscal position retained their original record IDs and configuration; all current records were created once; the old and new positions each retained exactly two isolated mappings; and the second reload was idempotent. - `/account:TestChartTemplate`: 22 tests passed, 0 failures, 0 errors. This is the first contribution by Grupo de Consultoria Henca (https://www.consultoriahenca.com); the corporate CLA signature is included in `doc/cla/corporate/consultoriahenca.md` as instructed by `doc/cla/sign-cla.md`.
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear
Original PR description
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the…
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear from the original order line. Protect so_line during the write and drop the pending recompute: deleting an invoice must only make the hours invoiceable again, not change their allocation. Steps to reproduce: - Install Sales and Timesheets - Create a service product with invoice policy "Based on Timesheets" and "Create a task in a new project" - Create and confirm a sale order with this product - Log a timesheet on the generated task - Create the invoice (keep it in draft) - Remove the Sales Order Item from the task and from the project settings (or point them to a sale order item of another order) - Delete the draft invoice - Open the timesheet: its Sales Order Item is emptied (or replaced by the other order's item, whose delivered quantity now includes the hours sold on the original order), and the original line's delivered quantity is reset --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
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/
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-prMiscellaneous changes
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
backport: [19.0](https://github.com/odoo/odoo/pull/266411) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr