Daily updates from Odoo
Wednesday, June 10, 2026
124 changes
26 changes
Resolved issues and error corrections
This update fixes a bug where users were unexpectedly locked out of list views after attempting to edit a row. The fix ensures the system correctly exits edit mode when a user deselects a row, preventing the view from becoming unusable and allowing normal operation.
Original PR description
Problem: When a user selects a row, attempts to edit a cell, and then clicks away without saving, the view becomes unusable. The selected row remains highlighted, and the system prevents the selection of other lines. The user is locked out until they click the "Save" or "Discard" buttons. Cause: The UI becomes stuck in edit mode. The `onGlobalClick` event handler within `documents_list_renderer` was missing the method call to exit edit mode. Solution: Updated `onGlobalClick` to correctly trigger the method to leave edit mode. task-6059836 Forward-Port-Of: odoo/enterprise#119594 Forward-Port-Of: odoo/enterprise#113000
This update resolves an issue where the timesheet assistant wouldn't function correctly when a rule was created without a template. The fix ensures the template field is required, allowing the assistant to build display names for key events accurately. This improves the reliability of the timesheet reporting feature.
Original PR description
## [FIX] timesheet_grid: make template field required in AW rule Before this commit, the template field in AW rule was not required and if one rule without any template is set, timesheet assistant will not be able to work correctly to build the display name for the key events found. This commit makes sure the template field is required. ## [FIX] timesheet_grid: ignore rules without template defined Before this commit, when the user creates a rule without any template set, the timesheet assistant will no longer work because it assumes the template is required. This commit adds a condition in the domain when we fetch all AW rules, to ignore the ones without template set. Forward-Port-Of: odoo/enterprise#119451 Forward-Port-Of: odoo/enterprise#119411
This update optimizes how default suppliers are selected in purchase orders, significantly reducing the number of database queries. The change eliminates a performance bottleneck that was slowing down order processing, resulting in a 90% reduction in query time. This improves overall system speed and efficiency.
Original PR description
Currently, computing effective_vendor_id and supplier_id_placeholder presents N+1 query issues. Since every call to _get_default_rule() eventually triggers a _read_group() in _search_rule_for_warehouses(). However we can get rid of this entirely, since the subsequent call to _get_matching_supplier() with an empty values dict depends entirely on the product and not the rule. Another query is also avoided in _get_matching_supplier() which eventually calls ref(). ref() can be substituted with the private method since we are checking against the rule's existing route_id. Benchmark web_search_read by effective_vendor_id on 12,000 orderpoints | |Query Count|Exec Time| |------|-----------|---------| |Before|15,519 |17.46s | |After |722 |3.21s | opw-6186351 Forward-Port-Of: odoo/odoo#269165 Forward-Port-Of: odoo/odoo#268315
This update ensures credit limit warnings accurately reflect a customer's outstanding balance, including recent bank payments. Previously, the system incorrectly flagged over-limit warnings when bank payments were received. Now, the system correctly calculates outstanding balances, preventing unnecessary alerts and improving financial reporting.
Original PR description
Before this fix: The credit limit warning calculation only considered credit notes but ignored outstanding bank payments when computing the partner's effective outstanding balance. For example, if a…
Before this fix: The credit limit warning calculation only considered credit notes but ignored outstanding bank payments when computing the partner's effective outstanding balance. For example, if a customer had a credit limit of 1,000 and an invoice of 2,000 was created, then a bank payment of 1,500 was received, the warning would still incorrectly appear showing the customer exceeded their limit (2,000 > 1,000), even though the actual outstanding amount was only 500. After this fix: The credit limit warning now properly includes outstanding bank payments in the calculation. Two cases are handled: - Bank payments received but not yet matched to any invoice, these are identified by their open suspense account entry and deducted from the partner's outstanding exposure. - Bank payments already matched to the invoice, the reconciled amount is read from the invoice's receivable line and deducted accordingly. So with this fix, after a 1,500 bank payment, the system correctly recognises the outstanding amount as 500 and does not show a warning since it is within the 1,000 credit limit. task-5427613 Forward-Port-Of: odoo/enterprise#119829 Forward-Port-Of: odoo/enterprise#118957
This update resolves a bug where cancelled journal entries were incorrectly displayed in the reconciliation view, preventing successful reconciliation and causing data inconsistencies. The fix removes a previous refactor that inadvertently allowed cancelled entries to appear, ensuring accurate reconciliation processes.
Original PR description
Issue: Cancelled journal entries were visible in the "Journal Items to Reconcile" view (action_move_line_posted_unreconciled) because the action domain had no filter to exclude them. This caused…
Issue: Cancelled journal entries were visible in the "Journal Items to Reconcile" view (action_move_line_posted_unreconciled) because the action domain had no filter to exclude them. This caused reconciliation failures, no reconciliation happened, and the cancelled record remained in the view. This regression was introduced during a refactor to allow draft entries in the reconciliation view, where the posted-state condition was removed from the domain: Enterprise commit: https://github.com/odoo/enterprise/commit/003cffabda7d91a6d10d58942ed972ca5e17366d As a result, cancelled journal items also became visible, causing reconciliation attempts to fail while the records remained in the view. Also, we are not allowed to reconcile cancelled move lines, and we already have the validation for this [here](https://github.com/odoo/odoo/blame/a236f67776616f6facdefb0117a6ffdde9b7c84c/addons/account/models/account_move_line.py#L2627) Issue is reproducible on runbot. Here is the video reference: https://drive.google.com/file/d/1ojIDxHn5Yst8gVFy8JyhwtJoDSSSJsmK/view?usp=sharing - OPW: 6247870 Forward-Port-Of: odoo/enterprise#119017 Forward-Port-Of: odoo/enterprise#118773
This update resolves an issue where users were encountering errors when attempting to use property fields within auto-fill fields in the Sign module. The fix restricts property field selection, ensuring data integrity and preventing the error. This improves the usability of the Sign module.
Original PR description
Currently, an error occurs when user tries to select a property field in auto field. Steps to replicate: - Install `sale_management` and `sign`. - Open Sales > Products > Products > Open any product.…
Currently, an error occurs when user tries to select a property field in auto field.
Steps to replicate:
- Install `sale_management` and `sign`.
- Open Sales > Products > Products > Open any product.
- From the Gear icon, Click Edit Properties and save the record.
- Enable Debug mode if you are using a version lower than 19.0 .
- Open Sign > Configuration > Field Types.
- Create a new Field > Give a name > Select model as `Product`.
- Select Field as `Property > Property 1` and click save.
Error:
- saas-18.3 and later:
```
File '/home/odoo/odoo18/enterprise/sign/models/sign_item_type.py', line 57, in _check_auto_field_exists
auto_field_value = record.mapped(sign_type.auto_field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File '/home/odoo/odoo18/community/odoo/orm/models.py', line 5472, in mapped
field = records._fields[field_name]
^^^^^^^^^^^^^^^
AttributeError: 'Property' object has no attribute '_fields'. Did you mean: 'field'?
```
- saas-18.2:
```
File '/home/odoo/odoo18/enterprise/sign/models/sign_item_type.py, line 41, in _check_auto_field_exists
auto_field_value = record.mapped(sign_type.auto_field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File '/home/odoo/odoo18/community/odoo/orm/models.py', line 5744, in mapped
if len(records) > PREFETCH_MAX:
^^^^^^^^^^^^
TypeError: object of type 'bool' has no len()
```
Cause:
- As the user gave auto fill field as a Property field the [line] called `mapped()` to access its value, this caused the error to occur.
- This occurs because `mapped()` expects a `recordset` (models.Model), but instead it receives a Property object, which does not have `_fields`.
Solution:
- Using `'allow_properties': 'False'`, the property fields wont appear in the list of field selection.
[line]: https://github.com/odoo/enterprise/blob/cdaeb79e1f623831fffa553dbb658698367c7e19/sign/models/sign_item_type.py#L41
sentry-7378769090
Forward-Port-Of: odoo/enterprise#119535
Forward-Port-Of: odoo/enterprise#113091This update corrects a problem where users could lose access to payrun details when switching between companies while running payroll. The fix ensures seamless access to payrun information regardless of company changes, improving payroll processing efficiency. This resolves a potential disruption for HR and finance teams.
Original PR description
todo details Task-6008140
This update resolves a technical issue where Odoo could crash if an offer didn't have a specified start date. The change ensures correct date calculations, particularly when running simulations for past employee versions, preventing inaccurate data and system instability. This improves the reliability of payroll and contract management.
Original PR description
Prevents various crashes from happening in case the contract_start_date isn't set on the offer as it's not required from SQL no related task
This update fixes a confusing error message displayed when sending invoices via Peppol. Previously, users received a generic 'no VAT' error, even when they'd correctly entered customer VAT information. Now, the system accurately identifies the missing Peppol VAT (like a Belgian Company Registry or French SIRET), guiding users to the correct data.
Original PR description
When a user sends a move via Peppol to a customer that has a VAT number set but not a Peppol endpoint, we show the user a generic error ("no VAT").
This makes the user confused, as he already filled the VAT field of his customer, It's the Peppol VAT that is missing (it could be: Belgian Company Registry, France SIRET, ...etc, depending on the customer's country)
This PR makes the error message more accurate by showing exactly the missing required field.
task-5499707
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#266993
Forward-Port-Of: odoo/odoo#245915This update resolves a technical issue where refund orders were generating duplicate invoice numbers for the Spanish tax authority (TicketBAI), leading to rejection of the refund documents. The fix ensures unique invoice numbers are used for refund orders by modifying how the document name is generated, preventing this duplication and improving compliance.
Original PR description
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce:…
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce: ------------------- * Enable TicketBAI for a Spanish company with POS * Create an order and complete payment (TicketBAI sent) * Refund that order from the POS and complete payment * Open the new TicketBAI XML (downloadable on the order in the backend) > Observation: `CabeceraFactura` `NumFactura` matches the original sale; tax authority returns 5040 (duplicate invoice for same issuer/series/year). Why the fix: ------------ The TicketBAI document `name` was set from `pos.order.name`, so `_get_tbai_seq_from_name` extracted the same numeric part as the original sale. `l10n_es_edi_tbai.document` derives Serie/Num from `name` via `_get_tbai_seq_from_name`. Refund POS names intentionally echo the original order label, so we derive the document name from the same components as non-refund orders (`get_reference_last_part()`), which is unique per receipt. opw-6067965 Forward-Port-Of: odoo/odoo#257475
This update resolves an issue where the 'Add to Cart' button wasn't functioning correctly for alternative products on the website. The fix ensures that users can successfully add these alternative products to their cart, improving the shopping experience. The change was made to correctly identify the button element within the website's product display.
Original PR description
Steps to reproduce: --- - Install `website_sale`. - Create a product and from the Sales tab, add alternative products, making sure all products are published on the website. - Open the main product…
Steps to reproduce: --- - Install `website_sale`. - Create a product and from the Sales tab, add alternative products, making sure all products are published on the website. - Open the main product on the website. - In the alternative products section, open the editor, click the `brush` icon under `card design`. - Under Actions > Buttons, click on the `cart` icon. - Save the changes and click the `Add to Cart` button on an alternative product. Issue: --- - Clicking the `Add to Cart` button on alternative products does nothing. Root cause: --- - At [1], the `AddToCart` interaction uses the selector `.oe_website_sale button[name="add_to_cart"]` to find and attach click handlers. When the dynamic snippet renders alternative products, `startInteractions` is called on the `.dynamic_snippet_template` div. It searches for the button inside that div, but at [2], no element wrapping the button has the `oe_website_sale` class in the rendered product card template. So the selector matches nothing, and no click handler is attached. Fix: --- - Add `oe_website_sale` to the `o_wsale_product_btn` wrapper div in the product card template so the button becomes a descendant of `.oe_website_sale` within the injected content, allowing the interaction to attach correctly. [1]https://github.com/odoo/odoo/blob/cbc446bfcaeeb4787cb512ddffbbeb2a154a6dde/addons/website_sale/static/src/interactions/add_to_cart.js#L5 [2]https://github.com/odoo/odoo/blob/cbc446bfcaeeb4787cb512ddffbbeb2a154a6dde/addons/website_sale/templates/snippets/product_snippet_template_data.xml#L95-L113 opw-6197375 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263253
This update resolves an issue where the database upgrade process would fail when reloading the Hungarian chart template due to a dependency on the NAV service. The fix bypasses the credential validation step during the migration, allowing upgrades to proceed smoothly without relying on external connectivity.
Original PR description
Steps to Reproduce: * Create a Hungarian company on 19.0 (or earlier). * Configure NAV credentials through Settings. * Upgrade the database to saas~19.2. Issue: * The upgrade fails while reloading…
Steps to Reproduce:
* Create a Hungarian company on 19.0 (or earlier).
* Configure NAV credentials through Settings.
* Upgrade the database to saas~19.2.
Issue:
* The upgrade fails while reloading the Hungarian chart template.
```python3
Traceback (most recent call last):
File "/home/odoo/src/odoo/saas-19.2/odoo/service/server.py", line 1626, in preload_registries
registry = Registry.new(dbname, update_module=update_module, install_modules=config['init'], upgrade_modules=config['update'], reinit_modules=config['reinit'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/func.py", line 65, in locked
return func(inst, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/registry.py", line 202, in new
load_modules(
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/loading.py", line 502, in load_modules
migrations.migrate_module(package, 'end')
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/migration.py", line 215, in migrate_module
exec_script(self.cr, installed_version, pyfile, pkg.name, stage, version)
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/migration.py", line 253, in exec_script
mod.migrate(cr, installed_version)
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu/migrations/3.1/end-migrate_update_taxes.py", line 7, in migrate
env['account.chart.template'].try_loading('hu', company)
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py", line 181, in try_loading
return self._load(template_code, company, install_demo, force_create)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/template_hu.py", line 11, in _load
company._l10n_hu_edi_configure_company()
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_company.py", line 79, in _l10n_hu_edi_configure_company
res_config_id = self.env['res.config.settings'].create({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/res_config_settings.py", line 39, in create
return super().create(vals_list)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_config_settings.py", line 48, in create
record.company_id._l10n_hu_edi_test_credentials()
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_company.py", line 107, in _l10n_hu_edi_test_credentials
raise UserError(
odoo.exceptions.UserError: Helytelen NAV hitelesítő adatok! Ellenőrizze, hogy a cég adószáma helyesen van-e beállítva.
Hiba részletei: HTTPSConnectionPool(host='api.onlineszamla.nav.gov.hu', port=443): Max retries exceeded with url: /invoiceService/v3/tokenExchange (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x798a970f1fd0>: Failed to establish a new connection: [Errno 111] Connection refused'))
```
Cause:
* Since the introduction of the Hungarian tax migration using `account.chart.template.try_loading('hu', company)` in saas~19.1, upgrades reload the Hungarian chart template.
* When `l10n_hu_edi` is installed, chart template loading triggers `_l10n_hu_edi_configure_company()`, which performs NAV credential validation through `_l10n_hu_edi_test_credentials()`.
* The credential validation performs a live request to the NAV service, making the upgrade dependent on an external service. https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_hu_edi/models/res_company.py#L99-L109 https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_hu_edi/models/template_hu.py#L11
Fix:
* Bypass NAV credential validation during the migration.
* The migration only updates localization data and does not modify the configured EDI credentials.
* This prevents temporary NAV connectivity issues from aborting the upgrade process.
see https://github.com/odoo/odoo/pull/253556
opw-6253523
upg-4326247
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#268178This update fixes an issue where Point of Sale account move lines were incorrectly reporting tax base amounts as always positive, regardless of the sale's actual tax impact. This change ensures that tax calculations in POS transactions accurately reflect the tax liability, leading to more precise accounting reports. It resolves a discrepancy in how tax amounts were processed.
Original PR description
Issue: While creating an account move line from POS, the tax_base_amount of tax line is always positive although it might be negative. Steps to reproduce: - with point_of_sale and account_reports - open register - Sale a product with taxes - close register - Go to Accounting -> Tax Report - Switch to current month - On a line click on the tree dots -> Audit Current Behavior: - POS AMLs always have a positive tax base amount for tax lines. Expected behavior: - POS AMLs have a positive or negative base amount for tax lines depending on the move. opw-5975658 Forward-Port-Of: odoo/odoo#265479
This update resolves an issue where invoices for Persona Natura customers in Colombia were incorrectly formatted for export to the DIAN tax authority. The fix ensures the correct XML structure is generated, addressing a misinterpretation of customer types and preventing export errors. This ensures accurate tax reporting for Colombian businesses.
Original PR description
Issue: Colombian partner being Persona Natura are misinterpreted as Person Juridica. It raises issue while exporting XMLs for dian. Steps to reproduce: - In a Colombian company - Create a Customer with NIT and "Obligaciones y Responsabilidades" to "R-99-PN" - Create an invoice - Send the invoice Current behavior: - node <cbc:AdditionalAccountID> is set to 1 and node PartyIdentification is missing Expected behavior: - node <cbc:AdditionalAccountID> is set to 2 and there is a PartyIdentification node Cause: Colombian partners having a NIT have is_company to True. However, Persona Natura have NIT but aren't companies. opw-6206308 Forward-Port-Of: odoo/enterprise#118193
This update fixes an issue where the 'Cancel Reason' wasn't properly transmitted when reversing invoices in Peruvian companies. Now, the credit note generated for the reversal accurately includes the user-specified cancellation reason, ensuring compliance with Peruvian tax regulations (SUNAT) and providing complete documentation.
Original PR description
### Issue before this commit: When reversing an invoice in a Peruvian company, the "Cancel Reason" entered in the credit note window is not propagated to the Peruvian EDI tab of the resulting Credit…
### Issue before this commit: When reversing an invoice in a Peruvian company, the "Cancel Reason" entered in the credit note window is not propagated to the Peruvian EDI tab of the resulting Credit Note. Only the Credit Reason is successfully reported. ### Steps to reproduce the issue: 1. Download Accounting and l10n_pe 2. Switch to PE company 3. Create an invoice and confirm it 4. Create a credit note for the invoice with a cancel reason and a credit reason and click the reverse button 5. See that in the Peruvian EDI tab only the Credit Reason is reported but not the Cancel Reason ### Cause of the issue: In the l10n_pe_edi module, the override of the _prepare_default_reversal method maps the l10n_pe_edi_refund_reason to the new move's values, but completely omits the mapping of the wizard's textual reason field to the l10n_pe_edi_cancel_reason field of the resulting credit note. ### Reason to introduce the fix: To ensure the generated credit notes contain all required information for the Peruvian EDI (SUNAT). Mapping the cancel reason guarantees that the electronic document accurately reflects both the refund code and the descriptive cancellation text provided by the user. opw-6238525 Forward-Port-Of: odoo/enterprise#119610 Forward-Port-Of: odoo/enterprise#118479
This update fixes an issue where grouped payments were incorrectly linking to unrelated invoices after reconciliation. The process has been updated to ensure payments are accurately associated with the invoices they cover, preventing duplicate payment entries. This improves the accuracy of financial reporting.
Original PR description
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially…
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially paid one and a brand new invoice. 3. Open the first payment, its "Reconciled Invoices" smart button now lists the new invoice from the second payment, which it never paid. Issue --- The smart button is built from the stored `invoice_ids` many2many, which shares its relation table with `account.move.matched_payment_ids`. After reconciling, the register wizard links the payment to its invoices with `lines.move_id.matched_payment_ids += payment` at https://github.com/odoo/odoo/blob/f726393267a28cedd5febd2106de17ae3838f3ff/addons/account/wizard/account_payment_register.py#L1212. When the payment groups several invoices, `lines.move_id` is a multi-record recordset. Reading `matched_payment_ids` on it returns the union of the payments already linked to all those invoices, and `+=` writes that union back to every invoice as a `(6, 0, ...)` replace command. So an invoice already paid by an earlier payment spreads that earlier payment onto every other invoice grouped in the new one, including brand new invoices, which then wrongly appear on the earlier payment. opw-6188013 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267968
This update corrects a bug where order filters on the Ticket Screen in Point of Sale weren't updating correctly when a different provider state was selected. The fix forces a reload of the Ticket Screen, ensuring that the displayed orders reflect the current state and filters. This prevents outdated order information from being shown to users.
Original PR description
Steps to Reproduce ------------------------- - Install Point of Sale and configure UrbanPiper. - Open a POS session and select a provider state from the notification popup to review orders. - While on the Ticket Screen, select a different provider state to review other orders. Issue ------- - Orders are not updated according to the newly selected state. - Previously applied filters remain unchanged. Cause -------- - Since the user is already on the Ticket Screen, changing only the provider state does not trigger a re-render. - The page was already rendered with the old filters. Fix ---- - The Ticket Screen is first switched away and then re-rendered. - This forces the screen to reload with the updated state and filters. Task: 6079663 Forward-Port-Of: odoo/enterprise#119828 Forward-Port-Of: odoo/enterprise#104546
This update resolves an issue preventing users in Peru from generating Closing Entries within the tax reporting feature. The fix creates a specific tax report variant for Peru, ensuring accurate VAT calculations and restoring the automated closing account configuration process. This improves the reliability of financial reporting for Peruvian businesses.
Original PR description
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that…
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that you need a Return Type in order to make a Closing Entry using the Validate button Additionally, using the Generic Tax Report by default creates a risk in Multi-VAT environments, as it mixes taxes from all countries instead of isolating Peruvian taxes ### Cause The new 18.3 accounting workflow requires at least one active Return Type associated with a country-specific report variant to display the Return options and process the closing entry Peru was relying on the Generic Tax Report, without a dedicated report variant No Return Type was configured, which blocked Odoo's automatic VAT closing workflow and prevented the system from prompting the user to configure the required closing accounts ### Steps to reproduce - Install `l10n_pe_reports` and `accountant` - Switch to a PE Company - Go to the Tax Report Before the fix, no Returns button is available for any of the existing reports, making it impossible to use Odoo's automatic process to configure the tax accounts and trigger the closing entry ### Notes This is fixed by creating a dedicated Peruvian tax report variant directly in Enterprise that inherits from the generic tax report A custom handler is added to force the domain filtering on Peruvian taxes only, and a corresponding Return Type is defined to restore the full closing entry process safely opw-5978673 Forward-Port-Of: odoo/enterprise#117891
Previously, a sign request scheduled for the future would immediately show up on the portal for the signer. This fix corrects a technical issue where the system wasn't properly recognizing scheduled requests. Now, scheduled requests will only appear on the portal after the scheduled date, ensuring a smoother user experience.
Original PR description
## Issue When scheduling a sign request, the request appears immediately on the portal for the requested signer. ## Steps to reproduce 1. Install *Sign* (`sign`) 2. Create and send a sign request -…
## Issue
When scheduling a sign request, the request appears immediately on the portal for the requested signer.
## Steps to reproduce
1. Install *Sign* (`sign`)
2. Create and send a sign request
- Signer 1: Any portal user (e.g., Joel Willis)
- Use the clock icon to schedule the signature request to a future date
3. Log in as the portal user used in step 2
4. Navigate to Signature Requests
5. **The signature request already appears in the list, even though it was scheduled for a future date.**
## Cause
The portal filters the sign requests shown based on the `is_mail_sent` field, which does not properly reflect when the signature request is shared to the user.
https://github.com/odoo/enterprise/blob/9898272f17809decf6c5bb4600aca46f9ffae6f0/sign/controllers/portal.py#L40
In fact, when scheduling a signature request, the `is_mail_sent` field is unconditionally set to `True`, even if the signature request will only be sent later.
https://github.com/odoo/enterprise/blob/9898272f17809decf6c5bb4600aca46f9ffae6f0/sign/models/sign_request_item.py#L289
## Fix
Since the `"scheduled"` `sign_request_item.state` option introduced by https://github.com/odoo/enterprise/commit/ed8d5a653e01b1378f0020e2f7a7c2d39fadf3e9 in 19.1, we can easily filter out the sign request items that are scheduled. That state is automatically updated by the `_cron_update_state`, introduced by the same commit as the `"scheduled"` option.
https://github.com/odoo/enterprise/blob/9898272f17809decf6c5bb4600aca46f9ffae6f0/sign/models/sign_request.py#L493-L503
opw-6227472
Forward-Port-Of: odoo/enterprise#118491This update fixes an issue where self-order kiosk payments would remain stuck if a user clicked the back button. Now, a confirmation dialog appears, allowing users to cancel the payment before returning to the previous screen. This ensures a smoother and more reliable payment experience for self-order transactions.
Original PR description
Before this commit, when a cash machine payment was in progress in the self order kiosk, clicking the back button would not cause the payment to be cancelled, leaving the cash machine stuck. After this commit, upon clicking the back button a dialog shows asking the user if they want to cancel their payment, and if so the transaction is cancelled before sending the user back. task-6276665 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update significantly speeds up the process of adding and removing participants from marketing campaigns. By optimizing the underlying code, the sync time has been reduced from over 51 seconds to just 0.65 seconds, even with a large campaign of 115,000 participants. This improves the overall performance and responsiveness of the marketing automation features.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
This update fixes an issue where payslips weren't correctly generating worked day lines for employees using attendance-based work schedules. The change ensures that all employees, regardless of their flexible working arrangement, receive accurate wage calculations based on their attendance records. This improves payroll accuracy and reporting.
Original PR description
### **Steps to reproduce:** - Install Payroll and Attendance apps. - Create an employee with a flexible working schedule and work entry source as attendance. - Create an attendance record for this…
### **Steps to reproduce:** - Install Payroll and Attendance apps. - Create an employee with a flexible working schedule and work entry source as attendance. - Create an attendance record for this employee. - Create and compute a payslip for this employee. ### **Observed Behavior:** Worked Day lines are not generated, and Basic Wage is calculated as 0. ### **Expected Behavior:** Worked Day lines should be populated based on attendance records. ### **Root Cause:** During payslip computation, [_compute_worked_days_line_ids](https://github.com/odoo/enterprise/blob/4339010eb1e1633a67573d08e032f3922b0bec49/hr_payroll/models/hr_payslip.py#L1846) only generated work entries for versions having a `resource_calendar_id` at [1]. As a result, fully flexible employees without a working schedule were excluded from work entry generation, preventing worked day lines from being computed. [1]- https://github.com/odoo/enterprise/blob/4339010eb1e1633a67573d08e032f3922b0bec49/hr_payroll/models/hr_payslip.py#L1890-L1898 ### **Fix:** Remove the `resource_calendar_id` filter when calling `generate_work_entries` in `_compute_worked_days_line_ids` so work entries are also generated for fully flexible employees using attendance-based work entries. **opw-6146452** Forward-Port-Of: odoo/enterprise#117409
This update resolves a previous issue that prevented exporting records with properties from the kanban and list views, specifically causing errors when inserting into spreadsheets. Now, users can reliably export records containing properties, and individual properties displayed in the views are automatically included in the export process.
Original PR description
**Before this commit:** - Exporting records with properties from the kanban view caused a `Client Error`. - Inserting records with properties from the kanban view into a spreadsheet caused a `Client Error`. - Individual properties were not exported by default in list views (even when optionally displayed) or in kanban views. **After this commit:** - Records containing properties can be exported from the kanban view. - Records with properties can be inserted into a spreadsheet without errors. - Individual properties that are optionally displayed are listed by default in `Fields to Export`. enterprise: https://github.com/odoo/enterprise/pull/118913 task-6123524 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#268696 Forward-Port-Of: odoo/odoo#264267
This update fixes a login issue in Safari's private browsing mode, where users were unable to complete the turnstile challenge. The fix addresses a conflict between Safari's tracking protection settings and Odoo's turnstile implementation, ensuring seamless login functionality.
Original PR description
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on…
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on Log in Result: nothing happens and there is an error in the console "An invalid form control with name='' is not focusable." Cause: By default Safari has the Settings > Advanced > "Use advanced tracking and fingerprinting protection" set to "in Private Browsing". If this options is enabled in private browser or in all browsing, you can't login to Odoo with turnstile because safari is preventing the update of the element that is preventing to send the form: <input style="display: none;" class="turnstile_captcha_valid" required> When turnstile challenge succeeds, a value should be set to this input that will unlock the form, the .value property is updated but the browser Shadow Content is not (and if we remove display:none, the input is empty). Fix: I've not been able to reproduce the issue without turnstile using same situation and iframe. We don't know Safari heuristic but the unlocking is working if: - we use setProperty instead of .value - we unset required - we remove the input - we display the turnstile_captcha_valid input before challenge This fix replaces setting .value by setProperty, and add a failsafe of unsetting required. opw-5917286 fixes #247536 Forward-Port-Of: odoo/odoo#253367
This update resolves an issue that prevented attendee imports on events with scheduled emails, causing import failures. By triggering the asynchronous email queue during imports, the system now correctly handles email scheduling, ensuring reliable attendee import processes. This improves the stability and usability of event registration.
Original PR description
Importing attendees on an event that has an `after_sub` mail scheduler (the default on every event) fails with `savepoint "..." does not exist` and the import is aborted.…
Importing attendees on an event that has an `after_sub` mail scheduler (the default on every event) fails with `savepoint "..." does not exist` and the import is aborted. [`_update_mail_schedulers`](https://github.com/odoo/odoo/blob/b2f3270271f6/addons/event/models/event_registration.py#L298) runs the attendee scheduler synchronously on every registration create. The scheduler commits after each mail batch, which is fine from cron but problematic during an import: since [29460b723f49](https://github.com/odoo/odoo/commit/29460b723f49) [`load`](https://github.com/odoo/odoo/blob/b2f3270271f6/odoo/orm/models.py#L884) uses a single savepoint for the whole run, and any commit underneath releases it, so the next `ROLLBACK TO` / `RELEASE SAVEPOINT` raises `InvalidSavepointSpecification`. When `import_file` is in context, trigger the cron like the async path already does so the mails are queued instead of running inline. Steps to reproduce: 0. Have Contacts and Events installed 1. Events > Events, create a published event 2. Open the event, Attendees tab > Favorites > Import records 3. Upload a file with new attendees (Name, Email, no external id) 4. Click Import => "savepoint ... does not exist", import fails Ticket [link](https://www.odoo.com/odoo/project.task/6124741) opw-6124741 Forward-Port-Of: odoo/odoo#267586 Forward-Port-Of: odoo/odoo#260648
This update significantly speeds up the calculation of future leave balances by fixing a recursive process that was causing performance bottlenecks. The change eliminates a redundant calculation step, resulting in a 98% reduction in processing time for complex employee leave scenarios. This improves the overall responsiveness of the HR module.
Original PR description
## The Problem When computing a future leave balance, `_get_future_leaves_on` triggers `_process_accrual_plans`, which iterates period by period and calls `_get_leaves_taken` at each step.…
## The Problem When computing a future leave balance, `_get_future_leaves_on` triggers `_process_accrual_plans`, which iterates period by period and calls `_get_leaves_taken` at each step. `_get_leaves_taken` re-enters `_get_consumed_leaves` with `ignore_future=True`, but other accrual allocations on the same employee were not guarded by `precomputed_allocations`, causing `_get_future_leaves_on` to fire again for each of them, launching another full accrual run recursively. With N periods and K allocations, total work grew as $O(N^K)$. ## The Solution Adding `not ignore_future` to the guard prevents future projection in any nested context where it is both semantically incorrect and the source of the blowup. --- ## Benchmarks *Tested on a customer database with an employee having 2 accrual allocations and pending future leave requests 6 months out:* | | Queries | Request Time | Improvement | | :--- | :--- | :--- | :--- | | **Before** | 220K | 145.0s | — | | **After** | 2.7K | 2.8s | **-98%** | **Note:** More optimizations could be done to reduce the queries to a constant. However given the current design, it would be a bit big change and the current performance is already acceptable. **OPW-6115804** Forward-Port-Of: odoo/odoo#261172
17 changes
Resolved issues and error corrections
This update resolves an issue where the database upgrade process failed due to dependency on a live NAV service during the Hungarian chart template reload. The fix bypasses the credential validation step, allowing the upgrade to proceed without relying on external connectivity, ensuring smoother updates.
Original PR description
Steps to Reproduce: * Create a Hungarian company on 19.0 (or earlier). * Configure NAV credentials through Settings. * Upgrade the database to saas~19.2. Issue: * The upgrade fails while reloading…
Steps to Reproduce:
* Create a Hungarian company on 19.0 (or earlier).
* Configure NAV credentials through Settings.
* Upgrade the database to saas~19.2.
Issue:
* The upgrade fails while reloading the Hungarian chart template.
```python3
Traceback (most recent call last):
File "/home/odoo/src/odoo/saas-19.2/odoo/service/server.py", line 1626, in preload_registries
registry = Registry.new(dbname, update_module=update_module, install_modules=config['init'], upgrade_modules=config['update'], reinit_modules=config['reinit'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/func.py", line 65, in locked
return func(inst, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/registry.py", line 202, in new
load_modules(
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/loading.py", line 502, in load_modules
migrations.migrate_module(package, 'end')
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/migration.py", line 215, in migrate_module
exec_script(self.cr, installed_version, pyfile, pkg.name, stage, version)
File "/home/odoo/src/odoo/saas-19.2/odoo/modules/migration.py", line 253, in exec_script
mod.migrate(cr, installed_version)
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu/migrations/3.1/end-migrate_update_taxes.py", line 7, in migrate
env['account.chart.template'].try_loading('hu', company)
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py", line 181, in try_loading
return self._load(template_code, company, install_demo, force_create)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/template_hu.py", line 11, in _load
company._l10n_hu_edi_configure_company()
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_company.py", line 79, in _l10n_hu_edi_configure_company
res_config_id = self.env['res.config.settings'].create({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/res_config_settings.py", line 39, in create
return super().create(vals_list)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_config_settings.py", line 48, in create
record.company_id._l10n_hu_edi_test_credentials()
File "/home/odoo/src/odoo/saas-19.2/addons/l10n_hu_edi/models/res_company.py", line 107, in _l10n_hu_edi_test_credentials
raise UserError(
odoo.exceptions.UserError: Helytelen NAV hitelesítő adatok! Ellenőrizze, hogy a cég adószáma helyesen van-e beállítva.
Hiba részletei: HTTPSConnectionPool(host='api.onlineszamla.nav.gov.hu', port=443): Max retries exceeded with url: /invoiceService/v3/tokenExchange (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x798a970f1fd0>: Failed to establish a new connection: [Errno 111] Connection refused'))
```
Cause:
* Since the introduction of the Hungarian tax migration using `account.chart.template.try_loading('hu', company)` in saas~19.1, upgrades reload the Hungarian chart template.
* When `l10n_hu_edi` is installed, chart template loading triggers `_l10n_hu_edi_configure_company()`, which performs NAV credential validation through `_l10n_hu_edi_test_credentials()`.
* The credential validation performs a live request to the NAV service, making the upgrade dependent on an external service. https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_hu_edi/models/res_company.py#L99-L109 https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_hu_edi/models/template_hu.py#L11
Fix:
* Bypass NAV credential validation during the migration.
* The migration only updates localization data and does not modify the configured EDI credentials.
* This prevents temporary NAV connectivity issues from aborting the upgrade process.
see https://github.com/odoo/odoo/pull/253556
opw-6253523
upg-4326247
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#268178This update clarifies error messages when sending invoices via Peppol. Previously, users received a generic "no VAT" message, which was confusing. Now, the system accurately identifies the missing Peppol VAT information (like a Belgian Company Registry), guiding users to complete the required details for successful transmission.
Original PR description
When a user sends a move via Peppol to a customer that has a VAT number set but not a Peppol endpoint, we show the user a generic error ("no VAT").
This makes the user confused, as he already filled the VAT field of his customer, It's the Peppol VAT that is missing (it could be: Belgian Company Registry, France SIRET, ...etc, depending on the customer's country)
This PR makes the error message more accurate by showing exactly the missing required field.
task-5499707
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#266993
Forward-Port-Of: odoo/odoo#245915This update resolves a technical issue where refund orders were generating duplicate invoice numbers for the Spanish tax authority (TicketBAI). The fix ensures unique invoice numbers are used for refund orders, preventing rejection by the tax authority and streamlining the refund process. This improves compliance and avoids delays.
Original PR description
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce:…
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce: ------------------- * Enable TicketBAI for a Spanish company with POS * Create an order and complete payment (TicketBAI sent) * Refund that order from the POS and complete payment * Open the new TicketBAI XML (downloadable on the order in the backend) > Observation: `CabeceraFactura` `NumFactura` matches the original sale; tax authority returns 5040 (duplicate invoice for same issuer/series/year). Why the fix: ------------ The TicketBAI document `name` was set from `pos.order.name`, so `_get_tbai_seq_from_name` extracted the same numeric part as the original sale. `l10n_es_edi_tbai.document` derives Serie/Num from `name` via `_get_tbai_seq_from_name`. Refund POS names intentionally echo the original order label, so we derive the document name from the same components as non-refund orders (`get_reference_last_part()`), which is unique per receipt. opw-6067965 Forward-Port-Of: odoo/odoo#257475
This update resolves an issue where the 'Add to Cart' button wasn't functioning correctly for alternative products on the website. The fix ensures that users can successfully add these alternative products to their cart, improving the shopping experience. The change was made to correctly identify the button element within the product display.
Original PR description
Steps to reproduce: --- - Install `website_sale`. - Create a product and from the Sales tab, add alternative products, making sure all products are published on the website. - Open the main product…
Steps to reproduce: --- - Install `website_sale`. - Create a product and from the Sales tab, add alternative products, making sure all products are published on the website. - Open the main product on the website. - In the alternative products section, open the editor, click the `brush` icon under `card design`. - Under Actions > Buttons, click on the `cart` icon. - Save the changes and click the `Add to Cart` button on an alternative product. Issue: --- - Clicking the `Add to Cart` button on alternative products does nothing. Root cause: --- - At [1], the `AddToCart` interaction uses the selector `.oe_website_sale button[name="add_to_cart"]` to find and attach click handlers. When the dynamic snippet renders alternative products, `startInteractions` is called on the `.dynamic_snippet_template` div. It searches for the button inside that div, but at [2], no element wrapping the button has the `oe_website_sale` class in the rendered product card template. So the selector matches nothing, and no click handler is attached. Fix: --- - Add `oe_website_sale` to the `o_wsale_product_btn` wrapper div in the product card template so the button becomes a descendant of `.oe_website_sale` within the injected content, allowing the interaction to attach correctly. [1]https://github.com/odoo/odoo/blob/cbc446bfcaeeb4787cb512ddffbbeb2a154a6dde/addons/website_sale/static/src/interactions/add_to_cart.js#L5 [2]https://github.com/odoo/odoo/blob/cbc446bfcaeeb4787cb512ddffbbeb2a154a6dde/addons/website_sale/templates/snippets/product_snippet_template_data.xml#L95-L113 opw-6197375 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263253
This update fixes an issue where tax calculations within Point of Sale (POS) were consistently showing positive tax base amounts, regardless of the actual tax amount. This resulted in inaccurate tax reporting in the accounting system. The fix ensures that tax base amounts accurately reflect the tax liability, aligning POS transactions with financial reporting.
Original PR description
Issue: While creating an account move line from POS, the tax_base_amount of tax line is always positive although it might be negative. Steps to reproduce: - with point_of_sale and account_reports - open register - Sale a product with taxes - close register - Go to Accounting -> Tax Report - Switch to current month - On a line click on the tree dots -> Audit Current Behavior: - POS AMLs always have a positive tax base amount for tax lines. Expected behavior: - POS AMLs have a positive or negative base amount for tax lines depending on the move. opw-5975658 Forward-Port-Of: odoo/odoo#265479
This update resolves an issue where invoices for Persona Natura customers in Colombia were incorrectly formatted for export to the DIAN tax authority. The fix ensures the correct XML structure is generated, addressing a misinterpretation of customer types and preventing export errors. This ensures accurate tax reporting for Colombian businesses.
Original PR description
Issue: Colombian partner being Persona Natura are misinterpreted as Person Juridica. It raises issue while exporting XMLs for dian. Steps to reproduce: - In a Colombian company - Create a Customer with NIT and "Obligaciones y Responsabilidades" to "R-99-PN" - Create an invoice - Send the invoice Current behavior: - node <cbc:AdditionalAccountID> is set to 1 and node PartyIdentification is missing Expected behavior: - node <cbc:AdditionalAccountID> is set to 2 and there is a PartyIdentification node Cause: Colombian partners having a NIT have is_company to True. However, Persona Natura have NIT but aren't companies. opw-6206308 Forward-Port-Of: odoo/enterprise#118193
This update fixes an issue where the 'Cancel Reason' wasn't being properly transmitted to the Peruvian EDI (SUNAT) documents when reversing invoices. Now, the credit note accurately reflects both the refund code and the user-provided cancellation explanation, ensuring compliance with Peruvian regulations. This improves data accuracy for financial reporting and audit trails.
Original PR description
### Issue before this commit: When reversing an invoice in a Peruvian company, the "Cancel Reason" entered in the credit note window is not propagated to the Peruvian EDI tab of the resulting Credit…
### Issue before this commit: When reversing an invoice in a Peruvian company, the "Cancel Reason" entered in the credit note window is not propagated to the Peruvian EDI tab of the resulting Credit Note. Only the Credit Reason is successfully reported. ### Steps to reproduce the issue: 1. Download Accounting and l10n_pe 2. Switch to PE company 3. Create an invoice and confirm it 4. Create a credit note for the invoice with a cancel reason and a credit reason and click the reverse button 5. See that in the Peruvian EDI tab only the Credit Reason is reported but not the Cancel Reason ### Cause of the issue: In the l10n_pe_edi module, the override of the _prepare_default_reversal method maps the l10n_pe_edi_refund_reason to the new move's values, but completely omits the mapping of the wizard's textual reason field to the l10n_pe_edi_cancel_reason field of the resulting credit note. ### Reason to introduce the fix: To ensure the generated credit notes contain all required information for the Peruvian EDI (SUNAT). Mapping the cancel reason guarantees that the electronic document accurately reflects both the refund code and the descriptive cancellation text provided by the user. opw-6238525 Forward-Port-Of: odoo/enterprise#119610 Forward-Port-Of: odoo/enterprise#118479
This update fixes an issue where grouped payments were incorrectly linking to unrelated invoices after reconciliation. The process has been updated to ensure payments are accurately associated with the invoices they cover, preventing data duplication and improving payment reconciliation accuracy. This resolves a previous bug impacting payment reporting.
Original PR description
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially…
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially paid one and a brand new invoice. 3. Open the first payment, its "Reconciled Invoices" smart button now lists the new invoice from the second payment, which it never paid. Issue --- The smart button is built from the stored `invoice_ids` many2many, which shares its relation table with `account.move.matched_payment_ids`. After reconciling, the register wizard links the payment to its invoices with `lines.move_id.matched_payment_ids += payment` at https://github.com/odoo/odoo/blob/f726393267a28cedd5febd2106de17ae3838f3ff/addons/account/wizard/account_payment_register.py#L1212. When the payment groups several invoices, `lines.move_id` is a multi-record recordset. Reading `matched_payment_ids` on it returns the union of the payments already linked to all those invoices, and `+=` writes that union back to every invoice as a `(6, 0, ...)` replace command. So an invoice already paid by an earlier payment spreads that earlier payment onto every other invoice grouped in the new one, including brand new invoices, which then wrongly appear on the earlier payment. opw-6188013 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267968
This update significantly speeds up the process of adding and removing participants from marketing campaigns. By optimizing the underlying code, the sync time has been reduced from over 51 seconds to just 0.65 seconds, even with a large campaign of 115,000 participants. This improves campaign performance and responsiveness.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
This update resolves an issue preventing users in Peru from generating closing entries for their tax reports. The fix introduces a dedicated Peruvian tax report variant, ensuring accurate VAT calculations and restoring the automated closing account configuration process. This improves the reliability of the Peruvian accounting workflow.
Original PR description
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that…
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that you need a Return Type in order to make a Closing Entry using the Validate button Additionally, using the Generic Tax Report by default creates a risk in Multi-VAT environments, as it mixes taxes from all countries instead of isolating Peruvian taxes ### Cause The new 18.3 accounting workflow requires at least one active Return Type associated with a country-specific report variant to display the Return options and process the closing entry Peru was relying on the Generic Tax Report, without a dedicated report variant No Return Type was configured, which blocked Odoo's automatic VAT closing workflow and prevented the system from prompting the user to configure the required closing accounts ### Steps to reproduce - Install `l10n_pe_reports` and `accountant` - Switch to a PE Company - Go to the Tax Report Before the fix, no Returns button is available for any of the existing reports, making it impossible to use Odoo's automatic process to configure the tax accounts and trigger the closing entry ### Notes This is fixed by creating a dedicated Peruvian tax report variant directly in Enterprise that inherits from the generic tax report A custom handler is added to force the domain filtering on Peruvian taxes only, and a corresponding Return Type is defined to restore the full closing entry process safely opw-5978673 Forward-Port-Of: odoo/enterprise#117891
This fix resolves an issue where credit notes for returned dropshipped products incorrectly displayed the wrong lot number on invoices. The update ensures that the correct lot number (the returned one) is shown, improving accuracy and transparency in financial reporting for dropshipping transactions.
Original PR description
**Issue** Printing a credit note for a returned dropshipped tracked product could display the wrong lot/serial number on the invoice report. **Steps to reproduce** - Activate "Display Lots & Serial…
**Issue**
Printing a credit note for a returned dropshipped tracked product could display the wrong lot/serial number on the invoice report.
**Steps to reproduce**
- Activate "Display Lots & Serial Numbers on Invoices"
- Create a product tracked by serial/lot and enable the dropship route
- Create two lots: "lot1" and "lot2"
- Create and confirm a SO for quantity 2
- Confirm the PO and validate the dropship for both lots
- Create and post an invoice
- Return "lot2" from the dropship picking
- Create and post a credit note for quantity 1
- Click on print -> The generated PDF displays "lot1" instead of "lot2"
**Cause**
While rendering `account.report_invoice_with_payments`, the report calls `_get_invoiced_lot_values` to determine which lot/serial numbers should be displayed:
https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L31-L32 `invoiced_qties = 1` since the credit is on a quantity of 1 https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L44 Three stock move lines are retrieved from the SO:
- the two original dropship deliveries,
- the return move for `lot2`. https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L63 However, none of them are considered as `is_stock_return` because the dropship locations use `supplier` instead of `internal`: https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L72-L76 As a consequence:
- The two original delivery move lines each keep quantity `1`: https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L69 they never pass through the return handling logic: https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L77-L80 which would make it as -1 (since `qties_per_lot[sml.lot_id]` is 0 for the first iteration of `sml.lot_id`). Thus, it does not pass by this code (since quantity is greater than 0): https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L87-L90 which would make it as 0.
- for the last one, `is_stock_return = False` as it should be, thus the quantity is 1 as it should be. The quantities are therefore accumulated as:
https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L92
resulting in:
`qties_per_lot = {lot1: 1, lot2: 2}`
instead of:
`qties_per_lot = {lot1: 0, lot2: 1}`
The report then selects the first matching lot and stops: https://github.com/odoo/odoo/blob/786c373d5ac8afdfb79eb7a7d69c5eb83b919625/addons/sale_stock/models/account_move.py#L94-L99
opw-6230281
Forward-Port-Of: odoo/odoo#266716This update resolves a performance issue that caused significant lag when hovering over account reports with many columns. The change optimizes CSS styling to reduce unnecessary calculations, resulting in a smoother and faster user experience. This improves the responsiveness of a key business reporting tool.
Original PR description
Forward-Port-Of: odoo/enterprise#119242
This update fixes an issue where undoing the auto-plan feature would reset a shift's allocated workload hours, leading to inaccurate reporting. The change preserves the original workload value during undo, ensuring that shift allocations remain consistent and reliable. This improves the accuracy of resource planning.
Original PR description
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation…
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation triggers recomputation of allocated_hours, causing the shift to lose its original workload value. Current Behaviour --- When resource_id is set to False during undo: - _compute_allocated_hours is triggered (depends on resource_id) - _compute_allocated_percentage is triggered (depends on allocated_hours) - Both fields are recalculated, potentially changing allocated_hours from its pre-assignment value Expected Behaviour --- Undoing auto-plan should preserve allocated_hours at its pre-assignment value while allowing allocated_percentage to adapt to the new context (open slot vs assigned resource). Fix --- Use protecting context manager in action_rollback_auto_plan_ids to prevent allocated_hours from being recomputed when resource_id is removed. This allows allocated_percentage to recalculate naturally based on slot duration while keeping allocated_hours stable. task - 4952149 Forward-Port-Of: odoo/enterprise#119772 Forward-Port-Of: odoo/enterprise#102864
This update fixes a login issue in Safari's private browsing mode, where users were unable to complete the turnstile challenge. The fix addresses a conflict between Safari's tracking protection settings and Odoo's form submission process. It ensures a stable login experience for Safari users in private browsing.
Original PR description
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on…
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on Log in Result: nothing happens and there is an error in the console "An invalid form control with name='' is not focusable." Cause: By default Safari has the Settings > Advanced > "Use advanced tracking and fingerprinting protection" set to "in Private Browsing". If this options is enabled in private browser or in all browsing, you can't login to Odoo with turnstile because safari is preventing the update of the element that is preventing to send the form: <input style="display: none;" class="turnstile_captcha_valid" required> When turnstile challenge succeeds, a value should be set to this input that will unlock the form, the .value property is updated but the browser Shadow Content is not (and if we remove display:none, the input is empty). Fix: I've not been able to reproduce the issue without turnstile using same situation and iframe. We don't know Safari heuristic but the unlocking is working if: - we use setProperty instead of .value - we unset required - we remove the input - we display the turnstile_captcha_valid input before challenge This fix replaces setting .value by setProperty, and add a failsafe of unsetting required. opw-5917286 fixes #247536 Forward-Port-Of: odoo/odoo#253367
This update resolves an issue that prevented attendee imports on events with the default mail scheduler. The fix ensures emails are queued instead of processed synchronously, preventing savepoint errors during the import process. This improves the reliability of attendee imports.
Original PR description
Importing attendees on an event that has an `after_sub` mail scheduler (the default on every event) fails with `savepoint "..." does not exist` and the import is aborted.…
Importing attendees on an event that has an `after_sub` mail scheduler (the default on every event) fails with `savepoint "..." does not exist` and the import is aborted. [`_update_mail_schedulers`](https://github.com/odoo/odoo/blob/b2f3270271f6/addons/event/models/event_registration.py#L298) runs the attendee scheduler synchronously on every registration create. The scheduler commits after each mail batch, which is fine from cron but problematic during an import: since [29460b723f49](https://github.com/odoo/odoo/commit/29460b723f49) [`load`](https://github.com/odoo/odoo/blob/b2f3270271f6/odoo/orm/models.py#L884) uses a single savepoint for the whole run, and any commit underneath releases it, so the next `ROLLBACK TO` / `RELEASE SAVEPOINT` raises `InvalidSavepointSpecification`. When `import_file` is in context, trigger the cron like the async path already does so the mails are queued instead of running inline. Steps to reproduce: 0. Have Contacts and Events installed 1. Events > Events, create a published event 2. Open the event, Attendees tab > Favorites > Import records 3. Upload a file with new attendees (Name, Email, no external id) 4. Click Import => "savepoint ... does not exist", import fails Ticket [link](https://www.odoo.com/odoo/project.task/6124741) opw-6124741 Forward-Port-Of: odoo/odoo#267586 Forward-Port-Of: odoo/odoo#260648
This update significantly speeds up the calculation of future leave balances by fixing a recursive process that was causing performance bottlenecks. The change eliminates unnecessary calculations, resulting in a 98% reduction in processing time for complex employee leave scenarios. This improves the responsiveness of the HR module.
Original PR description
## The Problem When computing a future leave balance, `_get_future_leaves_on` triggers `_process_accrual_plans`, which iterates period by period and calls `_get_leaves_taken` at each step.…
## The Problem When computing a future leave balance, `_get_future_leaves_on` triggers `_process_accrual_plans`, which iterates period by period and calls `_get_leaves_taken` at each step. `_get_leaves_taken` re-enters `_get_consumed_leaves` with `ignore_future=True`, but other accrual allocations on the same employee were not guarded by `precomputed_allocations`, causing `_get_future_leaves_on` to fire again for each of them, launching another full accrual run recursively. With N periods and K allocations, total work grew as $O(N^K)$. ## The Solution Adding `not ignore_future` to the guard prevents future projection in any nested context where it is both semantically incorrect and the source of the blowup. --- ## Benchmarks *Tested on a customer database with an employee having 2 accrual allocations and pending future leave requests 6 months out:* | | Queries | Request Time | Improvement | | :--- | :--- | :--- | :--- | | **Before** | 220K | 145.0s | — | | **After** | 2.7K | 2.8s | **-98%** | **Note:** More optimizations could be done to reduce the queries to a constant. However given the current design, it would be a bit big change and the current performance is already acceptable. **OPW-6115804** Forward-Port-Of: odoo/odoo#261172
This update corrects a bug in the sale details report that previously failed to include discounts applied through loyalty programs. Now, the report accurately displays the total discount amount, including those generated by the loyalty program, ensuring accurate sales reporting. This resolves an issue impacting all users utilizing the loyalty program.
Original PR description
When generating the sale details report, the number of discounts would not include the discount given by a loyalty program. The same problem applies for the total discount amount. Steps to reproduce: ------------------- * Create a loyalty program that gives a 10% discount automatically. * Open the PoS and make an order that activate the loyalty program. * Close the session and open the sale details report for this session. > Observation: The discount number and total is 0 opw-6185554 Forward-Port-Of: odoo/odoo#267753
14 changes
Resolved issues and error corrections
This update optimizes how Odoo identifies default suppliers for purchase orders, significantly speeding up the process. By eliminating redundant queries, the system now responds much faster – reducing query counts by over 90%. This improvement directly impacts order processing speed and efficiency.
Original PR description
Currently, computing effective_vendor_id and supplier_id_placeholder presents N+1 query issues. Since every call to _get_default_rule() eventually triggers a _read_group() in _search_rule_for_warehouses(). However we can get rid of this entirely, since the subsequent call to _get_matching_supplier() with an empty values dict depends entirely on the product and not the rule. Another query is also avoided in _get_matching_supplier() which eventually calls ref(). ref() can be substituted with the private method since we are checking against the rule's existing route_id. Benchmark web_search_read by effective_vendor_id on 12,000 orderpoints | |Query Count|Exec Time| |------|-----------|---------| |Before|15,519 |17.46s | |After |722 |3.21s | opw-6186351 Forward-Port-Of: odoo/odoo#268315
This update resolves a bug where splitting orders incorrectly applied tax settings. When an order was split, the new order defaulted to the system's standard settings instead of the original order's tax configuration. This change ensures that split orders accurately reflect the tax settings of the original order, improving financial reporting and accuracy.
Original PR description
When splitting an order, `createNewOrder()` was called with no preset,so the new order silently fell back to the config's default preset. The moved orderlines kept their original price_unit and tax_ids while the new order header used the wrong fiscal_position_id and pricelist_id. Steps to reproduce: - Order with two products, switch to a preset that adds tax - Split, select one line, Payment, Validate - Pay the other line, Validate - open order in backend, first line has default price_unit and no tax, second line has correct price_unit and tax -opw-6246434 Forward-Port-Of: odoo/odoo#268862 Forward-Port-Of: odoo/odoo#268772
This update resolves a bug where cancelled journal entries were incorrectly displayed in the reconciliation view, preventing successful reconciliation and causing data inconsistencies. The fix removes a previous refactor that allowed draft entries, which inadvertently exposed cancelled entries. This ensures accurate reconciliation processes.
Original PR description
Issue: Cancelled journal entries were visible in the "Journal Items to Reconcile" view (action_move_line_posted_unreconciled) because the action domain had no filter to exclude them. This caused…
Issue: Cancelled journal entries were visible in the "Journal Items to Reconcile" view (action_move_line_posted_unreconciled) because the action domain had no filter to exclude them. This caused reconciliation failures, no reconciliation happened, and the cancelled record remained in the view. This regression was introduced during a refactor to allow draft entries in the reconciliation view, where the posted-state condition was removed from the domain: Enterprise commit: https://github.com/odoo/enterprise/commit/003cffabda7d91a6d10d58942ed972ca5e17366d As a result, cancelled journal items also became visible, causing reconciliation attempts to fail while the records remained in the view. Also, we are not allowed to reconcile cancelled move lines, and we already have the validation for this [here](https://github.com/odoo/odoo/blame/a236f67776616f6facdefb0117a6ffdde9b7c84c/addons/account/models/account_move_line.py#L2627) Issue is reproducible on runbot. Here is the video reference: https://drive.google.com/file/d/1ojIDxHn5Yst8gVFy8JyhwtJoDSSSJsmK/view?usp=sharing - OPW: 6247870 Forward-Port-Of: odoo/enterprise#119017 Forward-Port-Of: odoo/enterprise#118773
This update resolves an issue where refund orders were generating duplicate invoice numbers for the Spanish tax authority (TicketBAI). The fix ensures unique invoice numbers are used for refunds, preventing rejection by the tax authority and streamlining the refund process. This improves compliance and reduces manual intervention.
Original PR description
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce:…
Refund orders use a display name based on the original ticket. The rectificativa was then submitted with a duplicate Serie/NumFactura and rejected with TicketBAI error 5040. Steps to reproduce: ------------------- * Enable TicketBAI for a Spanish company with POS * Create an order and complete payment (TicketBAI sent) * Refund that order from the POS and complete payment * Open the new TicketBAI XML (downloadable on the order in the backend) > Observation: `CabeceraFactura` `NumFactura` matches the original sale; tax authority returns 5040 (duplicate invoice for same issuer/series/year). Why the fix: ------------ The TicketBAI document `name` was set from `pos.order.name`, so `_get_tbai_seq_from_name` extracted the same numeric part as the original sale. `l10n_es_edi_tbai.document` derives Serie/Num from `name` via `_get_tbai_seq_from_name`. Refund POS names intentionally echo the original order label, so we derive the document name from the same components as non-refund orders (`get_reference_last_part()`), which is unique per receipt. opw-6067965 Forward-Port-Of: odoo/odoo#257475
This update fixes an issue where the product image carousel wouldn't scroll correctly after a product variant was selected on the e-commerce site. The fix ensures that the carousel properly updates and responds to user interactions like scrolling, improving the shopping experience. This was caused by a technical glitch in how the system handles carousel updates.
Original PR description
Steps to produce: --- - Install `website_sale` module. - Create a product with a variant and add the images from the sale tab. - Go to the product on the e-commerce and change the variant. - Attempt…
Steps to produce: --- - Install `website_sale` module. - Create a product with a variant and add the images from the sale tab. - Go to the product on the e-commerce and change the variant. - Attempt to scroll through the product images (using the mouse wheel). Issue: --- - After changing a product variant on the eCommerce product page, attempting to scroll through the product images (using mouse wheel) has no effect. Root cause: --- - When a product variant is changed, `_updateProductImage` dynamically replaces the product image carousel DOM element (`#o-carousel-product`) by injecting new HTML and removing the old one. - The old CarouselProduct interaction instance remains in memory, causing a resource and event listener leak on the detached old DOM element. - The newly inserted `#o-carousel-product` element is ignored by the interaction service, meaning that the CarouselProduct interaction is never initialized on the new carousel. This leaves the new carousel static and unresponsive to user interactions. Solution: --- - Before replacing the carousel DOM node, manually notify the public.interactions service to clean up any active interactions on the old element. After the new DOM node is queried, start the interactions on the new element. opw-6229291 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265537
This update optimizes the performance of account reports when hovering over tables with many columns. Previously, hovering caused significant lag due to complex CSS calculations. This change reduces these calculations by targeting specific table cells, resulting in a smoother user experience.
Original PR description
Forward-Port-Of: odoo/enterprise#119242
This update fixes an issue where undoing the auto-plan feature would reset shift workloads, leading to inaccurate resource allocation. The change ensures that allocated hours remain consistent after undoing, allowing for more reliable planning and scheduling. This improves the accuracy of workload assignments.
Original PR description
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation…
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation triggers recomputation of allocated_hours, causing the shift to lose its original workload value. Current Behaviour --- When resource_id is set to False during undo: - _compute_allocated_hours is triggered (depends on resource_id) - _compute_allocated_percentage is triggered (depends on allocated_hours) - Both fields are recalculated, potentially changing allocated_hours from its pre-assignment value Expected Behaviour --- Undoing auto-plan should preserve allocated_hours at its pre-assignment value while allowing allocated_percentage to adapt to the new context (open slot vs assigned resource). Fix --- Use protecting context manager in action_rollback_auto_plan_ids to prevent allocated_hours from being recomputed when resource_id is removed. This allows allocated_percentage to recalculate naturally based on slot duration while keeping allocated_hours stable. task - 4952149 Forward-Port-Of: odoo/enterprise#119772 Forward-Port-Of: odoo/enterprise#102864
This update fixes an issue where tax calculations within Point of Sale (POS) were consistently showing positive tax base amounts, regardless of the actual tax amount. This resulted in inaccurate tax reporting in the accounting system. The fix ensures that tax base amounts accurately reflect the tax liability, providing correct financial reporting.
Original PR description
Issue: While creating an account move line from POS, the tax_base_amount of tax line is always positive although it might be negative. Steps to reproduce: - with point_of_sale and account_reports - open register - Sale a product with taxes - close register - Go to Accounting -> Tax Report - Switch to current month - On a line click on the tree dots -> Audit Current Behavior: - POS AMLs always have a positive tax base amount for tax lines. Expected behavior: - POS AMLs have a positive or negative base amount for tax lines depending on the move. opw-5975658 Forward-Port-Of: odoo/odoo#265479
This update corrects a discrepancy in product pricing across Odoo. Previously, changing the price on a product's variant form didn't update the main product template. Now, the system ensures that the sales price is consistently reflected across both the product variant and the main product template, improving data accuracy for sales and inventory management.
Original PR description
Issue: When the sales price is changed from the product variant form for a product without configured variants, the price is updated only on `product.product.lst_price`. The main product form, opened…
Issue: When the sales price is changed from the product variant form for a product without configured variants, the price is updated only on `product.product.lst_price`. The main product form, opened from Inventory > Products, displays `product.template.list_price`, which remains unchanged. The same issue is visible from Purchase Orders because the product internal link on a purchase order line opens `product.product`, while the product page opens `product.template`. Steps to reproduce: - Create or open a product without configured variants - Open product variant form from the internal link in a purchase order - Change the Sales Price on the product from there - Open the product from Inventory > Products (`product.template`) - The template Sales Price still shows the old value Cause: Since version 19.1, `product.product.lst_price` is an editable stored field, allowing variant-level prices to differ from the template price. This is correct for products with multiple variants, where each variant may have its own sales price. However, for products with only one variant (the product itself), no synchronization was performed from `product.product.lst_price` back to `product.template.list_price`, leaving both product forms inconsistent. Solution: - Add `_inverse_product_lst_price` on `product.product.lst_price` so that When `lst_price` is written and the template has exactly one variant, set `list_price` to `lst_price` (delegates to the template) opw-6260015 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update significantly improves the speed of syncing participants to marketing campaigns, particularly for large campaigns. By optimizing the underlying code, the process now takes just a fraction of the previous time (from 51.71 seconds to 0.652 seconds). This enhancement ensures smoother campaign management and reduces potential delays for users.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
This update fixes an issue where delivery orders weren't correctly reserving newly produced lots in multi-step manufacturing workflows. Specifically, changing the destination of a 'Store Finished Products' transfer to a sublocation within the warehouse caused the system to incorrectly break the MTO link. Now, the delivery order will always reserve the intended, freshly produced lot.
Original PR description
Steps to reproduce: - Create a storable product “P1” with Lot tracking - Enable routes: MTO + Manufacture - Create a BoM for the product: - Component: C1 - Configure the warehouse with 3-step…
Steps to reproduce:
- Create a storable product “P1” with Lot tracking
- Enable routes: MTO + Manufacture
- Create a BoM for the product:
- Component: C1
- Configure the warehouse with 3-step manufacturing
- Have on-hand stock in WH/Stock with Lot 001
- Confirm a Sales Order for the product
- Confirm the generated Manufacturing Order and produce Lot 002
- In the "Store Finished Products" transfer, change the destination location from WH/Stock to WH/Stock/Shelf 1 and validate
- Check the Delivery Order reservation
Problem:
The move is reserved with Lot 001 instead of 002
When using a 3-step manufacturing flow (MTO + Manufacture), if the user manually changes the destination of the "Store Finished Products" transfer to a sublocation of WH/Stock (e.g. WH/Stock/Shelf 1), the MTO link between the production and the delivery order was incorrectly broken, causing the delivery to reserve existing stock instead of the freshly produced lot.
Root cause: `_skip_push()` only skipped push logic when the downstream move's source was a child-or-equal of the current move's destination (`m.location_id._child_of(self.location_dest_id)`). When the destination was changed to a sublocation (WH/Stock/Shelf 1), this check failed, so `_push_apply()` ran, found the delivery's source (WH/Stock) was not a child of WH/Stock/Shelf 1, and called `_break_mto_link()`, clearing `move_orig_ids` on the delivery move. The delivery then fell back to make-to-stock reservation and picked an unrelated lot.
opw-6197212
Forward-Port-Of: odoo/odoo#268783This update resolves an issue where Peruvian tax reports couldn't generate closing entries due to a change in Odoo's accounting workflow. The fix creates a specific report variant for Peru, ensuring accurate tax calculations and restoring the automatic closing entry process for Peruvian businesses.
Original PR description
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that…
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that you need a Return Type in order to make a Closing Entry using the Validate button Additionally, using the Generic Tax Report by default creates a risk in Multi-VAT environments, as it mixes taxes from all countries instead of isolating Peruvian taxes ### Cause The new 18.3 accounting workflow requires at least one active Return Type associated with a country-specific report variant to display the Return options and process the closing entry Peru was relying on the Generic Tax Report, without a dedicated report variant No Return Type was configured, which blocked Odoo's automatic VAT closing workflow and prevented the system from prompting the user to configure the required closing accounts ### Steps to reproduce - Install `l10n_pe_reports` and `accountant` - Switch to a PE Company - Go to the Tax Report Before the fix, no Returns button is available for any of the existing reports, making it impossible to use Odoo's automatic process to configure the tax accounts and trigger the closing entry ### Notes This is fixed by creating a dedicated Peruvian tax report variant directly in Enterprise that inherits from the generic tax report A custom handler is added to force the domain filtering on Peruvian taxes only, and a corresponding Return Type is defined to restore the full closing entry process safely opw-5978673 Forward-Port-Of: odoo/enterprise#117891
This update fixes a problem where receipt printing in Austria was incorrect, and prevented a deadlock during authentication with Fiskaly and FON. The changes ensure accurate receipt closing and a smoother authentication process for users in Austria, improving the overall POS experience.
Original PR description
In this task: -------------- - Fixed Austria closing receipt printing by calculating the offset from the last closed month instead of the current month. Closing records are returned in ascending order and exist only for completed months, so the latest month must use offset 0. - Prevent a deadlock during Fiskaly and FON authentication by checking for open sessions before starting any authentication flow, instead of after the first step of authentication. - The resp was used to show error which was not in the scope. task: 5420256 Forward-Port-Of: odoo/enterprise#119454 Forward-Port-Of: odoo/enterprise#102313
This update fixes a login issue in Safari's private browsing mode, where users were unable to complete the turnstile challenge. The fix addresses a conflict between Safari's tracking protection settings and Odoo's turnstile implementation, ensuring seamless login functionality.
Original PR description
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on…
Scenario: - set up turnstile - with recent safari mac os or ios (reproduced from 26.2) go to /web/login page in a private window - enter login and password and pass the turnstile challenge - click on Log in Result: nothing happens and there is an error in the console "An invalid form control with name='' is not focusable." Cause: By default Safari has the Settings > Advanced > "Use advanced tracking and fingerprinting protection" set to "in Private Browsing". If this options is enabled in private browser or in all browsing, you can't login to Odoo with turnstile because safari is preventing the update of the element that is preventing to send the form: <input style="display: none;" class="turnstile_captcha_valid" required> When turnstile challenge succeeds, a value should be set to this input that will unlock the form, the .value property is updated but the browser Shadow Content is not (and if we remove display:none, the input is empty). Fix: I've not been able to reproduce the issue without turnstile using same situation and iframe. We don't know Safari heuristic but the unlocking is working if: - we use setProperty instead of .value - we unset required - we remove the input - we display the turnstile_captcha_valid input before challenge This fix replaces setting .value by setProperty, and add a failsafe of unsetting required. opw-5917286 fixes #247536 Forward-Port-Of: odoo/odoo#253367
9 changes
Resolved issues and error corrections
This update fixes an error in the Colombian DIAN reporting process. Previously, the system incorrectly flagged invoices due to a timezone mismatch between UTC and Bogota time. The fix ensures invoices are validated against the correct local date, allowing accurate DIAN document submissions and avoiding potential reporting issues.
Original PR description
**Steps to reproduce:** * Install `l10n_co_edi` module with DEMO DIAN mode enabled. * Go to Accounting > Vendor > Bills and create a new bill. * Select any Colombian partner different from…
**Steps to reproduce:** * Install `l10n_co_edi` module with DEMO DIAN mode enabled. * Go to Accounting > Vendor > Bills and create a new bill. * Select any Colombian partner different from `Consumidor Final`. * Set the invoice date to 6 days in the past. * Select the DIAN Support Documents journal and a product with UNSPSC category. * Confirm the bill and click `Send Support Document to DIAN` after 5 PM Colombia time. **Observed behavior:** * An error is raised stating the issue date cannot be older than 6 days or more than 6 days in the future, even though the invoice date is within the allowed window in Colombia local time. **Cause:** * The date window validation in `_check_move_configuration` used `fields.Datetime.now()` which returns UTC time. Since Colombia is UTC-5, after 5 PM local time the UTC clock has already rolled over to the next calendar day, making a 6-day-old invoice appear 7 days old and failing the validation incorrectly. **Fix:** * Convert the current UTC datetime to the `America/Bogota` timezone and extract its local date before computing the allowed date window. * Compare directly against `move.invoice_date` (a `date` field) instead of using `fields.Datetime.to_datetime()`, keeping the comparison consistent as `date` vs `date`. opw-6011502 Forward-Port-Of: odoo/enterprise#119794 Forward-Port-Of: odoo/enterprise#115256
This update corrects a missing field on Fedex shipping labels, specifically the 'REF' field. This field is required by the Fedex API and was previously left blank, causing delivery issues. The fix ensures accurate label generation and proper communication with the shipping carrier.
Original PR description
Backport of bb4f8bf Original PR #116870 Forward-Port-Of: odoo/enterprise#118967 Forward-Port-Of: odoo/enterprise#117873
This update resolves an issue where the barcode inventory count feature would fail when using archived units of measure. The fix ensures that archived UOMs are correctly included in the inventory count cache, allowing accurate counts to be performed. This prevents errors during physical inventory adjustments.
Original PR description
### Steps to reproduce: - In the settings enable: "Units of Measure & Packagings", "Storage Locations" - Create a product in units and register 1 unit in stock - Inventory > Operations > Adjustments…
### Steps to reproduce: - In the settings enable: "Units of Measure & Packagings", "Storage Locations" - Create a product in units and register 1 unit in stock - Inventory > Operations > Adjustments > Physical Inventory - Select your line and request a count > Set Current Value - Inventory > Configurations > units of measures > UOM categories - Select unit and archive it - Go to the barcode app > Click Count inventory ### > Owl error: Uncaught promise ### Cause of the issue: Since the uom used on the quant is archived, it is not found by the search used to fill the barcodeCache: https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/components/main.js#L209-L213 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/models/stock_quant.py#L104-L106 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/components/main.js#L229 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/models/barcode_model.js#L37-L39 However, if the uom is not present in the barcode cache the `BarcodeQautnModel` will fail to createLinesState whihc raises a missing error: https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/models/barcode_quant_model.js#L712 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/lazy_barcode_cache.js#L107-L110 opw-6250090 Forward-Port-Of: odoo/enterprise#118987 Forward-Port-Of: odoo/enterprise#118813
This update fixes an issue where undoing the auto-plan feature would reset the allocated hours for shifts, leading to inaccurate workload calculations. The change ensures that shift workloads remain consistent after undoing the auto-plan, improving the reliability of resource planning.
Original PR description
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation…
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation triggers recomputation of allocated_hours, causing the shift to lose its original workload value. Current Behaviour --- When resource_id is set to False during undo: - _compute_allocated_hours is triggered (depends on resource_id) - _compute_allocated_percentage is triggered (depends on allocated_hours) - Both fields are recalculated, potentially changing allocated_hours from its pre-assignment value Expected Behaviour --- Undoing auto-plan should preserve allocated_hours at its pre-assignment value while allowing allocated_percentage to adapt to the new context (open slot vs assigned resource). Fix --- Use protecting context manager in action_rollback_auto_plan_ids to prevent allocated_hours from being recomputed when resource_id is removed. This allows allocated_percentage to recalculate naturally based on slot duration while keeping allocated_hours stable. task - 4952149 Forward-Port-Of: odoo/enterprise#119772 Forward-Port-Of: odoo/enterprise#102864
This update fixes a bug where untaxed invoice lines in German accounting reports incorrectly copied the datev code from the previous line. The fix ensures that untaxed lines now properly display an empty datev code, aligning with German tax regulations. This prevents reporting discrepancies and ensures accurate financial data for our German clients.
Original PR description
**PROBLEM** Untaxed move lines would take the datev code of the previous line instead of having no datev code like they should. **STEP TO REPRODUCE** 1. On a german company, create an invoice with a line with tax 19% I, and a line that is untaxed (with a non-null price). 2. On the general ledger, generate the datev zip. 3. Unzip, and open the account entries csv, and notice the 2nd line of the invoice as the datev code set to something instead of it being empty (column BU-Schlüssel). opw-6141003 Forward-Port-Of: odoo/enterprise#118486
This update fixes an issue where subscription product quantities weren't correctly applying pricelist rules, specifically when the quantity was set to 1. The change ensures that the unit price adjusts to the correct price based on the defined quantity tiers, improving the accuracy of subscription billing.
Original PR description
Steps to produce: --- - Install `sale_subscription` module. - Enable `pricelists` from Settings. - Create a new subscription product. - Create a new pricelist and Under Recurring Prices, add below…
Steps to produce:
---
- Install `sale_subscription` module.
- Enable `pricelists` from Settings.
- Create a new subscription product.
- Create a new pricelist and Under Recurring Prices, add below rules for
the monthly recurring plan on creatred subscription product:
- Min Qty = 0 then Price = 0.
- Min Qty = 10 then Price = 10.
- Min Qty = 20 then price = 20.
(This setup ensures the product is free when the quantity is less than 10, and
pricing increases based on the defined quantity tiers.)
- Create a sale order > Set a customer > Select the recurring plan as `Monthly` > Apply the created pricelist.
- Add the subscription product with quantity = 20 then Unit price is correctly set to 20.
- Change the quantity to 1.
Observation:
---
- The unit price does not update to 0 as expected.
Root cause:
---
- At [1], when quantity is updated to 1, `super()._get_pricelist_price()` correctly returns `0`.
- However, due to the `or self.price_unit` condition, the existing unit price is retained instead of applying the new value.
- This prevents valid pricelist rules (including zero-priced ones) from being applied.
Fix:
---
- As shown in [2], `line.pricelist_item_id` is set when a matching pricelist rule is found. Therefore, when a rule (including one for quantity = 0) applies, it will be reflected in `line.pricelist_item_id`.
- If `pricelist_item_id` is set, the computed price from the rule must be used; otherwise, fall back to the existing unit price.
[1]: https://github.com/odoo/enterprise/blob/7498010b8206df56fc5d8a9fd08b24169bf08cb2/sale_subscription/models/sale_order_line.py#L651-L654
[2]: https://github.com/odoo/odoo/blob/fab39bbea642c1a185e6cb91afea860fa0c0f27d/addons/sale/models/sale_order_line.py#L547-L557
opw-6122234
---This update significantly speeds up the process of adding and removing participants from marketing campaigns. The change optimizes a key function within the marketing automation module, reducing processing time from over 51 seconds to just 0.65 seconds when handling large campaigns (over 115,000 participants). This improves overall campaign management efficiency.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
This update resolves an issue where PE tax reports in Odoo 18.4 couldn't generate closing entries due to a change in the accounting workflow. The fix adds a dedicated Peruvian tax report variant and a Return Type, allowing users to correctly configure tax accounts and trigger the closing process safely, particularly in multi-VAT environments.
Original PR description
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that…
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that you need a Return Type in order to make a Closing Entry using the Validate button Additionally, using the Generic Tax Report by default creates a risk in Multi-VAT environments, as it mixes taxes from all countries instead of isolating Peruvian taxes ### Cause The new 18.3 accounting workflow requires at least one active Return Type associated with a country-specific report variant to display the Return options and process the closing entry Peru was relying on the Generic Tax Report, without a dedicated report variant No Return Type was configured, which blocked Odoo's automatic VAT closing workflow and prevented the system from prompting the user to configure the required closing accounts ### Steps to reproduce - Install `l10n_pe_reports` and `accountant` - Switch to a PE Company - Go to the Tax Report Before the fix, no Returns button is available for any of the existing reports, making it impossible to use Odoo's automatic process to configure the tax accounts and trigger the closing entry ### Notes This is fixed by creating a dedicated Peruvian tax report variant directly in Enterprise that inherits from the generic tax report A custom handler is added to force the domain filtering on Peruvian taxes only, and a corresponding Return Type is defined to restore the full closing entry process safely opw-5978673 Forward-Port-Of: odoo/enterprise#117891
This update resolves a potential error in the Hong Kong payroll calculations. The fix ensures the system doesn't divide by zero when a company's resource calendar is missing or if an employee has zero hours per week. This prevents inaccurate payroll processing and ensures correct payments.
Original PR description
. Add a check for a null resource calendar and zero hours per week. task-6229271 Forward-Port-Of: odoo/enterprise#117685
6 changes
Resolved issues and error corrections
This update resolves a bug where importing XML bills with identical filenames would create multiple vendor bills. The fix ensures attachments are correctly linked to the appropriate accounting records, preventing duplicate bill creation. This improves data accuracy and simplifies invoice processing.
Original PR description
Fixup of https://github.com/odoo-dev/odoo/commit/3fc85b6ed7936956abbaf8e8364bb2b288cbe289 Issue 1 - Import XML bill into documents app - Create vendor bill from the document Issue: Only the main attachment would be found in the created bill opw-6267888 Issue 2 - From the accounting app import XML bill containing two identically named documents Issue: Two bills were created opw-6231265 Forward-Port-Of: odoo/odoo#268850 Forward-Port-Of: odoo/odoo#268310
This update fixes an error in the Colombian DIAN invoice processing flow. Previously, the system incorrectly flagged invoices due after 5 PM Colombia time as invalid due to a timezone mismatch. The fix ensures invoices are validated against the correct Bogota local time, resolving the issue and allowing proper DIAN document submission.
Original PR description
**Steps to reproduce:** * Install `l10n_co_edi` module with DEMO DIAN mode enabled. * Go to Accounting > Vendor > Bills and create a new bill. * Select any Colombian partner different from…
**Steps to reproduce:** * Install `l10n_co_edi` module with DEMO DIAN mode enabled. * Go to Accounting > Vendor > Bills and create a new bill. * Select any Colombian partner different from `Consumidor Final`. * Set the invoice date to 6 days in the past. * Select the DIAN Support Documents journal and a product with UNSPSC category. * Confirm the bill and click `Send Support Document to DIAN` after 5 PM Colombia time. **Observed behavior:** * An error is raised stating the issue date cannot be older than 6 days or more than 6 days in the future, even though the invoice date is within the allowed window in Colombia local time. **Cause:** * The date window validation in `_check_move_configuration` used `fields.Datetime.now()` which returns UTC time. Since Colombia is UTC-5, after 5 PM local time the UTC clock has already rolled over to the next calendar day, making a 6-day-old invoice appear 7 days old and failing the validation incorrectly. **Fix:** * Convert the current UTC datetime to the `America/Bogota` timezone and extract its local date before computing the allowed date window. * Compare directly against `move.invoice_date` (a `date` field) instead of using `fields.Datetime.to_datetime()`, keeping the comparison consistent as `date` vs `date`. opw-6011502 Forward-Port-Of: odoo/enterprise#119794 Forward-Port-Of: odoo/enterprise#115256
This update corrects a missing reference field on Fedex shipping labels. The label now includes a 'CustomerReference' field, as required by the Fedex API, ensuring accurate tracking and documentation of stock transfers. This resolves an issue where the reference was absent, potentially causing delays or discrepancies in shipping processes.
Original PR description
Backport of bb4f8bf Original PR #116870 Forward-Port-Of: odoo/enterprise#118967 Forward-Port-Of: odoo/enterprise#117873
A recent update was causing the Asset Depreciation Schedule report to crash when dealing with a large number of assets grouped together. This fix ensures the report handles missing data correctly, preventing errors and allowing users to accurately analyze their assets even with extensive groupings. This resolves a critical issue impacting report usability for our customers.
Original PR description
#### Description of the issue/feature this PR addresses: Opening the Asset Depreciation Schedule report with a period comparison enabled crashes with KeyError: 'no_format' when prefix grouping is…
#### Description of the issue/feature this PR addresses:
Opening the Asset Depreciation Schedule report with a period comparison enabled crashes with KeyError: 'no_format' when prefix grouping is active (large number of assets in one account group). The report becomes unusable for affected customers.
#### Current behavior before PR:
_regroup_lines_by_name_prefix sums each subline column by indexing prefix_subline['columns'][i]['no_format'] directly. Empty columns are built as {} by _build_column_dict (both col_value and col_data are None), so they have no 'no_format' key. With a comparison period enabled, an asset that has no value in the comparison period produces an empty column for that period; once prefix grouping fires (len(lines) >= prefix_groups_threshold, default 4000), the direct lookup hits that empty dict and raises KeyError: 'no_format'.
#### Desired behavior after PR is merged:
The prefix group total treats a missing 'no_format' as 0, matching the sibling caller in account_asset/models/account_assets_report.py that already guards with .get('no_format', 0). The report builds without crashing and the empty comparison column contributes 0 to the prefix group total.
opw-6225639
Forward-Port-Of: odoo/enterprise#119088A bug was causing grouped payments to incorrectly link existing invoices to new, unrelated invoices. This update fixes the issue by ensuring payments are properly associated with the intended invoices, preventing data duplication and improving payment reconciliation accuracy. This resolves a problem where payments were spreading across multiple invoices.
Original PR description
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially…
Steps to reproduce --- 1. Register a grouped customer payment over several invoices, leaving one of them only partially paid. 2. Register a second grouped payment over two invoices: the partially paid one and a brand new invoice. 3. Open the first payment, its "Reconciled Invoices" smart button now lists the new invoice from the second payment, which it never paid. Issue --- The smart button is built from the stored `invoice_ids` many2many, which shares its relation table with `account.move.matched_payment_ids`. After reconciling, the register wizard links the payment to its invoices with `lines.move_id.matched_payment_ids += payment` at https://github.com/odoo/odoo/blob/f726393267a28cedd5febd2106de17ae3838f3ff/addons/account/wizard/account_payment_register.py#L1212. When the payment groups several invoices, `lines.move_id` is a multi-record recordset. Reading `matched_payment_ids` on it returns the union of the payments already linked to all those invoices, and `+=` writes that union back to every invoice as a `(6, 0, ...)` replace command. So an invoice already paid by an earlier payment spreads that earlier payment onto every other invoice grouped in the new one, including brand new invoices, which then wrongly appear on the earlier payment. opw-6188013 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267968
This update significantly improves the speed of syncing participants to marketing campaigns. By optimizing the underlying code, the process now takes just a fraction of the previous time – reducing it from over 51 seconds to less than a second. This change enhances the performance of our marketing automation tools, especially when managing large campaigns.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
5 changes
Resolved issues and error corrections
This update resolves an issue where both units of a quality check would be incorrectly moved to the failure location after a partial failure. The fix ensures that the destination of a move line is only updated when there's no remaining demand, preventing unintended movement to the failure location. This improves the accuracy of quality control processes.
Original PR description
Version: ---------- - 18.0+ Steps to reproduce: ------------------- 1. Install *quality_control* module. 2. Go to *Settings* and enable *Storage Locations*. 3. Open Quality module go to the Quality…
Version:
----------
- 18.0+
Steps to reproduce:
-------------------
1. Install *quality_control* module.
2. Go to *Settings* and enable *Storage Locations*.
3. Open Quality module go to the Quality control -> Quality points
4. Create a *Quality Point* with:
* *Product* set.
* *Control per* set to *Quantity*.
* *Operation* set to *Receipts*.
* *Failure Location* set to *WH/Stock/Shelf1*.
5. Create a *Receipt* with demand of *2 units* for the product used in QP.
6. Mark the quality check as *To Do*.
7. Update the *Done Quantity* to *1*.
8. Open the quality check and click *Fail*.
9. Update the *Done Quantity* back to *2* and save.
10. Open the quality check again, click *Pass*, and validate the receipt.
11. Open the *Detailed Operations* to inspect move lines.
Issue:
------
* Both units (failed and passed) are moved to the *failure location*.
Cause:
------
When a user fails a move line via the QC wizard, the flow is:
do_fail() → show_failure_message() → confirm_fail()
→ check._move_to_failure_location(failure_location_id, failed_qty)
Inside `_move_to_failure_location`, when `failed_qty == move_line.quantity`,
the condition:
https://github.com/odoo/enterprise/blob/a33f580455a54a81d89a848f7b493d9dcc9ba2b2/quality_control/models/quality.py#L458
e.g. 1 == 1
was True even when `move.product_uom_qty = 2` (demand still 2). It only
compared the done quantities, ignoring that unfulfilled demand remained.
As a result, `move.location_dest_id` was set to the failure location.
Later, when the user increases the quantity from 1 to 2 on the move form,
the flow is:
_set_quantity → process_increase → _set_quantity_done → _prepare_move_line_vals
In `_prepare_move_line_vals` :
'location_dest_id': self.location_dest_id.id,
https://github.com/odoo/odoo/blob/47bf284e1e9d8be0d4255418e0a3f67c74fa5114/addons/stock/models/stock_move.py#L1688
The new move line inherits `move.location_dest_id` directly, which at this
point is already the failure location.
When the user then calls `do_pass()` on the second unit, `do_pass()` only
writes `quality_state = 'pass'` and never touches `location_dest_id`. So
the second (passed) move line silently retains the failure location.
Solution:
---------
Add the guard `move.product_uom_qty <= move_line.quantity` to the condition
so the entire move's destination is only redirected when there is genuinely
no remaining unfulfilled demand:
When demand > done qty, the else-branch runs instead: it reduces the
original move's demand and creates a new separate move pointing to the
failure location, leaving the original move's `location_dest_id` pointing
to stock. Any subsequent move lines created on the original move therefore
correctly inherit the stock destination.
---
opw-6080871
Forward-Port-Of: odoo/enterprise#112859A recent update caused the Asset Depreciation Schedule report to crash when dealing with many assets grouped together. This fix ensures the report handles empty asset values correctly, preventing errors and allowing customers to generate reports without interruption. The change aligns with existing safeguards to ensure data integrity.
Original PR description
#### Description of the issue/feature this PR addresses: Opening the Asset Depreciation Schedule report with a period comparison enabled crashes with KeyError: 'no_format' when prefix grouping is…
#### Description of the issue/feature this PR addresses:
Opening the Asset Depreciation Schedule report with a period comparison enabled crashes with KeyError: 'no_format' when prefix grouping is active (large number of assets in one account group). The report becomes unusable for affected customers.
#### Current behavior before PR:
_regroup_lines_by_name_prefix sums each subline column by indexing prefix_subline['columns'][i]['no_format'] directly. Empty columns are built as {} by _build_column_dict (both col_value and col_data are None), so they have no 'no_format' key. With a comparison period enabled, an asset that has no value in the comparison period produces an empty column for that period; once prefix grouping fires (len(lines) >= prefix_groups_threshold, default 4000), the direct lookup hits that empty dict and raises KeyError: 'no_format'.
#### Desired behavior after PR is merged:
The prefix group total treats a missing 'no_format' as 0, matching the sibling caller in account_asset/models/account_assets_report.py that already guards with .get('no_format', 0). The report builds without crashing and the empty comparison column contributes 0 to the prefix group total.
opw-6225639
Forward-Port-Of: odoo/enterprise#119088This update fixes a bug where untaxed invoice lines in German accounting reports incorrectly inherited the Datev code from the previous line. This resulted in inaccurate Datev reports, which are crucial for compliance. The fix ensures untaxed lines have the correct, empty Datev code, improving report accuracy and reliability.
Original PR description
**PROBLEM** Untaxed move lines would take the datev code of the previous line instead of having no datev code like they should. **STEP TO REPRODUCE** 1. On a german company, create an invoice with a line with tax 19% I, and a line that is untaxed (with a non-null price). 2. On the general ledger, generate the datev zip. 3. Unzip, and open the account entries csv, and notice the 2nd line of the invoice as the datev code set to something instead of it being empty (column BU-Schlüssel). opw-6141003 Forward-Port-Of: odoo/enterprise#118486
This update resolves an error that occurred when creating payment reports for Swiss companies using the ‘l10n_ch_hr_payroll’ module. The issue stemmed from a missing module dependency, causing a value error during report generation. This fix ensures the payment report functionality works correctly for all company types.
Original PR description
*=l10n_ch_hr_payroll,hr_payroll_account_iso20022 When clicking the create payment report button on a payslip for a Swiss company, a traceback occurs if the ``hr_payroll_account_iso20022`` module is…
*=l10n_ch_hr_payroll,hr_payroll_account_iso20022 When clicking the create payment report button on a payslip for a Swiss company, a traceback occurs if the ``hr_payroll_account_iso20022`` module is not installed. Steps to reproduce the error: - Install ``l10n_ch_hr_payroll`` module - Switch to CH Company - Create an Employee and running contract for it - Go to Payroll > Payslip > All payslips > Create a new payslip > Set the employee > Confirm > Create payment report Traceback: ```py ValueError: Wrong value for hr.payroll.payment.report.wizard.export_format: 'iso20022_ch' ``` https://github.com/odoo/enterprise/blob/7792926504a823590fbbe574a96994002a92fc17/l10n_ch_hr_payroll/models/hr_payslip.py#L383 https://github.com/odoo/enterprise/blob/7792926504a823590fbbe574a96994002a92fc17/l10n_ch_hr_payroll/models/hr_payslip_run.py#L13 Here, ``iso20022_ch`` is passed as ``export_format``, However, ``iso20022_ch`` is added to the selection field in the ``hr_payroll_account_iso20022`` module at [1]. When that module is not installed, the selection value does not exist, leading to the above error. [1]: https://github.com/odoo/enterprise/blob/7792926504a823590fbbe574a96994002a92fc17/hr_payroll_account_iso20022/wizard/hr_payroll_payment_report_wizard.py#L11 sentry-7391832811 Forward-Port-Of: odoo/enterprise#113277
This update significantly speeds up the process of adding and removing participants from marketing campaigns, particularly for large campaigns. The change optimizes how the system identifies and manages participants, reducing processing time from over 51 seconds to just 0.65 seconds. This improvement enhances campaign performance and user experience.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
6 changes
Resolved issues and error corrections
This update ensures field service technicians are routed to the correct job site by automatically selecting the delivery address associated with a project or task, instead of the company headquarters. This resolves issues where technicians were directed to the wrong location, improving efficiency and accuracy.
Original PR description
Currently, when creating a planning shift and selecting a project or task, the shift's customer (`partner_id`) defaults to the main partner (the parent company). This causes issues for field service technicians, as the Map view routes them to the company headquarters instead of the actual physical job site. This commit improves the behavior by attempting to fetch the 'delivery' child contact of the partner linked to the task or project. If a delivery address exists, it is set as the default on the shift. If it does not exist, it falls back to the main partner. Task-6132902
This update ensures that the phone numbers dialed through the Odoo Enterprise system now use the sanitized E164 format, rather than the raw, user-entered number. This change improves accuracy and reliability when making calls or sending messages, aligning with updated phone widget functionality.
Original PR description
This commit ensures that the sanitized phone number is the one passed to the softphone + displayed instead of the raw phone number. Task-5184717 Community: https://github.com/odoo/odoo/pull/261001
This update fixes an issue where the manual prorata year selection for Belgian VAT returns wasn't being saved correctly. Previously, the system defaulted to the current year regardless of user input. The fix ensures that the user's chosen prorata year is accurately reflected in the generated VAT XML, improving data accuracy and compliance.
Original PR description
### Issue before this commit: Manually modifying the "Prorata Year" in the Belgian VAT return lock wizard was ignored. The generated XML always exported the tax return's current year instead of the user's input. ### Steps to reproduce the issue: 1. Download Accounting and l10n_be 2. Switch to BE company 3. Open a tax return for 7 April (as an example) 4. Try to validate the VAT March 2026 ### Cause of the issue: The prorata_year field was a computed field lacking the store=True attribute. Upon validation, the unsaved manual input was lost, triggering the compute method which blindly overwrote it with the default year. https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/l10n_be_reports/wizard/vat_return_lock_wizard.py#L18 ### Reason to introduce the fix: Adding store=True ensures the user's manual override is persisted in the database and correctly injected into the Intervat XML payload. opw-6165520
This update fixes an issue where the activity rate used in Swiss payroll calculations was incorrectly tied to individual employees. Now, the rate is based on the Odoo Enterprise version, ensuring accurate and compliant payroll processing for Swiss businesses. This change improves the reliability of financial reporting and payroll accuracy.
Original PR description
…ployee Forward-Port-Of: odoo/enterprise#119658
This update fixes an issue where flexible schedules were incorrectly calculating work hours, leading to inaccurate payroll totals. The change decouples the hour splitting logic, now relying on specified daily hours instead of attendance hours, ensuring accurate half-day and full-day calculations.
Original PR description
Steps: - Create half day off for an employee - Create a full day off of the same type - Create a payslip for the employee Issue: - Due to the lack of attendance hours in the flexible schedules, the _get_work_hours_split_half is unable to split half day and full days work entries of the same type. - Half worked days will be rounded up which affects the total number of work days in a month Solution: The approach was to decouple the work_hours_split_half functionality from the attendance hours and rely on the specified hours_per_day instead. This accurately splits half and full days. Task: 6253675 Forward-Port-Of: odoo/enterprise#118968 Forward-Port-Of: odoo/enterprise#118836
This update refines the calculation of worked days for payroll, addressing inconsistencies and improving accuracy. The changes enhance the reliability of payslip generation, ensuring employees are paid correctly based on their actual working hours. This update impacts the core payroll functionality.
11 changes
Resolved issues and error corrections
This update fixes an issue where the VAT reports for Spanish companies were incorrectly including withholding taxes in the total VAT calculation. The fix excludes 'retencion' (withholding tax) from the VAT calculation, ensuring accurate VAT reporting figures. This improves the reliability of financial reports.
Original PR description
Step to reproduce - install `l10n_es_reports` and switch to ES company - create a invoice, add a product, set price = 100 - add two taxes (one should be withholding tax) ex: 21%G and 19%whi - confirm it, total payable is now 100 + 21 - 19 = 102 - open vat Books report for ES, see line for this invoice Observation: - for this invoice, in total vat column, we get 102 value - it should be 100+ 21 i.e 121 as we do not include withholding taxes in total vat Cause: - the query for report used to sum up all the taxes for calculating vat Fix: - excluded tax of type "retencion" in tax summation opw-6082329
This update resolves an issue where generating the general ledger report could create extremely large PDF files due to lengthy invoice references. By limiting the length of invoice references, we prevent the PDF from becoming bloated and ensure reports generate reliably, avoiding system errors related to file descriptor limits.
Original PR description
The display name of the account.report.line in the general ledger report has the format of: INVOICE NAME (invoice refs) In the case where a client has hundreds of sales orders batched to a single…
The display name of the account.report.line in the general ledger report has the format of: INVOICE NAME (invoice refs) In the case where a client has hundreds of sales orders batched to a single invoice, the ref can become extremely long, e.g.: INV/2026/00001 (S12123, S12152, S12159, S12140, S12165, S12161, S12162, S12110, S12099, S12124, S12145, S12128, S12114, S12131, S12097, S12185, S12154, S12133, S12190, S12118, S12116, S12102, S12155, S12153, S12158, S12150, S12100, S12142, S12121, S12122, S12111, S12187, S12172, S12177, S12095, S12117, S12144, S12137, S12092, S12138, S12186, S12182, S12112, S12148, S12183, S12101, S12178, S12119, S12169, S12115, S12146, S12093, S12126, S12160, S12163, S12129, S12098, S12151, S12096, S12174, S12120, S12130, S12147, S12180, S12191, S12164, S12141, S12105, S12136, S12139, S12109, S12106, S12104, S12103, S12175, S12179, S12188, S12113, S12173, S12167, S12171, S12134, S12094, S12184, S12166, S12170, S12125, S12135, S12143, S12176, S12189, S12156, S12181, S12107, S12157, S12132, S12149, S12127, S12108, S12168...) Because the length of the account.report.line is unchecked in account_general_ledger.py label builder, the pdf can clog to one or two account.report.lines per page, skyrocketing the pdf page length. As wkhtmltopdf processes the report from html to pdf it makes a system call openat() to the /tmp/report.footer.tmp.x.html file for EACH page of the pdf. You can see the TODO comment in the spoolTo function in wkhtmltopdf (both in Odoo and the original repo) saying that the header and footer need to be freed, on each page processing, not just null pointed. https://github.com/odoo/wkhtmltopdf/blob/2c884bd1545b8a639847de22f24754ee5a6fc44c/src/lib/pdfconverter.cc#L794 I verified that that the number of openat calls to the /tmp/report.footer.tmp.x.html file equals the exact number of pages in the pdf to be generated if the report HAD generated successfully by setting the footer input into _run_wkhtmltopdf to None, generating the report without footers, then separately running an strace on wkhtmltopdf when the report fails to generate. See related ticket linked at bottom. The linux machine used on sh instances has a ulimit -n of 1024 file descriptors. Because the footer file descriptors accumulate, once a pdf has about 1010+ pages (~a dozen fd's are allocated for other purposes), over 1024 file descriptors are opened and the system fails with: Wkhtmltopdf failed (error code: -6). Message: QEventDispatcherUNIXPrivate(): Unable to create thread pipe: Too many open files QEventDispatcherUNIXPrivate(): Can not continue without a thread pipe Since wkhtmltopdf is archived and Odoo has a replacement in development, I suggest that we limit the display_name of the account.report.line to 200 to keep the bloat minimized, preventing one account.report.line's name from taking up an entire page of the general ledger pdf. This allows many more batched invoices to be shown in the report and a much greater time range of data to be printed without hitting the fd limit. I suggest changing it at the general ledger report level rather than in the account.move.line _compute_display_name function, as we probably still want to see the full display_names at the invoice level. On runbot, the machine has different memory constraints than on sh / local, so it hits the following error before the one above: Wkhtmltopdf failed (error code: -11). Memory limit too low or maximum file number of subprocess reached. Message : Steps to Reproduce on 19.0 newdb: 1. newdb -n test_gl -v 19.0 2. ensure ulimit is set to 1024 in shell that runs odoo instance by running ulimit -n 1024 to mimic ulimit of sh environment 3. run db with python3 odoo-bin, ensuring high enough memory constraints to simulate multi worker sh instance, i.e. --limit-memory-soft=12884901888 --limit-memory-hard=1288490188 4. install sales, accounting, stock 5. install demo data 6. create invoices with 100+ associated sales orders 7. generate the pdf 8. Increase the amount of invoices till the general ledger page count hits ~1010+, where you will hit the error. Notes: opw-ticket-6201508 closes #118067
This update addresses a performance issue within the Dimona payroll module for Belgium. By adding missing indexes to key database tables, the system now processes payroll calculations more efficiently, reducing potential delays and improving overall responsiveness. This enhancement ensures smoother and faster payroll processing for our Belgian clients.
Original PR description
opw-6241383 runbot-233176
This update resolves an issue where inventory counts weren't accurately recording products without lot numbers. The fix ensures that new units without lots are correctly added to inventory counts, preventing miscounts and improving data accuracy. It addresses a validation error related to how the system handles lotless products during inventory adjustments.
Original PR description
### Steps to reproduce: 1. Create a product tracked by lot 2. Put 10 units in WH/Stock without lot 3. Inventory > Operations > Adjustments > Physical Inventory 4. Select the line referring to your…
### Steps to reproduce: 1. Create a product tracked by lot 2. Put 10 units in WH/Stock without lot 3. Inventory > Operations > Adjustments > Physical Inventory 4. Select the line referring to your product and request an inventory count + Show Expected Quantity 5. Open the barcode app > Count Inventory 6. Scan your product #### > The line is not selected, in particular, next scans will be re-interpreted as product scans rather than new serial creation for your product. ### Cause of the issue: Scanning your product search a line to select if any: https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/stock_barcode/static/src/models/barcode_model.js#L1432-L1435 https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/stock_barcode/static/src/models/barcode_model.js#L1630-L1632 However, the `findLine` will fail since this method calls the `_canOverrideTrackingNumber` to determine if the lot of the barcodData matches the one of the line: https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/stock_barcode/static/src/models/barcode_model.js#L1859-L1863 But, the override of the `_canOverrideTrackingNumber` method for the `BarcodeQuantModel` does not handle the absence of lotName in the barcodeData correctly as it does not consider that a line without lot can be overridden by an empty lotName: https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/stock_barcode/static/src/models/barcode_quant_model.js#L729-L731 Note however that the super call does: https://github.com/odoo/enterprise/blob/bce04ce24b66fb1a2481274eb3aebdc30a62766e/stock_barcode/static/src/models/barcode_model.js#L795-L798 ### Issue 2: ### Steps to reproduce: - Steps 1 -> 5 - Click on your product line to select it - Scan a new lot to add one new unit referring to that lot - Confirm (1) - Apply Now #### > User Error: Quant's editing is restricted, you can't do this operation Since the line is selected, you have a currentLine during the `processBarcode` and hence the existing line will be updated using the `lotName``: https://github.com/odoo/enterprise/blob/cf3c2fce8a6b7b2d7547d44a0e4423f887986d52/stock_barcode/static/src/models/barcode_model.js#L1560-L1584 However, writing on the line will then try to write on the related quant during the validation process which will be forbiden since we are not allowed to change the lot of an existing quant: https://github.com/odoo/odoo/blob/e3b0ca11d99b2ef819cdad68b169112cd73668b6/addons/stock/models/stock_quant.py#L351-L360 Now, the issue is that actually due to the nature of the line and of the barcode data, the line lot is not expected to be updated but rather a new line is expected to be created: https://github.com/odoo/enterprise/blob/cf3c2fce8a6b7b2d7547d44a0e4423f887986d52/stock_barcode/static/src/models/barcode_model.js#L795-L798 Additional issue: Fixing issue 1 and 2 highlight and other issue of the validation process: - Steps 1 -> 6 > The line gets selected - Scan a newlot > a new subline is added referring to 1 unit of your new quant - Confirm (1) > Some serials where not counted, set them as missing #### > Check your quants: the 10 unit lotless quant was not updated but a new quant for 1 units was created for your newlot ### Cause of the issue: Applying all quantities is expecting to toggle them as counted before applying to update the existing quants: https://github.com/odoo/enterprise/blob/c8535a7a0e2eae811048a34a0bae187a1fa45311/stock_barcode/static/src/models/barcode_quant_model.js#L72-L82 https://github.com/odoo/enterprise/blob/c8535a7a0e2eae811048a34a0bae187a1fa45311/stock_barcode/static/src/models/barcode_quant_model.js#L287-L296 However, only line tracked by serial numbers are set as counted: https://github.com/odoo/enterprise/blob/c8535a7a0e2eae811048a34a0bae187a1fa45311/stock_barcode/static/src/models/barcode_quant_model.js#L60-L63 opw-6212923
This update corrects a bug in the accrual reports (like 'Bill To Receive') that caused group totals for 'Received,' 'Billed,' and 'Amount' to incorrectly show as zero. The fix ensures these reports accurately reflect aggregated data, which is essential for accountants during financial closing processes. This improves the reliability of key financial reporting.
Original PR description
### Issue before this commit: In accrual reports (e.g., "Bill To Receive", "Billed Not Received", "Invoices To Be Issued", and "Invoices Not Delivered"), when grouping the list view by fields such as…
### Issue before this commit: In accrual reports (e.g., "Bill To Receive", "Billed Not Received", "Invoices To Be Issued", and "Invoices Not Delivered"), when grouping the list view by fields such as Vendor, the group header totals for the "Received", "Billed", and "Amount" columns display 0.00 even if the interanl lines of the group are not 0.00. ### Steps to reproduce the issue: 1. Download Purchase Accounting and Sale Accounting 2. Go to one of this pages: Billed Not Received, Bill To Receive, Invoices To Be Issued, and Invoices Not Delivered 3. Ensure the view is in its default grouping (grouped by Vendor or Customer) 4. Observe the group header rows for the Received (or Delivered), Billed (or Invoiced), and Amount columns. They all display 0.00 5. Expand a group that contains records with values greater than zero 6. Observe that the individual records populate correctly, but the aggregated group header row continues to display 0.00. ### Cause of the issue: The commit ddc1b681656ea8c70f3231cda20b5a58b9ff7dd6 adapted the code to retrieve the new accrual reports but attempted to fetch grouped records using group[0].id as the dictionary key, while the grouped() method actually used the recordset object as the key. This mismatch caused the dictionary lookup to fail, resulting in 0.00 sums. https://github.com/odoo/enterprise/blob/c8535a7a0e2eae811048a34a0bae187a1fa45311/account_accountant/models/analytic_mixin.py#L40-L48 ### Reason to introduce the fix: This fix restores the core analytical utility of the accrual reports, which are crucial for accountants during period-end closings to evaluate totals at a glance. opw-6232273
This update fixes a bug where untaxed invoice lines in German reports incorrectly inherited the Datev code from the previous line. The fix ensures that untaxed lines now properly display an empty Datev code, aligning with German tax regulations. This prevents reporting discrepancies and ensures accurate financial data for Datev.
Original PR description
**PROBLEM** Untaxed move lines would take the datev code of the previous line instead of having no datev code like they should. **STEP TO REPRODUCE** 1. On a german company, create an invoice with a line with tax 19% I, and a line that is untaxed (with a non-null price). 2. On the general ledger, generate the datev zip. 3. Unzip, and open the account entries csv, and notice the 2nd line of the invoice as the datev code set to something instead of it being empty (column BU-Schlüssel). opw-6141003 Forward-Port-Of: odoo/enterprise#118486
This update ensures that undoing the auto-plan feature correctly preserves the initial workload assigned to a shift. Previously, the system recomputed the workload, leading to inaccurate shift allocations. This fix maintains the intended functionality of the auto-plan process.
Original PR description
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation…
Steps to Reproduce --- 1. Open the Planning module 2. Create an open shift with allocated_hours 3. Click Auto Plan 4. Click Undo on the auto-plan notification Issue --- Undoing an auto-plan operation triggers recomputation of allocated_hours, causing the shift to lose its original workload value. Current Behaviour --- When resource_id is set to False during undo: - _compute_allocated_hours is triggered (depends on resource_id) - _compute_allocated_percentage is triggered (depends on allocated_hours) - Both fields are recalculated, potentially changing allocated_hours from its pre-assignment value Expected Behaviour --- Undoing auto-plan should preserve allocated_hours at its pre-assignment value while allowing allocated_percentage to adapt to the new context (open slot vs assigned resource). Fix --- Use protecting context manager in action_rollback_auto_plan_ids to prevent allocated_hours from being recomputed when resource_id is removed. This allows allocated_percentage to recalculate naturally based on slot duration while keeping allocated_hours stable. task - 4952149 Forward-Port-Of: odoo/enterprise#119772 Forward-Port-Of: odoo/enterprise#102864
This update significantly speeds up the process of adding and removing participants from marketing campaigns. By optimizing a key database function, the system now completes this task in just a fraction of the time – reducing it from over 51 seconds to less than a second. This improvement will result in quicker campaign updates and a smoother user experience.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334 Forward-Port-Of: odoo/enterprise#119589 Forward-Port-Of: odoo/enterprise#117656
This update resolves an issue where Peruvian tax reports couldn't generate closing entries after the 18.3 update. The fix creates a specific tax report variant for Peru, ensuring accurate VAT calculations and restoring the automated closing entry process for Peruvian businesses. This prevents errors and allows for proper tax account configuration.
Original PR description
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that…
### Issue Since the introduction of the Tax Returns feature in 18.3, it was no longer possible to generate a Closing Entry as the button has been replaced by Returns The Return mechanism implies that you need a Return Type in order to make a Closing Entry using the Validate button Additionally, using the Generic Tax Report by default creates a risk in Multi-VAT environments, as it mixes taxes from all countries instead of isolating Peruvian taxes ### Cause The new 18.3 accounting workflow requires at least one active Return Type associated with a country-specific report variant to display the Return options and process the closing entry Peru was relying on the Generic Tax Report, without a dedicated report variant No Return Type was configured, which blocked Odoo's automatic VAT closing workflow and prevented the system from prompting the user to configure the required closing accounts ### Steps to reproduce - Install `l10n_pe_reports` and `accountant` - Switch to a PE Company - Go to the Tax Report Before the fix, no Returns button is available for any of the existing reports, making it impossible to use Odoo's automatic process to configure the tax accounts and trigger the closing entry ### Notes This is fixed by creating a dedicated Peruvian tax report variant directly in Enterprise that inherits from the generic tax report A custom handler is added to force the domain filtering on Peruvian taxes only, and a corresponding Return Type is defined to restore the full closing entry process safely opw-5978673 Forward-Port-Of: odoo/enterprise#117891
This update resolves an issue where the barcode inventory count feature would fail when using archived units of measure. The fix ensures that archived UoMs are correctly included in the inventory count cache, allowing accurate counts to be performed. This prevents errors during physical inventory adjustments.
Original PR description
### Steps to reproduce: - In the settings enable: "Units of Measure & Packagings", "Storage Locations" - Create a product in units and register 1 unit in stock - Inventory > Operations > Adjustments…
### Steps to reproduce: - In the settings enable: "Units of Measure & Packagings", "Storage Locations" - Create a product in units and register 1 unit in stock - Inventory > Operations > Adjustments > Physical Inventory - Select your line and request a count > Set Current Value - Inventory > Configurations > units of measures > UOM categories - Select unit and archive it - Go to the barcode app > Click Count inventory ### > Owl error: Uncaught promise ### Cause of the issue: Since the uom used on the quant is archived, it is not found by the search used to fill the barcodeCache: https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/components/main.js#L209-L213 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/models/stock_quant.py#L104-L106 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/components/main.js#L229 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/models/barcode_model.js#L37-L39 However, if the uom is not present in the barcode cache the `BarcodeQautnModel` will fail to createLinesState whihc raises a missing error: https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/models/barcode_quant_model.js#L712 https://github.com/odoo/enterprise/blob/26546bcd3beebc7f65ce08385441b6284b46598e/stock_barcode/static/src/lazy_barcode_cache.js#L107-L110 opw-6250090 Forward-Port-Of: odoo/enterprise#118987 Forward-Port-Of: odoo/enterprise#118813
This update fixes an issue where barcode scanning incorrectly displayed delivered quantities on sales orders. The problem stemmed from how the system selected lines during delivery updates, leading to inaccurate order fulfillment. The fix ensures correct quantity updates when using barcode scanning with lots.
Original PR description
Currently when user adds adds quantity in barcode using lots it leads to incorrect sale order quantities. ## Steps to replicate: - Install Sales and Barcode (no demo data). - Enable Lots & Serial…
Currently when user adds adds quantity in barcode using lots it leads to incorrect sale order quantities.
## Steps to replicate:
- Install Sales and Barcode (no demo data).
- Enable Lots & Serial Numbers in settings.
- Create Test Product with Tracking by Lots.
- Go to Inventory > Products>Lots & Serial Numbers and create 3 lots for the product.
- Update each lot’s on-hand quantity to 10 from the product page.
- Create and confirm a Sales Order for the product (lines: qty 3 and 2 units).
- Open the delivery in the Barcode app:
- Scan lot 2 > increase qty to 3 using +1 button
- Scan lot 3 > increase qty to 2 using +1 button
- Validate and go to the sale order.
## Observed Behavior:
The sale order delivered quantities are flipped and a backorder is created even though the quantity for the product is satisfied.
## Root cause:
The issue occurs because when a sales order is confirmed, the system defaults to
using lot 1 on the delivery receipt. When a user scans lot 2, the `_processBarcode` function is triggered, which calls `_findLine` at [1] to select the appropriate line on the receipt.
As the loop in `_findLine` iterates through `pageLines` with values like:
```
[{display_name: "Test product", quantity: 3, lot_id: { name: 'lot1' }},
{display_name: "Test product", quantity: 2, lot_id: { name: 'lot1' }}]
```
During the first iteration, `foundLine` is set at [2] for the line with quantity 3 . Since the subsequent if condition is not satisfied, the loop hits the continue block at [3].
On the next iteration, the line with quantity 2 causes `foundLine` to be overwritten at [2], and the continue block is executed again at [3].
This results in the line with quantity 2 being selected as the line to update at the end of the function.
When the user manually increases the quantity to 3, the line that originally required quantity 2 is updated and fulfilled.
Later, when lot 3 is scanned, the line that required quantity 3 is selected for update, and manually increasing the quantity to 2 before validating the order leads to a backorder and causes the delivered quantities to be flipped.
[1]:
https://github.com/odoo/enterprise/blob/81eaeb4b18d849c644160234def6c69013591abe/stock_barcode/static/src/models/barcode_model.js#L1335-L1337 [2]:
https://github.com/odoo/enterprise/blob/81eaeb4b18d849c644160234def6c69013591abe/stock_barcode/static/src/models/barcode_model.js#L1690-L1699 [3]:
https://github.com/odoo/enterprise/blob/81eaeb4b18d849c644160234def6c69013591abe/stock_barcode/static/src/models/barcode_model.js#L1727-L1729
## Solution:
Avoid grouping lines from different moves unless using batch transfers. This ensures that backorders are not created when the barcode lines are fulfilled.
opw-5423943
Forward-Port-Of: odoo/enterprise#118085
Forward-Port-Of: odoo/enterprise#1090326 changes
Resolved issues and error corrections
This update fixes a security issue where users could view financial budgets belonging to other companies. The change adds a security rule to the budget module, ensuring that users only see budgets associated with the company they are actively working with. This enhances data privacy and control.
Original PR description
**Steps to reproduce:** - Install the `account_reports` module. - Create a new company. - Navigate to Accounting > Configuration > Financial Budgets. - Create a new budget record. - Switch to another company. - Open the list view of Financial Budgets. **Observation:** The budget record created in another company is still visible. **Root Cause:** The model `account.report.budget` does not have any record rule restricting access based on company. As a result, users can see financial budgets belonging to other companies even if they are not connected to them. **Fix:** This commit allows users to hide financial budgets from companies they are not connected to by adding a record rule on `account.report.budget` opw-6083892
This update fixes a bug where untaxed invoice lines were incorrectly inheriting the Datev code from the previous line, leading to inaccurate reports. The change ensures that untaxed lines now correctly have an empty Datev code, resolving a reporting discrepancy. This improves the accuracy of financial reports generated for German businesses using the Datev system.
Original PR description
**PROBLEM** Untaxed move lines would take the datev code of the previous line instead of having no datev code like they should. **STEP TO REPRODUCE** 1. On a german company, create an invoice with a line with tax 19% I, and a line that is untaxed (with a non-null price). 2. On the general ledger, generate the datev zip. 3. Unzip, and open the account entries csv, and notice the 2nd line of the invoice as the datev code set to something instead of it being empty (column BU-Schlüssel). opw-6141003 Forward-Port-Of: odoo/enterprise#118486
This update resolves a problem where DHL delivery confirmations were failing due to incorrect scheduled dates (past dates). The system now automatically sets a future date (one hour ahead) to ensure successful confirmation, preventing delivery errors and improving order processing.
Original PR description
When confirming the delivery of an order using DHL shipping method we get an error that the date must be in the future. This happens when the scheduled date was not set, or set for a time in the past. This commit automatically sets the time to 1 hour in the future and bypasses the user error. opw-6148927 Forward-Port-Of: odoo/enterprise#116211
This update resolves a rounding issue that occurred when generating PEPPOL invoices, specifically impacting unit prices. The change reverts a previous update that introduced this problem, ensuring accurate pricing calculations for international transactions. This improves the reliability of invoices for our business partners using PEPPOL.
Original PR description
Reverts https://github.com/odoo/odoo/pull/262242 opw-6293201
This update resolves a problem where the PDF Quote Builder generated incorrect data due to how it handled temporary records during the demo setup. The fix ensures that all data changes are immediately applied within the transaction, preventing inconsistencies and errors when the builder is enabled. This ensures the PDF quote builder functions correctly after demo data is used.
Original PR description
Steps to produce: --- - Install sale_management and website_sale modules with demo data. - From settings, disable the PDF Quote Builder. - Go to Settings > Technical > Sequences & Identifiers >…
Steps to produce:
---
- Install sale_management and website_sale modules with demo data.
- From settings, disable the PDF Quote Builder.
- Go to Settings > Technical > Sequences & Identifiers > External Identifiers.
- Delete the `consu_delivery_02_product_template` identifier.
- From settings, try to enable the PDF Quote Builder again.
Issue:
---
```py
insert or update on table "quotation_document_sale_pdf_form_field_rel" violates foreign key constraint
"quotation_document_sale_pdf_form_fie_quotation_document_id_fkey"
DETAIL: Key (quotation_document_id)=(1) is not present in table "quotation_document".
```
Cause:
---
When the XML demo loader processes **`sale_pdf_quote_builder_demo.xml`**, it calls `create()` on `quotation.document` for each of the 5 demo records one by one. Each create() call receives vals_list that contains datas, the actual PDF base64 content.
Inside `super().create(vals_list) `[1], the ORM writes datas to `ir_attachment`. Since `form_field_ids` is a `store=True` computed field with `@api.depends('datas')` [2], the ORM knows it needs to recompute `form_field_ids`. But it does not run the computation immediately. It simply registers the records in a pending recompute set and moves on. Nothing hits the DB yet for this compute.
After `super().create() `returns, the `write({'res_model': ..., 'res_id': ...})` runs. Since `res_model` and `res_id` live on `ir_attachment` (the parent table via _inherits), this write is also not immediately flushed to the DB. The ORM marks it as dirty in the cache and defers it.
So after all 5 records are created, the ORM holds two deferred things: a dirty UPDATE ir_attachment write for all 5 records, and a pending recompute for `form_field_ids` on all 5 records. Nothing has been flushed to the DB yet.
When the demo XML tries to reference `product.consu_delivery_02_product_template` [3], which no longer exists,
a ValueError is raised. From `load_demo()` [4], the savepoint is rolled back.
After the except block logs the failure, execution continues in l`oad_module_graph()` which calls `env.cr.commit()`. This triggers f`lush() -> transaction.flush() -> flush_all() -> _recompute_all()`. The ORM processes the pending recompute registry and finds `form_field_ids` needs recomputing for quotation.document(1, 2, 3, 4, 5). It calls `_compute_form_field_ids`() on those stale ids. Inside that compute, `_create_or_update_form_fields_on_pdf_records()` tries to insert into `quotation_document_sale_pdf_form_field_rel` with quotation_document_id=1. But quotation_document id 1 no longer exists in the DB — it was rolled back — so PostgreSQL raises the foreign key violation.
This is confirmed by the traceback from `_compute_form_field_ids`, which shows the call chain going through `commit() -> flush_all() -> _recompute_all()` rather than through `create()`, proving the compute fired after the rollback using stale ORM cache state.
Fix:
---
Calling `docs.flush_recordset()` at the end of `create()` forces all pending writes and pending recomputes to be executed immediately, while still inside the savepoint scope:
- It flushes the dirty res_model/res_id write on the ir_attachment parent table, so UPDATE ir_attachment hits the DB inside the savepoint.
- It triggers `_recompute_all()` for the pending `form_field_ids` compute on docs, so `_compute_form_field_ids()` runs inside the savepoint and the INSERT into `quotation_document_sale_pdf_form_field_rel` happens while the quotation_document rows still exist in the DB.
If the savepoint then rolls back, the ORM cache has nothing dirty or pending left. The subsequent commit() in `load_module_graph()` finds nothing to flush, so no stale writes execute against non-existent ids and no foreign key violation occurs.
[1]https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/sale_pdf_quote_builder/models/quotation_document.py#L93-L98
[2]https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/sale_pdf_quote_builder/models/quotation_document.py#L62-L71
[3]https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/addons/sale_pdf_quote_builder/data/sale_pdf_quote_builder_demo.xml#L44-L51
[4]https://github.com/odoo/odoo/blob/8791cdcd89ea3cb56b1fac63b3e2ffbd2956a912/odoo/modules/loading.py#L89-L90
opw-6139555
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update fixes an issue where product matching by name was incorrectly associating products across multiple lines in imports. The fix adds a necessary cache key, ensuring that products are matched accurately based on their unique characteristics, preventing incorrect product assignments during import processes. This improves data integrity and import accuracy.
Original PR description
**PROBLEM** When retrieving a product by name, there is no cache_key for the search_method criteria. This leads to the cache_key frozendict being an frozen dict with None values. This means, once we retrieve a first product with the search_method criteria, all following product will match its cache_key, so we ends up associating a product to all subsequent lines, even if they don't have anything in common. **STEP TO REPRODUCE** 1. Create a product with the name: "CASTELTORRE MERLOT DELLE VENEZIE 75CL 10,5i" (it's important the name is not exactly matching) 2. Import the xml which is attached to the bug fix ticket. 3. Notice the product column on all the lines after a certain point have the CASTELTORRE product, even though the corresponding line in the ubl is for another product. opw-6227280