Daily updates from Odoo
Wednesday, July 29, 2026
72 changes · saas-19.4
New functionality added to Odoo
Odoo now supports ShipStation as a native shipping option, giving businesses access to a wider network of US carriers and marketplaces from within their existing workflows. This helps reduce manual shipping work and supports key tasks such as label generation, carrier selection, and shipment tracking.
Original PR description
ShipStation is a cloud-based shipping and order-fulfillment platform that centralizes order management, processing, and shipping. It integrates with major e-commerce marketplaces (Amazon, eBay, Shopify, WooCommerce, Etsy) and connects to leading US carriers (USPS, UPS, FedEx, DHL). Odoo's current shipping integrations are limited to specific carriers and do not cover the broad range supported by ShipStation. Without native integration, businesses must rely on manual processes or third-party connectors that lack full functionality for shipping label generation and tracking management. Why another shipping aggregator alongside EasyPost and Sendcloud: - Market share: ShipStation has significantly more customers in the US than both other providers. - Freight support: EasyPost does not support freight. Since ShipStation merged into the Auctane conglomerate, their API covers freight, LTL, and flatbed trucking carriers. task-5185679
Enhancements to existing features
TikTok Shop configuration is now found under the new Marketplaces menu. This makes marketplace-related setup easier to find and keeps sales channel settings better organized.
Original PR description
- Move tiktok shop configuration to the new 'Marketplaces' menu PR Ref: https://github.com/odoo/enterprise/pull/113307
Users reconciling a bank transaction with a different partner will now be notified and can choose whether to move the bank account to the selected partner. This helps keep partner bank details accurate and reduces manual cleanup after reconciliation.
Original PR description
Add a new notification in the bank reco widget when a user do a reconciliation with a partner different from the one on the st_line. The idea is to let the user chose if he wants to move the bank account from the st_line partner to the move he tries to reconcile. task-6303397 Forward-Port-Of: odoo/enterprise#125702 Forward-Port-Of: odoo/enterprise#120900
Users who try to create an expense card before Stripe is connected are now directed to the relevant settings page. This helps them complete the required setup faster and reduces confusion when card creation cannot proceed.
Original PR description
When a user tries to create a card but the configuration is not connected, redirect the user towards the settings to do the connection. task-6272805 Forward-Port-Of: odoo/enterprise#125113
Australian payroll now applies a superannuation contribution cap in the relevant salary rule. This helps businesses stay aligned with ATO requirements for maximum superannuation contributions, including the updated guarantee calculation from 1 July 2026.
Original PR description
Added superannuation limit to the QE rule. Task-6221399 Forward-Port-Of: odoo/enterprise#119792
The ActivityWatch suggestions panel now shows total tracked time by project and a grand total for all suggestions. This gives users a clearer view of their logged hours before turning suggestions into timesheets.
Original PR description
This commit introduces new time tracking metrics to the ActivityWatch suggestions panel to improve user visibility into their tracked hours. **Enhancements:** - Added the total duration per project in the By Project grouped view. - Added a grand total footer for all suggestions at the bottom of the list. task-6088877 Forward-Port-Of: odoo/enterprise#125087 Forward-Port-Of: odoo/enterprise#114772
Bank statement reconciliation has been optimized to avoid timeouts in large multi-company databases. This should make scheduled reconciliation jobs run much faster and more reliably, reducing delays in accounting operations.
Original PR description
The standard cron process reconciles batches of 100 bank statement lines at a time. In multi-company environments with large historical datasets, this query frequently timed out, exceeding the…
The standard cron process reconciles batches of 100 bank statement lines at a time. In multi-company environments with large historical datasets, this query frequently timed out, exceeding the 15-minute execution limit. Even when throttled to a batch size of 10 records, the query required approximately 18.8 seconds to execute. The performance degradation was driven by the following factors: 1. Suboptimal Lateral Filtering: The statement line batch array filter was placed inside the `LATERAL` block's `WHERE` clause. This prevented the query planner from optimizing the drive path effectively across iterations. 2. Inefficient Join Sequence and Filtering: The original join order scanned `account_move_line` before resolving the company hierarchy constraint. As a result, millions of rows across all companies were retrieved from the index, forcing repeated primary key lookups on `res_company` before ultimately discarding over 99.9% of the records via the late `parent_path` hierarchy filter. This commit addresses these issues by: 1. Moving the `st_line.id` filtering constraint out of the lateral subquery and into the outer main query block to guide the execution path properly. 2. Reordering the inner `LATERAL` subquery to resolve the company hierarchy (`res_company`) prior to joining `account_move_line`. This constraints the scan boundaries early in the pipeline. Performance Benchmarks (10 record batch): - Before Execution Time: ~18,818 ms - Before Shared Hit Blocks: 8,535,272 - After Execution Time: ~130 ms - After Shared Hit Blocks: 67,204 ms Before Plan: https://explain.dalibo.com/plan/h4739gh3519eaa43 After Plan: https://explain.dalibo.com/plan/5ebc5g8deff272gc Forward-Port-Of: odoo/enterprise#125686 Forward-Port-Of: odoo/enterprise#125250
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#277088 Forward-Port-Of: odoo/odoo#251797
Original PR description
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#277088 Forward-Port-Of: odoo/odoo#251797
Resolved issues and error corrections
Sales order information in planning slot forms is now shown correctly for companies using a single-company setup. This prevents important sales links from being accidentally hidden and helps users manage planning work consistently across company configurations.
Original PR description
The `sale_line_id` field was previously injected after `company_id`. Because the first instance of `company_id` in the base view is wrapped inside a `<t groups="base.group_multi_company">` block, the inserted fields were inadvertently hidden in single-company databases. This commit changes the XPath target to `role_id` to ensure the sales order fields are always visible in the planning slot form view, regardless of multi-company settings. task: 6398673 Forward-Port-Of: odoo/enterprise#125712
Incoming Chilean electronic document emails that are missing a recipient tax ID no longer stop the mailbox processing job. The system now handles the missing value gracefully, so one malformed customer claim will not repeatedly block other incoming mail from being processed.
Original PR description
### Problem `Mail: Fetchmail Service` cron aborts with `AttributeError: 'NoneType' object has no attribute 'upper'` in `l10n_cl_edi/models/fetchmail_server.py::_process_incoming_customer_claim` when…
### Problem
`Mail: Fetchmail Service` cron aborts with `AttributeError: 'NoneType' object has no attribute 'upper'` in `l10n_cl_edi/models/fetchmail_server.py::_process_incoming_customer_claim` when an incoming customer claim DTE has no `<RUTRecep>`:
```python
dte.findtext('.//ns0:RUTRecep', ...).upper() or
dte.findtext('.//ns0:RutReceptor', ...).upper()
```
`findtext()` returns `None` when the tag is missing, so `.upper()` blows up before the `or` fallback can run. Once the cron hits such a message it re-crashes on every subsequent run and blocks the whole mailbox until the offending mail is deleted.
### Fix
Guard each `findtext(...)` with `or ''` so the `or` chain actually falls through. Empty `partner_vat` is already handled by the existing "Partner … has not been found" branch a few lines below.
### Traceback (Odoo 19)
```
File "/mnt/extra-addons/enterprise/l10n_cl_edi/models/fetchmail_server.py", line 285, in _process_incoming_customer_claim
dte.findtext('.//ns0:RUTRecep', namespaces=XML_NAMESPACES).upper() or
AttributeError: 'NoneType' object has no attribute 'upper'
```
### Ticket
No ticket open for this but opw-5257481 is related.
Forward-Port-Of: odoo/enterprise#123387Vietnam Sales Tax Report details now show VAT base amounts with the same positive sign as the report totals. This removes confusing negative values in unfolded invoice details and helps users review Vietnamese VAT reports more reliably.
Original PR description
## Current behavior: In Vietnam's Sales Tax Report, when unfolding until the minimum layer, the VAT now displays the value in negative ## Expected behavior: The VAT value in the minimum layer should…
## Current behavior: In Vietnam's Sales Tax Report, when unfolding until the minimum layer, the VAT now displays the value in negative ## Expected behavior: The VAT value in the minimum layer should be consistent with the upper layers and kept positive ## Steps to reproduce: - Install l10n_vn (l10n_vn_reports will install as well) - Switch to VN Company - With demo data, go to Invoicing / Reporting / Tax Report - Change Tax Report (VN) to Sales/Purchase Tax Report (VN) - Unfold the lines all the way to the minimum layer: VAT on sales of goods and services 10% -> INV/2026/00003 - Observe that the VAT 10% line is at negative, which is inconsistent with the Total line below ## Cause of the issue: - In Odoo, tax journal items store tax_base_amount with the accounting sign (negative for sales). This is expected behavior - However, Vietnamese VAT listing expects commercial amounts, which means sales bases need to be displayed as positive numbers ## Fix: Normalized tax_base_amount sign in _query_tax_lines function before building the SQL query opw-6344577 Forward-Port-Of: odoo/enterprise#125257 Forward-Port-Of: odoo/enterprise#122785
The Malaysia Statement of Account report now calculates total and overdue amounts using the selected statement date. This ensures the PDF totals match the balances shown in the report, improving accuracy for past-date customer account statements.
Original PR description
## Current behavior: In Malaysia's Statement of Account, the total and total overdue amounts dont consider the selected Statement Date, and will calculate all the balances up until today in the…
## Current behavior: In Malaysia's Statement of Account, the total and total overdue amounts dont consider the selected Statement Date, and will calculate all the balances up until today in the generated PDF report ## Expected behavior: The total and total overdue amounts should only sum the balances included in the report up until the selected Statement Date ## Steps to reproduce: 1. Install l10n_my_reports module, switch to Malaysian company 2. Go inside Invoicing > Report > Aged receivable 3. Select a specific date in the past 4. Observe that the total amounts dont match with the balance column, and wont change regardless of the date selected ## Cause of the issue: The template used o.total_overdue which ignores the report domain and statement date ## Fix: Accumulate overdue_total in the template loop with the same domain and date_to cutoff as the balance lines, so it always matches the displayed Balance lines for the selected Statement Date opw-6332970 Forward-Port-Of: odoo/enterprise#125410 Forward-Port-Of: odoo/enterprise#123694
Mexican payroll now calculates expected work hours even when a draft payslip has not yet been created. This helps off-cycle payroll runs start with consistent attendance and work-hour values, reducing manual corrections and payroll delays.
Original PR description
Previously, the `_preprocess_work_hours_data` method would abort early if no payslip was found for the given period. This prevented the correct generation or evaluation of expected Mexican work hours…
Previously, the `_preprocess_work_hours_data` method would abort early if no payslip was found for the given period. This prevented the correct generation or evaluation of expected Mexican work hours in contexts where a draft payslip does not yet exist (i.e., when an off-cycle payslip is initially generated for a given time period). To resolve this, the strict dependency on the payslip record has been removed. It now falls back to the contract version's base data when a payslip is absent: - The calendar defaults to the contract version's `resource_calendar_id`. - The duration is assumed to be standard (`is_wrong_duration = False`). - The Mexican schedule table (`l10n_mx_schedule_table`) is fetched globally from the environment (`hr.rule.parameter`) rather than relying on the payslip-specific helper method. This ensures expected work hours and attendance fields are calculated consistently across all payroll workflows, regardless of whether the payslip has been instantiated. opw-6351402 Forward-Port-Of: odoo/enterprise#125219
Accrual report totals now show the correct amounts when users group records by dates such as order date. This prevents misleading zero totals in purchase and sales accounting reports, helping teams review bills and revenue accruals accurately.
Original PR description
Currently, when users group accrual reports by dates, aggregated computed fields are not calculated correctly. As a result, values such as the total amount may be displayed as zero even when the…
Currently, when users group accrual reports by dates, aggregated computed fields are not calculated correctly. As a result, values such as the total amount may be displayed as zero even when the grouped records contain non-zero amounts.
## Steps to produce:
- Install Purchase and Accounting
- Create a product `Energy drink Sample` with cost 10$
- Create and Confirm a PO with the energy drink sample with vendor as `Administrator`
- Set `Received` as 1.
- Go to Accounting > Review > Bill to Receive
- Search Filter> Remove vendor grouping > Use Custom Group `Order Date`
- Expand the group
## Observed Behavior
Although the grouped lines contain an amount of 10 dollars, the aggregated total displayed in the sum remains zero.
The total should reflect the combined value of the grouped lines.
## Root Cause:
This issue occurs because opening the view triggers `_read_group_for_accrual`, which overrides the `_read_group` method on purchase order lines. The purpose of this override is to support grouping on computed fields that are not stored in the database such as `amount_to_invoice_at_date (Amount)`, `qty_received_at_date (Received)`.
When `_read_group_for_accrual` is executed, it delegates grouping for non-computed fields to the parent `_read_group` implementation, as shown at [1].
The parent method returns results in `res` similar to:
```
[(datetime.datetime(2026, 6, 1, 0, 0), 1.0, 10.0, 1)]
```
During iteration over res at [2], the code uses `group[0]` as the grouping key. In this example, group[0] is `datetime.datetime(2026, 6, 1, 0, 0)`, which represents the granularity date
(e.g., the first day of the month when grouping by month).
However, `records_by_group` is keyed by the actual purchase order line order dates rather than the granularity dates returned by `_read_group`. For example:
```
{datetime.datetime(2026, 6, 12, 0, 0): purchase.order.line(1,)}
```
As a result, the lookup performed using the granularity date (`datetime.datetime(2026, 6, 1, 0, 0))` does not find a matching entry in `records_by_group`. Consequently, records falls back to an empty `purchase.order.line()` recordset.
Later, at [3], the aggregation logic computes totals using this empty recordset. Since the required fields are evaluated on an empty set of records, the aggregated values are computed as zero.
This ultimately causes the method to return a total value of zero
[1]-
https://github.com/odoo/enterprise/blob/eeea1377a7d36d11e140c44dee30a45228e7b4a6/account_accountant/models/analytic_mixin.py#L9-L24
[2]-
https://github.com/odoo/enterprise/blob/eeea1377a7d36d11e140c44dee30a45228e7b4a6/account_accountant/models/analytic_mixin.py#L40-L42
[3]-
https://github.com/odoo/enterprise/blob/eeea1377a7d36d11e140c44dee30a45228e7b4a6/account_accountant/models/analytic_mixin.py#L44-L47
## Solution:
The issue can be resolved by grouping records using the same date granularity specified in the groupby, rather than using the field values.
By aligning the grouping logic with the granularity returned by `_read_group` (for example, grouping by the first day of the month when using monthly grouping ), the keys in `records_by_group` match the values returned in `res`. As a result, the corresponding records are correctly retrieved during aggregation.
This ensures that the aggregation is performed on the appropriate purchase order lines instead of an empty recordset, allowing the computed totals to be calculated correctly.
[opw-6261225](https://www.odoo.com/odoo/project/49/tasks/6261225)
Forward-Port-Of: odoo/enterprise#120369The Australian payroll scheduled update now refreshes payroll rule category data before updating salary rules. This prevents errors when previously deleted categories are needed again, helping payroll maintenance run reliably.
Original PR description
Currently, an error occurs when the "Payroll: Update Data" scheduled action is run. **Steps to Reproduce:** - Install `l10n_au_hr_payroll` with demo data. - Switch to `Australian Company`. - Go to…
Currently, an error occurs when the "Payroll: Update Data" scheduled action is run. **Steps to Reproduce:** - Install `l10n_au_hr_payroll` with demo data. - Switch to `Australian Company`. - Go to `Payroll` > `Configuration` > `Salary` > `Rule Categories`. - Delete all records related to the `Australian Company`. - Go to `Scheduled Actions` and run `Payroll: Update Data`. `ValueError: External ID not found in the system: l10n_au_hr_payroll.rule_category_ote` After [this commit], the category_id field becomes non-required, allowing users to delete a rule category record even if it is linked to a salary rule. When updating the data file [1], an error is raised due to the missing rule category. This commit ensures that when updating the salary rule data, it updates the category data beforehand as like [here] [this commit]: https://github.com/odoo/enterprise/commit/c663fd2a81b7f6b34f8199fdbdc4a75c4f21379e [1] https://github.com/odoo/enterprise/blob/b6612dd58276646b81a3a12cd48e8427ea5359c2/l10n_au_hr_payroll/models/hr_payslip.py#L76-L83 [here]: https://github.com/odoo/enterprise/blob/b6612dd58276646b81a3a12cd48e8427ea5359c2/l10n_ch_hr_payroll/models/hr_payslip.py#L1074-L1087 sentry-7349905716 Forward-Port-Of: odoo/enterprise#121167
Users without Planning access can now add products from the catalog on relevant sales quotations without hitting an access error. This prevents blocked sales workflows when quotations are linked to field service or planning records the user cannot directly access.
Original PR description
A user without Project rights cannot add a product from the catalog on a sale order Steps to reproduce: 1. Install industry_fsm_sale module 2. Go to Settings > Users & Companies > Users and open user Marc Demo 3. Set Field Service and Project rights to No 4. Log in as Marc Demo 5. Go to Sales and open any quotation 6. Click on Catalog in the order lines 7. An access error is raised Issue: industry_fsm_sale overrides `action_add_from_catalog` and tries to read sale.order.tasks_ids but users can't always access this field as it requires Project rights Solution: Check that the user has Project rights before trying to read tasks_ids opw-6315647 Forward-Port-Of: odoo/enterprise#125526 Forward-Port-Of: odoo/enterprise#123228
The timesheet timer now automatically returns focus to the description field after saving or resetting an entry. This removes an extra click for users entering multiple timesheets in a row and keeps time entry faster and smoother.
Original PR description
Steps to reproduce: - Install the timesheets application. - Open the timesheet timer menu from the systray. - Fill out the new timesheet entry. - Click the 'Save' or 'Reset' button (or use the keyboard hotkey). - Notice that the cursor focus is lost and the user must manually click back into the description field to start a new entry. Cause: - When a user clicks save or reset, the existing form is cleared via a DOM patch. Because the component is not remounted, the initial onMounted focus logic does not execute again. Fix: - Use onPatched to check if the save or discard button is the active element, and automatically re-focus the description input. task-6357438 Forward-Port-Of: odoo/enterprise#123697
Swiss payroll contract templates now show the same relevant wage fields as the employee contract view. When a template is loaded for an employee, wage type and wage-related values such as hourly wage are correctly carried over, reducing manual re-entry and payroll setup errors.
Original PR description
## Issue When creating a Contract Template for a Swiss company, the template does not match the version shown in the Employee's view. Also, some fields are not correctly applied when loading a…
## Issue
When creating a Contract Template for a Swiss company, the template does not match the version shown in the Employee's view. Also, some fields are not correctly applied when loading a contract template on an employee (e.g. `hourly_wage`, `wage`, ...).
## Steps to reproduce
1. Install *Switzerland - Swissdec Certified ELM 5.0 - Payroll* (`l10n_ch_hr_payroll`)
2. (Create and) Use a Swiss company
3. In Employees > Configuration > Contract Templates, create a Contract Template
- Wage Type: Hourly Wage
- Hourly Wage: Any value > 0
- **(Notice how the aforementionned fields are missing from the template)**
4. In Employees > Employees, create an Employee
5. On the new employee's view, on the Payroll tab, click "Load Template"
and load the template created in step 3
6. **The data from the template is not applied to the employee's contract**
## Cause
The fields loaded from a contract template are listed in the `whitelist` variable of the `hr.version.wizard`:
https://github.com/odoo/odoo/blob/5c3deb11627f4d6762c4994207bd582afb96f064/addons/hr/wizard/hr_contract_template_wizard.py#L15-L30
Multiple fields were missing from the whitelist (e.g. `hourly_wage`, `l10n_ch_has_{hourly|monthly|lesson}`, ...). These fields would not be loaded from the template when applying a template on an employee.
**This commit replicates the employee's version view on the contract template and adds the related fields to the whitelist for them to be correctly applied when loading a contract template.**
opw-5966664
opw-6128467
Forward-Port-Of: odoo/enterprise#125435
Forward-Port-Of: odoo/enterprise#110683Credit notes for Guatemalan invoices now reference the original invoice's actual issue date instead of a technical certification timestamp. This helps prevent rejected electronic documents by matching SAT validation requirements.
Original PR description
### Issue before this commit: When reverting a Guatemalan invoice (creating a credit note), the XML node FechaEmisionDocumentoOrigen incorrectly reports the EDI document's technical certification…
### Issue before this commit: When reverting a Guatemalan invoice (creating a credit note), the XML node FechaEmisionDocumentoOrigen incorrectly reports the EDI document's technical certification date instead of the original invoice's emission date. This causes the SAT to reject the document. ### Steps to reproduce the issue: 1. Download Accounting and l10n_gt 2. Revert an invoice (credit note) inserting a different date than the one of the invoice 3. See that FechaEmisionDocumentoOrigen report the date of the credit note instead of the one of the invoice ### Cause of the issue: The _l10n_gt_edi_add_reference_values method extracted the date from original_document.datetime (the technical timestamp of when the XML was generated) rather than using the actual accounting date of the original invoice. ### Reason to introduce the fix: SAT validation rules strictly require the reference date to match the exact commercial emission date of the original invoice. Fetching invoice_date directly ensures compliance, avoids timezone conversion errors, and prevents the XML from being rejected. Source: https://www.lawinsider.com/es/contracts/dJXl4Vo79L2 <img width="730" height="205" alt="2026-07-17_10-19" src="https://github.com/user-attachments/assets/802e7bb3-fcf9-48db-b86f-227b494001b6" /> opw-6394409 Forward-Port-Of: odoo/enterprise#125788 Forward-Port-Of: odoo/enterprise#124794
Fixes an issue where the Planning Gantt view could crash when users grouped shifts by role if a role had no assigned resource or no working schedule. This keeps planning views usable in edge cases and prevents disruption when reviewing schedules by role.
Original PR description
Currently, an error occurs when grouping planning slots by role. **Steps to Reproduce:** - Install the `Planning` module. - Go to `Planning` > `Configuration` > `Roles`. - Create a `role` without…
Currently, an error occurs when grouping planning slots by role.
**Steps to Reproduce:**
- Install the `Planning` module.
- Go to `Planning` > `Configuration` > `Roles`.
- Create a `role` without assigning any resource to it (or assign a resource without a working time).
- Go to `Planning`, create a new `planning slot`, assign the `role` created above, set the start date to `20/07/2026 12:00 PM` and the end date to `21/07/2026 2:00 AM`.
- Switch to the `Gantt view` of the `planning slots`.
- Group by `Role` and set the custom date range to `07/19/2026 -> 07/20/2026`, then click `Apply`.
`KeyError: 1`
The error occurs when the user groups the planning slots by role in the Gantt view. During
the computation of the Gantt progress bar, if the existing slot's role has no resource, or has
a flexible resource without a calendar, the regular resources become empty [1]. Then it
attempts to compute the valid work intervals for these empty resources [2], resulting in an
empty calendar work interval dictionary ({}) [3]. Later, when computing the duration over the
period with the valid range slots, it tries to access the resource directly from the empty
calendar_intervals dictionary [4], which raises the error.
This commit ensures that, when no work intervals are available for a resource, an empty
work interval is used instead.
[1]- https://github.com/odoo/enterprise/blob/3871b75fb74a7b35bfea9610ca08b853eb723320/planning/models/planning_slot.py#L307
[2]: https://github.com/odoo/odoo/blob/5663509fe5caa1191fafcea3b3879dcef9ceca8f/addons/resource/models/resource_resource.py#L220
[3]: https://github.com/odoo/enterprise/blob/3871b75fb74a7b35bfea9610ca08b853eb723320/planning/models/planning_slot.py#L3415-L3417
[4]- https://github.com/odoo/enterprise/blob/3871b75fb74a7b35bfea9610ca08b853eb723320/planning/models/planning_slot.py#L3300
sentry-7620222164
Forward-Port-Of: odoo/enterprise#124991Draft invoices no longer show the vehicle field on invoice lines as a clickable link. This prevents users from opening or acting on related vehicle records before the invoice is officially posted, keeping behavior consistent with product links.
Original PR description
The vehicle under account on the invoice lines should not be clickable when the invoice is in draft. Only when it is posted, like the product. task-6385436 Forward-Port-Of: odoo/enterprise#125688 Forward-Port-Of: odoo/enterprise#124514
Installing POS no longer resets customized payroll account settings on Swiss salary rules. This protects customer configuration from being overwritten when related accounting features are installed.
Original PR description
### Steps to reproduce: - In a database with Switzerland localization, install 'Accounting' and 'Payroll' - Manually change the debit and credit accounts on the Swiss ELM salary rules (rule code…
### Steps to reproduce: - In a database with Switzerland localization, install 'Accounting' and 'Payroll' - Manually change the debit and credit accounts on the Swiss ELM salary rules (rule code 1000) - Install 'POS' - Check the accounts you configured on the salary rules >The accounts are reset to their default values ### Cause of Issue: POS depends on the module `stock_account`. When `stock_account` is installed, the `_configure_journals` method in its `__init__.py` creates a new `account.journal` for inventory valuation. To apply default values to this journal, the method retrieves data from the chart template. https://github.com/odoo/odoo/blob/7d16ef88784e18e885b97dbded3231775f1349d2/addons/stock_account/__init__.py#L41 The `hr_payroll_account` module overrides `_post_load_data` and unconditionally calls `_load_payroll_accounts(template_code, company)`. https://github.com/odoo/enterprise/blob/fcf06997a0eb999af86e4b6917311a86f04a390e/hr_payroll_account/models/account_chart_template.py#L16-L18 This triggers the reinstallation of the default payroll accounts, which overwrites and discards any manual configuration changes the user has made to their salary rules. ### Fix: Ensure that default payroll accounts are only reset during a genuine chart of accounts loading process, and not during localized post-load operations triggered by other modules. opw-6251112 Forward-Port-Of: odoo/enterprise#121687
Chilean invoice PDFs now show the legally required CEDIBLE disclaimer in Spanish regardless of the customer's language settings. This prevents incorrect English text from appearing on official Chilean tax documents and supports compliance with local requirements.
Original PR description
Steps to reproduce: - Set the database language to Spanish (Latin America). - Create a customer invoice, confirm it and send it to the SII. - Print it using Print > Invoice PDF copy (Chile). - Scroll…
Steps to reproduce:
- Set the database language to Spanish (Latin America).
- Create a customer invoice, confirm it and send it to the SII.
- Print it using Print > Invoice PDF copy (Chile).
- Scroll to the CEDIBLE section at the bottom of the PDF.
Cause of the issue:
The CEDIBLE footer is merged into l10n_cl.report_invoice_document, which account.report_invoice (odoo/addons/l10n_cl/views/report_invoice.xml) renders with t-lang set to the invoice partner's lang, not the database/user language. The disclaimer text was hardcoded in English and relied on the regular translation to be shown in Spanish, so as
soon as the partner's lang field isn't Spanish, the translation lookup falls back to the untranslated English source, regardless of the database language.
Solution:
This disclaimer is boilerplate mandated by Chilean law: it must always be printed in Spanish, independently of the invoice partner's or current user's language. The same template already follows that rule a few lines above for the SII stamp block ("Timbre Electrónico SII..."), which is hardcoded in Spanish instead of relying on translation.
opw-6390207
Forward-Port-Of: odoo/enterprise#124916Opening the Scrap option from a new manufacturing operation in the Barcode app no longer triggers an error when no location record is available. This prevents interruptions for warehouse and manufacturing users, including a related consignment scanning scenario.
Original PR description
Versions -------- - 18.0+ Steps ----- 1. Open Barcode app; 2. click Operations; 3. click MANUFACTURING 4. click New; 5. click cogwheel on top right; 6. click Scrap. Issue ----- Traceback: > Error: Record stock.location with id=undefined doesn't exist in the cache Cause ----- When setting up the default context for the scrap menu, it it assumes `this.record` is not empty. Solution -------- Make `cache.getRecord` not raise an error when a location isn't found. Use optional chaining for other parts of the context that rely on a `record` being present. Also fixes a related issue introduced by 4b457fe, where the same traceback would be thrown on opening a new MO and scanning a product whilst consignment is enabled. opw-6397774 Forward-Port-Of: odoo/enterprise#125571 Forward-Port-Of: odoo/enterprise#124818
The manufacturing planning screen now correctly uses a product's Bill of Materials batch size even when the BOM was not manually selected while adding the product. This prevents under-planning production quantities and helps ensure forecasts generate the right replenishment amounts.
Original PR description
In MPS, if a product’s Bill of Materials (BOM) is not specified at the time of addition, the system will not correctly account for batch size. Steps to reproduce: ------------------- * Create a…
In MPS, if a product’s Bill of Materials (BOM) is not specified at the time of addition, the system will not correctly account for batch size. Steps to reproduce: ------------------- * Create a product with a bom that has a batch size of 2 * Open MPS * Add the product - without specifying the bom - Route Manufacture * Add 1 in the Forcast Demand -> the batch size from the bom it's not taken into account. Observation: ------------- When updating mps, it will call get_production_schedule_view_state: https://github.com/odoo/enterprise/blob/7092dd2cc3578c3f22d1fbc82d958d57e2f93553/mrp_mps/models/mrp_mps.py#L424 this function when calculating the quantity to resplenish will call _get_resplenish_qty: https://github.com/odoo/enterprise/blob/7092dd2cc3578c3f22d1fbc82d958d57e2f93553/mrp_mps/models/mrp_mps.py#L534 to know the quantity to resplenish it will need the batch size, in mps they will only consider the batch size from the bom registered: https://github.com/odoo/enterprise/blob/7092dd2cc3578c3f22d1fbc82d958d57e2f93553/mrp_mps/models/mrp_mps.py#L863-L865 Since there is no default value for bom_id, If there is no bom selected, there is no batch size. opw-6259956 Forward-Port-Of: odoo/enterprise#124922 Forward-Port-Of: odoo/enterprise#119560
This fix improves how Belgian payroll notification files are analyzed, helping prevent errors during payroll declaration handling. It supports more reliable processing for Belgian HR payroll workflows without changing the user-facing process.
Original PR description
Forward-Port-Of: odoo/enterprise#125500
The Belgian Partner VAT Listing now always covers the official calendar year, from January 1 to December 31. This prevents incorrect reporting periods for companies whose fiscal year does not match the calendar year, improving compliance accuracy.
Original PR description
The Belgian Partner VAT Listing must always report on the civil calendar year (01/01/N to 12/31/N). Previously, the report was relying on the company's fiscal year configuration, which caused incorrect reporting periods for companies with non-calendar fiscal years. This commit overrides `_custom_options_initializer` to strictly enforce a civil year date range based on the selected year, entirely ignoring custom fiscal year boundaries. Task-6086513 Forward-Port-Of: odoo/enterprise#125752 Forward-Port-Of: odoo/enterprise#114337
Fixes an issue where changing a work order's planned end time in the Gantt view could crash when dependent work orders were present. The scheduling flow now keeps the information it needs to update related work orders reliably, helping manufacturing planners adjust timelines without interruption.
Original PR description
Resizing a work order from the right edge in the Gantt view updated only the stop date (`date_finished`). Since the work order Gantt view defines dependencies, this triggered dependency propagation…
Resizing a work order from the right edge in the Gantt view updated only the stop date (`date_finished`). Since the work order Gantt view defines dependencies, this triggered dependency propagation through `web_gantt_reschedule`. During that flow, `_web_gantt_move_candidates` needed both the old start and stop dates to update dependent work orders, but the old-value snapshot only contained fields present in `vals`. As a result, `date_start` was missing and the resize crashed with a `KeyError: 'date_start'`. Handle the missing old start date in the generic Gantt dependency propagation flow. When only the stop date is changed, the old start date is added to the old-value snapshot without adding it to the actual write values. This keeps the right-edge resize payload unchanged while giving dependency propagation the values it needs. Steps to reproduce: 1. Create a manufacturing order with work orders. 2. Plan the manufacturing order. 3. Open the Work Order Gantt view. 4. Resize a work order from the right edge to change its planned end date. Before this commit: Right-edge resizing a work order with dependencies crashed during dependency propagation with `KeyError: 'date_start'`. After this commit: Right-edge resizing keeps the changed `date_finished`, dependency propagation has access to the old `date_start`, and dependent work orders are rescheduled without crashing. task-6345347
Sales commission achievement records with very large identifiers can now be opened correctly. This prevents users from seeing an incorrect “record does not exist” message when accessing affected achievement reports.
Original PR description
Steps to reproduce: - Open an achievement with id > JS limit Issues: - We get a pop-up saying the record does not exists The reason we get this error is because since we are browsing a record with an id greater than JS limit the browser truncate it. In order to solve this issue the following PR was made #108751. A field `id_str` was added but it still wasn't working as we weren't retrieving the `id_str`. We now do this by passing `id_str` in the context and retrieving it on the `web_read`. Forward-Port-Of: odoo/enterprise#123757 Forward-Port-Of: odoo/enterprise#113701
This fix ensures the US payroll localization is installed through the standard automatic installation process instead of a later setup hook. This makes payroll module detection more reliable during database creation and upgrades, reducing the risk of modules appearing temporarily uninstalled.
Original PR description
In a [previous PR], a test was introduced to reject any `l10n_xx_hr_*` module that has a `countries` key in its manifest and depends on a module that also has that country key. This test was too…
In a [previous PR], a test was introduced to reject any `l10n_xx_hr_*` module that has a `countries` key in its manifest and depends on a module that also has that country key. This test was too broad and rejected some valid cases: 1. A non-auto-install module can have a country key defined to add flags in the apps kanban view. ([src]) 2. A module that has a country-specific regular dependency, but not as an auto-install condition. The second case is illustrated by [l10n_us_hr_payroll], which should auto-install when `hr_payroll` is installed and a US company exists. With the old test, achieving this required adding `l10n_us` to its auto-install dependencies. But, since `l10n_us` is not auto-installable, `l10n_us_hr_payroll` would not be installed if you create a DB with a US company and only install `hr_payroll`. In practice, that module was still being installed via a [post-init hook] in `hr_payroll`. This hook was installing all `l10n_XX_hr_payroll` modules for each country where a company is located, which is the behavior of the `countries` parameter in the manifest. This caused issues during upgrades as this runs late in the process: after the auto-discovery phase. Modules installed by this hook would be considered as `uninstalled` until `hr_payroll` is loaded. This commit narrows the check to only fail if: - A module has a `country` key in its manifest, and - It has a country-specific module in its **auto-install** dependencies. Moreover, it modifies `l10n_us_hr_payroll` to correctly rely on the auto_install mechanism instead of the post-init hook. [previous PR]: https://github.com/odoo/enterprise/pull/101843 [src]: https://github.com/odoo/odoo/blob/6df9f92a537aa4bb4ee5dc946fe31c4e56e6dfea/odoo/addons/base/models/ir_module.py#L271-L273 [l10n_us_hr_payroll]: https://github.com/odoo/enterprise/blob/24a33ffb769557be498d61328522bb77f68d3a5a/l10n_us_hr_payroll/__manifest__.py [post-init hook]: https://github.com/odoo/enterprise/blob/85185595cfd1ee5310ceb9dc80c0589accad2f19/hr_payroll/__init__.py#L21 Forward-Port-Of: odoo/enterprise#125853 Forward-Port-Of: odoo/enterprise#120279
This fix prevents Swiss payroll pension fund numbers from being included where they are not expected during BVG-LPP status checks. It helps avoid incorrect declaration handling and supports smoother Swiss payroll compliance processing.
Original PR description
Forward-Port-Of: odoo/enterprise#126040
Swiss employee payslips now show the actual contract withdrawal date instead of a related version end date. This avoids incorrect termination dates appearing on payroll reports when those dates differ.
Original PR description
The Withdrawal Date in the payslip of CH employees was printing the date_end relative to the version related to the payslip. Instead, it should print the end of the contract of that version, since they can be different. The end date of the contract is in l10n_ch_withdrawal. Task: 6398291 Forward-Port-Of: odoo/enterprise#124848
The barcode app now correctly blocks scanning products that were not reserved when extra products are not allowed, even after users leave and reopen a transfer. It also restores the ability to add products on immediate delivery transfers where that action is expected, reducing confusion and preventing incorrect stock entries.
Original PR description
This [PR] made sure it was not possible to scan unreserved products when `allow_extra_product` was disabled, even when exiting and re-entering a transfer. It worked under the assumption that an immediate transfer always stays in draft, which is wrong for deliveries. The 2nd commit of this PR partly address this issue by allowing the user to add multiple products with the "Add Product" button when the transfer is immediate. While working on this issue, we encountered a bug in the scanning prevention that should have been caught by a tour but was not. This is fixed in the 1st commit. More details in the commit messages. [PR]: https://github.com/odoo/enterprise/pull/123793 Forward-Port-Of: odoo/enterprise#125313
This fix prevents Mexican electronic document status checks from repeatedly triggering on the same records. It prioritizes older customer invoices and limits vendor bill checks, reducing unnecessary background processing and helping scheduled checks run more reliably.
Original PR description
While trying to fix the SAT cron, we did not think it through it could cause infinite cron triggering. Indeed, the write_date is updated on every record that is handled. Fix is manyfold: - Avoid…
While trying to fix the SAT cron, we did not think it through it could cause infinite cron triggering. Indeed, the write_date is updated on every record that is handled. Fix is manyfold: - Avoid re-processing what we already check within the last 4 hours/12 hours depending on the type of the document. - The domain takes the *static* create date instead of the write_date to make sure we don't endless re-process the same record and that the window of 7/60 days applies. - Limit the Vendor Bill to be checked only during 7 days after their creation. - Use the create_date in the order of the search to ensure we process older records first, before their time-window closes. - Process the Vendor Bills last, this ensure Customer Invoices will be processed in priority in case we are not able to process everything within the last 4/12 hours. This is still imperfect and a little fragile, we will find a better solution in master, most likely by adding a dedicated field to keep track of the last SAT check. See https://github.com/odoo/enterprise/pull/123213 See https://github.com/odoo/enterprise/pull/103272 task-none Forward-Port-Of: odoo/enterprise#125598 Forward-Port-Of: odoo/enterprise#125317
### Issue before the commit: During the import of Italian e-invoices, Pension Fund taxes (Cassa Previdenziale) linked to a 0% VAT rate with a specific exemption reason (Natura, e.g., N2.2) are ignored and not applied to the invoice lines. ### Steps to reproduce the issue: 1. Download Accounting and l10n_it 2. Go to vendor -> bills and import the bill in the ticket 3. Check that taxes are not imported as expected ### Cause of the issue: The system incorrectly used the Natura to search
Original PR description
### Issue before the commit: During the import of Italian e-invoices, Pension Fund taxes (Cassa Previdenziale) linked to a 0% VAT rate with a specific exemption reason (Natura, e.g., N2.2) are ignored and not applied to the invoice lines. ### Steps to reproduce the issue: 1. Download Accounting and l10n_it 2. Go to vendor -> bills and import the bill in the ticket 3. Check that taxes are not imported as expected ### Cause of the issue: The system incorrectly used the Natura to search for the Pension Fund tax itself. Fiscally, the Natura belongs to the related VAT, not the Pension Fund. This incorrect domain caused the tax search to fail. The Pension Fund tax should not have a Natura setted. ### Reason to introduce the fix: To correctly apply Pension Fund taxes to exempt invoice lines. Ticket [link](https://www.odoo.com/odoo/project.task/6357133) opw-6357133 Forward-Port-Of: odoo/odoo#278704 Forward-Port-Of: odoo/odoo#275317
[1] added a bus channel for discuss categories. This can be done with or without any token. When the token is not passed, `verify_limited_field_access_token` is still called and crashes. When no token is provided, we should use category access rights instead and avoir verifying the token (which is `None`). [1]: https://github.com/odoo/odoo/pull/243131 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: ---
Original PR description
[1] added a bus channel for discuss categories. This can be done with or without any token. When the token is not passed, `verify_limited_field_access_token` is still called and crashes. When no token is provided, we should use category access rights instead and avoir verifying the token (which is `None`). [1]: https://github.com/odoo/odoo/pull/243131 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#277085 Forward-Port-Of: odoo/odoo#276931
Steps to Reproduce: - Go to Settings > Translations > Languages and select your active language. - Change the Time Format to a 13:00:00 (24-hour) - Click on the Attendance systray icon in the top navbar and Check In. - Notice that the recorded time still displays in a 12-hour format. Cause: - The attendance popup doesn't enforce a strict 12-hour or 24-hour rule. Because of this missing rule, your web browser just uses your computer's default time settings. As a result, Odoo's actual
Original PR description
Steps to Reproduce: - Go to Settings > Translations > Languages and select your active language. - Change the Time Format to a 13:00:00 (24-hour) - Click on the Attendance systray icon in the top…
Steps to Reproduce:
- Go to Settings > Translations > Languages and select your active language.
- Change the Time Format to a 13:00:00 (24-hour)
- Click on the Attendance systray icon in the top navbar and Check In.
- Notice that the recorded time still displays in a 12-hour format.
Cause:
- The attendance popup doesn't enforce a strict 12-hour or 24-hour rule. Because of this missing rule, your web browser just uses your computer's default time settings. As a result, Odoo's actual language and time settings are completely ignored.
Fix:
- Replaced the browser-based formatting with the existing formatting utilities.
- Used is24HourFormat() to determine whether the active time format uses a 12-hour or 24-hour clock.
Solution:
- Dynamically selected the display format using:
- HH:mm for 24-hour format
- hh:mm a for 12-hour format
- Applied the selected format consistently for both check-in and check-out times so the systray now correctly follows the configured language time format.
task-6120540
Forward-Port-Of: odoo/odoo#259918Steps to reproduce: - Open tasks from any project or open a task form, go to the Blocked By tab, and click Add a line to open the task selection view. - Apply the `Templates` filter - Observe that non-template tasks and tasks from template projects are also shown. Cause: - The domain condition checks for `default_project_id` and falls back to `Domain.TRUE`, allowing non-template tasks and tasks from template projects to bypass template-specific filtering. Fix: - Remove the `default_p
Original PR description
Steps to reproduce: - Open tasks from any project or open a task form, go to the Blocked By tab, and click Add a line to open the task selection view. - Apply the `Templates` filter - Observe that non-template tasks and tasks from template projects are also shown. Cause: - The domain condition checks for `default_project_id` and falls back to `Domain.TRUE`, allowing non-template tasks and tasks from template projects to bypass template-specific filtering. Fix: - Remove the `default_project_id` condition and enforce only `has_template_ancestor = True` in the domain, ensuring that only actual template tasks are shown. task-5966601 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277379 Forward-Port-Of: odoo/odoo#260624
When a product uses automated inventory valuation, scrapping it from an already validated (done) picking generated no inventory valuation journal entry, even though the stock move value and the on-hand quantity were correctly updated. The same scrap done from the Scrap menu, or from a picking that is not done yet, worked as expected. A stock move whose picking is already done is created directly in the 'done' state (stock.move.create). Such a move is filtered out of the recordset returned by
Original PR description
When a product uses automated inventory valuation, scrapping it from an already validated (done) picking generated no inventory valuation journal entry, even though the stock move value and the on-hand quantity were correctly updated. The same scrap done from the Scrap menu, or from a picking that is not done yet, worked as expected. A stock move whose picking is already done is created directly in the 'done' state (stock.move.create). Such a move is filtered out of the recordset returned by _action_done(), on which _create_account_move() is called, so the scrap move never received its journal entry. Steps to reproduce: - Use a storable product with automated inventory valuation - Create and validate a receipt for it - Open the completed picking, click Scrap, set a quantity and validate it - The stock is reduced but no journal entry is created. opw-6368258 Forward-Port-Of: odoo/odoo#278210 Forward-Port-Of: odoo/odoo#275847
Steps to reproduce: - Install the `l10n_br` module. - Go to Portal > Addresses > Add Address > select Brazil as the country. (Do not change the company's country to Brazil) Issue: - The address layout is broken: the Street input has no label and `Steet and Number` field is missing. Cause: - The `o_extended_address` elements are not rendered when the company country is not Brazil. When the user selects Brazil, `_setVisibility` looks for `o_extended_address` elements but finds none, s
Original PR description
Steps to reproduce: - Install the `l10n_br` module. - Go to Portal > Addresses > Add Address > select Brazil as the country. (Do not change the company's country to Brazil) Issue: - The address layout is broken: the Street input has no label and `Steet and Number` field is missing. Cause: - The `o_extended_address` elements are not rendered when the company country is not Brazil. When the user selects Brazil, `_setVisibility` looks for `o_extended_address` elements but finds none, so it fails to make the standard address fields visible. Fix: - Restore the company country condition in JS so `o_standard_address` is not hidden when no `o_extended_address` elements are rendered. Forward-Port-Of: odoo/odoo#278008
The mock server was computing the reaction sequence with `Math.min(reactionGroup.map(...))` instead of `Math.min(...reactionGroup.map(...))`. This returned `NaN`, making the sort order non-deterministic and causing the 'Reactions are ordered by id' test to be flaky. Fixes runbot error https://runbot.odoo.com/odoo/error/944188 Forward-Port-Of: odoo/odoo#278895
Original PR description
The mock server was computing the reaction sequence with `Math.min(reactionGroup.map(...))` instead of `Math.min(...reactionGroup.map(...))`. This returned `NaN`, making the sort order non-deterministic and causing the 'Reactions are ordered by id' test to be flaky. Fixes runbot error https://runbot.odoo.com/odoo/error/944188 Forward-Port-Of: odoo/odoo#278895
## Current behavior: Clicking on the Late activity for Lot/Serial in the notification systray doesn't apply the Late activities filter. ## Expected behavior: Clicking on the Late activity for Lot/Serial in the notification systray should apply the Late activities filter. ## Steps to reproduce: 1. Install Inventory (stock) module, make sure to enable Lots & Serial Numbers in Inventory > Traceability 2. Create some new Lots / Serial numbers 3. Add some Activities with Due Date before to
Original PR description
## Current behavior: Clicking on the Late activity for Lot/Serial in the notification systray doesn't apply the Late activities filter. ## Expected behavior: Clicking on the Late activity for…
## Current behavior: Clicking on the Late activity for Lot/Serial in the notification systray doesn't apply the Late activities filter. ## Expected behavior: Clicking on the Late activity for Lot/Serial in the notification systray should apply the Late activities filter. ## Steps to reproduce: 1. Install Inventory (stock) module, make sure to enable Lots & Serial Numbers in Inventory > Traceability 2. Create some new Lots / Serial numbers 3. Add some Activities with Due Date before today 4. Observe that the Clock icon on systray will count up, showing the number of activities that are late, for today and for future 5. Click on the Late activities (e.g. 2 Late) should only show the 2 late activities. Instead, no filter is applied, thus showing all the Lots / Serial numbers in the inventory ## Cause of the issue: Missing filters for Late, Today and Future activities in the stock_lot_views.xml ## Fix: Added 3 filters for Late, Today and Future activities opw-6332560 Forward-Port-Of: odoo/odoo#278165 Forward-Port-Of: odoo/odoo#272735
Steps to reproduce the bug: - Install l10n_ke_edi_oscu_stock - Run Test_sale_mrp_kit_bom_cogs Problem: ```self.assertAlmostEqual(stock_out_aml.credit, 1.53, msg="Should not include the value of consumable component") AssertionError: 3.07 != 1.53 within 7 places (1.5399999999999998 difference) : Should not include the value of consumable component ``` The test delivered 1 whole unit of every component of Kit A regardless of the fractional quantity actually needed to produce a single kit
Original PR description
Steps to reproduce the bug: - Install l10n_ke_edi_oscu_stock - Run Test_sale_mrp_kit_bom_cogs Problem: ```self.assertAlmostEqual(stock_out_aml.credit, 1.53, msg="Should not include the value of…
Steps to reproduce the bug: - Install l10n_ke_edi_oscu_stock - Run Test_sale_mrp_kit_bom_cogs Problem: ```self.assertAlmostEqual(stock_out_aml.credit, 1.53, msg="Should not include the value of consumable component") AssertionError: 3.07 != 1.53 within 7 places (1.5399999999999998 difference) : Should not include the value of consumable component ``` The test delivered 1 whole unit of every component of Kit A regardless of the fractional quantity actually needed to produce a single kit (0.34/0.14/0.2 units for Component A/B/BB respectively). This went unnoticed under the default invoice_policy 'order', since qty_delivered never drives the invoiced quantity in that case. l10n_ke_edi_oscu_stock forces invoice_policy to 'delivery' for storable products that have no explicit company_id, which is the case for the products created in this test. With invoice_policy 'delivery', _compute_kit_quantities() correctly reads the over-delivered components as enough stock to form 2 complete kits (min ratio 2.94, floored to 2) instead of 1, doubling the invoiced quantity and the resulting COGS (3.07 instead of 1.53). runbot-243633 Forward-Port-Of: odoo/odoo#277519
Steps to reproduce the bug: - Run the product module test suite on a loaded/slow CI runner - Observe test_get_first_possible_combination occasionally failing Problem: test_get_first_possible_combination asserts that _get_first_possible_combination() completes in under 0.5 seconds on a template with 10 attributes x 50 values and exclusion rules. On a busy runner (Testing country uk build) it took 0.587s and the test failed with `0.5868358612060547 not less than 0.5`, even though the return
Original PR description
Steps to reproduce the bug: - Run the product module test suite on a loaded/slow CI runner - Observe test_get_first_possible_combination occasionally failing Problem:…
Steps to reproduce the bug: - Run the product module test suite on a loaded/slow CI runner - Observe test_get_first_possible_combination occasionally failing Problem: test_get_first_possible_combination asserts that _get_first_possible_combination() completes in under 0.5 seconds on a template with 10 attributes x 50 values and exclusion rules. On a busy runner (Testing country uk build) it took 0.587s and the test failed with `0.5868358612060547 not less than 0.5`, even though the returned combination was correct. The 0.5s threshold is an arbitrary sanity check meant to catch a gross algorithmic regression (e.g. loss of early pruning of invalid combinations), not a strict performance SLA, so it is too tight to survive normal CI load variance. Solution: Raise the threshold to 2 seconds, keeping enough margin to absorb CI load variance while still catching a real performance regression, which would take far longer than the current computation. runbot-243606 Forward-Port-Of: odoo/odoo#277986
Steps to reproduce the bug: - Create a purchase requisition: - add any storable product and vendor - From it, create a purchase order and confirm it -> a picking is created - Cancel the "purchase.requisition" Problem: The confirmed purchase order was cancelled even though it had already been validated (state = 'purchase') and had active receipts or vendor bills attached to it. In `action_cancel`, `requisition.purchase_ids.button_cancel()` is called unconditionally on all linked POs,
Original PR description
Steps to reproduce the bug:
- Create a purchase requisition:
- add any storable product and vendor
- From it, create a purchase order and confirm it -> a picking is created
- Cancel the "purchase.requisition"
Problem:
The confirmed purchase order was cancelled even though it had already been validated (state = 'purchase') and had active receipts or vendor bills attached to it.
In `action_cancel`, `requisition.purchase_ids.button_cancel()` is called unconditionally on all linked POs, with no check on their current state or on whether picking or invoices existed.
Solution:
Only cancel linked purchase orders that are still in 'draft' state. Once
outside of that state, the purchase process might be too far engaged to
simply cancel the purchase order without notice.
opw-6329742
Forward-Port-Of: odoo/odoo#272190**Steps to reproduce:** - Go to Discuss app - Start a new meeting - Enable Push-To-Talk in voice settings - A banner for the discuss extension recommendation is added the first time you enable the setting - Banner makes the call window move down - Call actions are pushed to the bottom of the screen and not easily accessible **Issue:** An ad banner for the Push-To-Talk extension was added by [1], but it was inserted above the call window rather than on top of it, causing the content bel
Original PR description
**Steps to reproduce:** - Go to Discuss app - Start a new meeting - Enable Push-To-Talk in voice settings - A banner for the discuss extension recommendation is added the first time you enable the setting - Banner makes the call window move down - Call actions are pushed to the bottom of the screen and not easily accessible **Issue:** An ad banner for the Push-To-Talk extension was added by [1], but it was inserted above the call window rather than on top of it, causing the content below to be pushed down. **Fix:** Moved the banner inside the call window, but only when it is not compact (not in smaller chat window or mobile). Could fix with css but we probably don't want to show the banner in these cases anyway (as it would take too much space in the chat window, or not be relevant to mobile users). [1] https://github.com/odoo/odoo/commit/d45c92eb07cd16cc45b0e9c8bf9422fe974f0c62 opw-6250056 Forward-Port-Of: odoo/odoo#278962 Forward-Port-Of: odoo/odoo#277982
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: . Add safe execution to the element before use focus() task-6409715 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278083
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: . Add safe execution to the element before use focus() task-6409715 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278083
**Issue:** When an employee uses a fully fixed duration based working schedule and each half-day attendance has a decimal duration such as 3.36h, a multi-day half-day time off request can compute a decimal duration such as 5.01 days. This is inconsistent with half-day time off types, which should consume time in half-day increments. **Steps to reproduce:** - Create an employee with a fully fixed duration-based schedule - Set morning and afternoon attendances to 3.36 hours for each weekday
Original PR description
**Issue:** When an employee uses a fully fixed duration based working schedule and each half-day attendance has a decimal duration such as 3.36h, a multi-day half-day time off request can compute a…
**Issue:** When an employee uses a fully fixed duration based working schedule and each half-day attendance has a decimal duration such as 3.36h, a multi-day half-day time off request can compute a decimal duration such as 5.01 days. This is inconsistent with half-day time off types, which should consume time in half-day increments. **Steps to reproduce:** - Create an employee with a fully fixed duration-based schedule - Set morning and afternoon attendances to 3.36 hours for each weekday - Create a time off type with duration type set to half-day - Create a time off request for the employee (e.g. Monday to Friday) - The computed duration is 5.01 days instead of 5 days **Cause:** For half-day time off types, `number_of_days` was taken from generic calendar interval computation. https://github.com/odoo/odoo/blob/19c0e59cc37c7671f13cbda1b2d4850a7731eade/addons/hr_holidays/models/hr_leave.py#L585-L593 On duration-based schedules, this computation returns day values rounded at 0.001 precision, so decimal drift (e.g. 5.01) can appear, Since no final rounding to half-day steps was applied, half-day requests could end with non-half-day values. **Solution:** Round computed durations for half-day time off types to the nearest half-day increment. opw-6215768 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278656 Forward-Port-Of: odoo/odoo#267726
When running test_catalog_price test, it checks for productUomFactor. However, when running single app tests, sometimes UOM is disabled, and productUomFactor won't be returned in that case. runbot-242515 Forward-Port-Of: odoo/odoo#276910
Original PR description
When running test_catalog_price test, it checks for productUomFactor. However, when running single app tests, sometimes UOM is disabled, and productUomFactor won't be returned in that case. runbot-242515 Forward-Port-Of: odoo/odoo#276910
[FIX] html_builder: fix job location editing on website Scenario: 1) Head over to website 2) Go to any specific job page 3) Open editor 4) Try changing job location 5) Hit save Result: The changes aren't saved or propagated into the backend. Expectation: Users should be able to edit job locations via the editor on website and have those changes reflect on the site and job record. Cause: How elements were marked as savable was changed [in this IMP][1] to rely on `o_savable` rat
Original PR description
[FIX] html_builder: fix job location editing on website Scenario: 1) Head over to website 2) Go to any specific job page 3) Open editor 4) Try changing job location 5) Hit save Result: The changes…
[FIX] html_builder: fix job location editing on website Scenario: 1) Head over to website 2) Go to any specific job page 3) Open editor 4) Try changing job location 5) Hit save Result: The changes aren't saved or propagated into the backend. Expectation: Users should be able to edit job locations via the editor on website and have those changes reflect on the site and job record. Cause: How elements were marked as savable was changed [in this IMP][1] to rely on `o_savable` rather than the savable selectors resource. As a result, elements which had the `o_not_editable` class, such as job location, did not have `o_savable` added to them. These elements were excluded from the builder's dirty-tracking for save. Therefore, editing the location didn't mark the element as changed and saving to drop the update. Fix: Remove `o_not_editable` from the location element on plugin setup so the field is now editable and savable through the builder option. This surfaced a second issue: when the location was set to "Remote", the element's content could be directly editable inline. Saving it that way disconnected the content from the job location field. This was fixed by adjusting the selector that determines when a many2one's content is editable inline. The result is the "Remote" case is handled consistently as changing to any other location. [1]: https://github.com/odoo/odoo/commit/f3c119dd034b4c3df9f392b0cdc66a1141662c25 Task-6311200 Forward-Port-Of: odoo/odoo#278777 Forward-Port-Of: odoo/odoo#274731
The test that verifies the behavior of cancelling the link popover during an attachment upload relies on a hard-coded delay. Occasionally, when the test runs on an overloaded runbot infrastructure, the upload manages to complete before the discard happens. This commit fixes this by making sure the upload never completes within the test. runbot-940190 runbot-944098 Forward-Port-Of: odoo/odoo#278820
Original PR description
The test that verifies the behavior of cancelling the link popover during an attachment upload relies on a hard-coded delay. Occasionally, when the test runs on an overloaded runbot infrastructure, the upload manages to complete before the discard happens. This commit fixes this by making sure the upload never completes within the test. runbot-940190 runbot-944098 Forward-Port-Of: odoo/odoo#278820
This commit [1] added a top margin to headings for the html_editor. However, this margin was also applied in the website, breaking the WYSIWYG behavior. Exclude the website from this rule so the margin is only applied in the html_editor. [1]: https://github.com/odoo/odoo/commit/13a452106733c950a7e25cfeb5107eb03367b756 Forward-Port-Of: odoo/odoo#278730
Original PR description
This commit [1] added a top margin to headings for the html_editor. However, this margin was also applied in the website, breaking the WYSIWYG behavior. Exclude the website from this rule so the margin is only applied in the html_editor. [1]: https://github.com/odoo/odoo/commit/13a452106733c950a7e25cfeb5107eb03367b756 Forward-Port-Of: odoo/odoo#278730
`_get_belgian_cocontractant_note()` resolves the co-contractant fiscal position through chart_template.ref(), which uses env.company. The "Send invoices automatically" cron runs as the inactive OdooBot user, so env.company is OdooBot's default company, not the invoice's. That company can differ from the invoice's and even be archived, in which case ref() raises "IndexError: tuple index out of range" (parent_ids is empty for an archived company) Steps to reproduce: - Set the main company to a
Original PR description
`_get_belgian_cocontractant_note()` resolves the co-contractant fiscal position through chart_template.ref(), which uses env.company. The "Send invoices automatically" cron runs as the inactive OdooBot user, so env.company is OdooBot's default company, not the invoice's. That company can differ from the invoice's and even be archived, in which case ref() raises "IndexError: tuple index out of range" (parent_ids is empty for an archived company) Steps to reproduce: - Set the main company to a non-Belgian company, and invoice from another active Belgian company. - Move every active user off the main company and archive it - Send a Belgian 0% invoice through the cron. => IndexError: tuple index out of range in chart_template.ref opw-6398778 Forward-Port-Of: odoo/odoo#278666 Forward-Port-Of: odoo/odoo#278326
The actions menu is cluttered with redundant presence actions. To streamline the UI, the presence actions have been removed from the root menu and are now kept exclusively within the "Presence Control" submenu. Steps to reproduce: 1. Navigate to an Employee record. 2. Open the Actions menu. 3. Observe that "Set Present", "Set Absent", "Send SMS", and "Create a Time Off" appear in the root menu AND in the "Presence Control" submenu. Furthermore, invoking these actions from the Presence C
Original PR description
The actions menu is cluttered with redundant presence actions. To streamline the UI, the presence actions have been removed from the root menu and are now kept exclusively within the "Presence…
The actions menu is cluttered with redundant presence actions. To streamline the UI, the presence actions have been removed from the root menu and are now kept exclusively within the "Presence Control" submenu. Steps to reproduce: 1. Navigate to an Employee record. 2. Open the Actions menu. 3. Observe that "Set Present", "Set Absent", "Send SMS", and "Create a Time Off" appear in the root menu AND in the "Presence Control" submenu. Furthermore, invoking these actions from the Presence Control submenu did not pass the correct record context. Because of this, dialogs like "Create a Time Off" failed to automatically prefill with the currently selected employee's data, disrupting the workflow. 1. Navigate to an Employee record. 2. Open the Actions menu and hover over the "Presence Control" submenu. 3. Click "Create a Time Off". 4. Observe that the employee field in the dialog is blank instead of prefilling with the current employee. task-6384993 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce ------------------ 1. install l10n_sa_edi and l10n_sa_pos 2. onboard the company for ZATCA and link a printer to the PoS 3. make a PoS order with a customer and print the receipt -> the ZATCA QR code is too small to be scanned. Why it's happening ------------------ The phase 2 QR code is big because it also contains the invoice hash, signature and public key. We render it at 200 px, which is too small to scan a QR with that much data. The QR image also has no max
Original PR description
Steps to reproduce ------------------ 1. install l10n_sa_edi and l10n_sa_pos 2. onboard the company for ZATCA and link a printer to the PoS 3. make a PoS order with a customer and print the receipt…
Steps to reproduce ------------------ 1. install l10n_sa_edi and l10n_sa_pos 2. onboard the company for ZATCA and link a printer to the PoS 3. make a PoS order with a customer and print the receipt -> the ZATCA QR code is too small to be scanned. Why it's happening ------------------ The phase 2 QR code is big because it also contains the invoice hash, signature and public key. We render it at 200 px, which is too small to scan a QR with that much data. The QR image also has no max width, so it gets cut when the receipt is narrow. The fix ------- Render it at 400 px, and add `max-width: 100%` so it is not cut on a narrow receipt. opw-6399766 Before <img width="647" height="1036" alt="image" src="https://github.com/user-attachments/assets/6bcb8526-71a8-4d9f-8372-219959416214" /> After <img width="649" height="1031" alt="image" src="https://github.com/user-attachments/assets/70f5fdb5-ba71-4fbe-8f03-ef0a1b29be2e" /> Forward-Port-Of: odoo/odoo#278737 Forward-Port-Of: odoo/odoo#277813
Issue: The unreserve button in forecast is no longer visible. Steps to Reproduce: 1. Create an MO for a product that has a storable component 2. Confirm the MO 3. Go to the component product form 4. Click on the forecast smart button Cause: After this commit, https://github.com/odoo/odoo/commit/df40bc9e261a62c045e7e6150a60663a582e7dae to support Owl3, the code was breaking because we were no longer passing line_index through `this`. Solution: Since the variable `line_index` was n
Original PR description
Issue: The unreserve button in forecast is no longer visible. Steps to Reproduce: 1. Create an MO for a product that has a storable component 2. Confirm the MO 3. Go to the component product form 4. Click on the forecast smart button Cause: After this commit, https://github.com/odoo/odoo/commit/df40bc9e261a62c045e7e6150a60663a582e7dae to support Owl3, the code was breaking because we were no longer passing line_index through `this`. Solution: Since the variable `line_index` was no longer available with `this`, we are now passing it as a parameter. opw-6317760 Forward-Port-Of: odoo/odoo#271322
# How to reproduce - Have two language in the db - Create an e-Commerce category - Have a different translation for each language for that category's name - Copy the link to that category's name in one language (Should be something like /shop/category/name-x) - Have two websites, each with a different default language - Create a link to the shop category in each website. > ! The link must be the exact same > You can put it in the header menu for simplicity - Open the website editor - D
Original PR description
# How to reproduce - Have two language in the db - Create an e-Commerce category - Have a different translation for each language for that category's name - Copy the link to that category's name in…
# How to reproduce
- Have two language in the db
- Create an e-Commerce category
- Have a different translation for each language for that category's name
- Copy the link to that category's name in one language (Should be something like /shop/category/name-x)
- Have two websites, each with a different default language
- Create a link to the shop category in each website.
> ! The link must be the exact same
> You can put it in the header menu for simplicity
- Open the website editor
- Do 2 or 3 times :
- click on the category link and switch website
# The issue
A traceback is displayed
# Cause
This manipulation will make us enter an infinite redirect scenario described here :
https://github.com/odoo/odoo/blob/f8741728294a5147c7d2427d9997738096386262/addons/website/static/src/client_actions/website_preview/website_builder_action.js#L371-L384
The problem is that this commit introduced an assignation to `iframe.contentDocument.body` before the patch :
https://github.com/odoo/odoo/commit/89994eb7a54ca606bab26b6d579d03e86c11ba60
But in our case, iframe.contentDocument is null, so it throws an error before the patch can be applied
opw-6322115
Forward-Port-Of: odoo/odoo#276634# How to reproduce - In Contacts, Create a contact with name X and mail Y - Go to the Recruitment App > Applications > All Applications - Create a new Application with name Z, mail Y, any Job Position - Save # The problem If we go back to the contact, we can see it's name changed from X to Z. This is an expect behavior since : https://github.com/odoo/odoo/commit/c06aefa827bc00a14c9f8bd994d1831053cbf7af The issue lies in the fact that this change to the linked partner is not logged in
Original PR description
# How to reproduce - In Contacts, Create a contact with name X and mail Y - Go to the Recruitment App > Applications > All Applications - Create a new Application with name Z, mail Y, any Job…
# How to reproduce - In Contacts, Create a contact with name X and mail Y - Go to the Recruitment App > Applications > All Applications - Create a new Application with name Z, mail Y, any Job Position - Save # The problem If we go back to the contact, we can see it's name changed from X to Z. This is an expect behavior since : https://github.com/odoo/odoo/commit/c06aefa827bc00a14c9f8bd994d1831053cbf7af The issue lies in the fact that this change to the linked partner is not logged in the Applicant's form view. This may lead to contacts being unitentionally updated. # The cause `hr.applicant` inherits from 'mail.track.mixin', which correctly handles the logging in the chatter when editing an Applicant. However, the edition of the contact is triggered by an inverse function when creating the record, which is not handled by the inherited module : https://github.com/odoo/odoo/blob/af50cb24ac536e6afb14eee8221c69906191ba2b/addons/hr_recruitment/models/hr_applicant.py#L248-L253 # Proposed solution Use the `_track_record` method while tacking inspiration from : https://github.com/odoo/odoo/blob/3b5e4f558ccf9160e74e9e49ae324dd616241d61/addons/account/models/account_move_line.py#L2079 opw-6095671 Forward-Port-Of: odoo/odoo#271547
sale_loyalty filters amount_type == 'fixed' taxes when building discountable_per_tax in all three helpers: _discountable_order , _discountable_cheapest, _discountable_specific, so fixed taxes stay only on the original product line and are not transferred onto the discount reward line.
Original PR description
sale_loyalty filters amount_type == 'fixed' taxes when building discountable_per_tax in all three helpers: _discountable_order , _discountable_cheapest, _discountable_specific, so fixed taxes stay…
sale_loyalty filters amount_type == 'fixed' taxes when building discountable_per_tax in all three helpers: _discountable_order , _discountable_cheapest, _discountable_specific, so fixed taxes stay only on the original product line and are not transferred onto the discount reward line.
pos_loyalty has the equivalent filter on _getDiscountableOnOrder (pos_order.js:962, added in commit 68d35232dd5) but not on the two sibling methods. As a result, when a promotion program uses discount_applicability='cheapest' or 'specific', the fixed tax is copied onto the reward line's tax_ids and because the reward line carries a negative price its fixed-tax contribution cancels the same tax on the original product line, understating the order total.
This change ports the filter expression from _getDiscountableOnOrder to _getDiscountableOnCheapest and _getDiscountableOnSpecific, preserving the e-wallet / gift-card carve-out so those programs can still consume the full amount.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#267020
Forward-Port-Of: odoo/odoo#261370Once the cron is called to update current_version_id, the new employee calendar is applied to the previous version as well, due to the inverse on resource.resource def _inverse_calendar_id(self): for resource in self: if resource.calendar_id != resource.employee_id.resource_calendar_id: resource.employee_id.resource_calendar_id = resource.calendar_id All that because the introduced piece of code was called before super if 'current_versi
Original PR description
Once the cron is called to update current_version_id, the new employee calendar is applied to the previous version as well, due to the inverse on resource.resource
def _inverse_calendar_id(self):
for resource in self:
if resource.calendar_id != resource.employee_id.resource_calendar_id:
resource.employee_id.resource_calendar_id = resource.calendar_id
All that because the introduced piece of code was called before super
if 'current_version_id' in vals:
new_version = self.env['hr.version'].browse(vals.get('current_version_id'))
self.resource_id.calendar_id = new_version.resource_calendar_id
And this the inverse method was called on the previous version (not yet updated), not the new one.
Forward-Port-Of: odoo/odoo#279048## Problem When a `web_read_group` call is made with some condition on the active field, the active test is bypassed by adding `['active', 'in', [True, False]]` to the domain. This will cause a search to fail if the model's active field is not called `active` (like in a studio model). ## Solution We will change the domain to `[self._active_name, 'in', [True, False]]` to properly handle customizations. ## Steps to replicate (Runbot v19) 1. Create a new model with Studio - enable Pipeline
Original PR description
## Problem When a `web_read_group` call is made with some condition on the active field, the active test is bypassed by adding `['active', 'in', [True, False]]` to the domain. This will cause a search to fail if the model's active field is not called `active` (like in a studio model). ## Solution We will change the domain to `[self._active_name, 'in', [True, False]]` to properly handle customizations. ## Steps to replicate (Runbot v19) 1. Create a new model with Studio - enable Pipeline and Archiving 2. Open the kanban view and add 'Archived' to the filter 3. Traceback opw-6403422 Forward-Port-Of: odoo/odoo#277908 Forward-Port-Of: odoo/odoo#277666
When an order/invoice is fully discounted by a global discount, its base and tax amounts are sums of values each already rounded to the currency precision, so their sum is only a sub-unit floating point residue (~1e-6) instead of a clean zero. `_get_tax_totals_summary` fed that raw sum straight to the cash rounding. With a 'UP' (or 'DOWN') rounding method the residue was inflated into a full rounding step, e.g. a 0.01 total to pay on an otherwise empty document. opw-6402143 --- I confi
Original PR description
When an order/invoice is fully discounted by a global discount, its base and tax amounts are sums of values each already rounded to the currency precision, so their sum is only a sub-unit floating point residue (~1e-6) instead of a clean zero. `_get_tax_totals_summary` fed that raw sum straight to the cash rounding. With a 'UP' (or 'DOWN') rounding method the residue was inflated into a full rounding step, e.g. a 0.01 total to pay on an otherwise empty document. opw-6402143 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278291
When the shop sidebar categories are enabled with collapsed categories, subcategory names could wrap unexpectedly at 100% zoom or lower, even when there was enough available space. Steps to reproduce: - Open the Shop page and click Edit - Go to the Style tab - Set Content Width to Full - Enable the Categories sidebar - Enable Collapse Category This issue caused sidebar subcategory labels to wrap only at lower zoom levels, while higher zoom levels displayed them correctly. Add CSS
Original PR description
When the shop sidebar categories are enabled with collapsed categories, subcategory names could wrap unexpectedly at 100% zoom or lower, even when there was enough available space. Steps to reproduce: - Open the Shop page and click Edit - Go to the Style tab - Set Content Width to Full - Enable the Categories sidebar - Enable Collapse Category This issue caused sidebar subcategory labels to wrap only at lower zoom levels, while higher zoom levels displayed them correctly. Add CSS rules to prevent category names in the sidebar from wrapping. opw-6296804 Forward-Port-Of: odoo/odoo#275279
When receiving a Peppol/UBL XML file containing an embedded PDF via an email alias, the PDF is not extracted and attached to the resulting vendor bill. Steps to reproduce: - Set up a BE Company - Configure an incoming mail server - Set up an email alias for the Vendor Bill journal - Receive a Peppol XML with embedded PDF via alias - Check the created Bill Issue: PDF has not been extracted from the xml This occurs because the received xml is set as main attachment for the record a
Original PR description
When receiving a Peppol/UBL XML file containing an embedded PDF via an email alias, the PDF is not extracted and attached to the resulting vendor bill. Steps to reproduce: - Set up a BE Company - Configure an incoming mail server - Set up an email alias for the Vendor Bill journal - Receive a Peppol XML with embedded PDF via alias - Check the created Bill Issue: PDF has not been extracted from the xml This occurs because the received xml is set as main attachment for the record and in this case we skip extraction opw-6075250 Forward-Port-Of: odoo/odoo#278806 Forward-Port-Of: odoo/odoo#262047
- Fix: The edit() calls now pass { confirm: false } to avoid calling the extra step that involved clicking manually on the input to trigger the search and the dropdown display. This should remove the race condition. - Small cleanup: clickFieldDropdownItem replaces the hardcoded ".dropdown-item:nth-child(1)" click, allowing selecting by product name instead of position. runbot-error: 941390 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-P
Original PR description
- Fix: The edit() calls now pass { confirm: false } to avoid calling the extra step that involved clicking manually on the input to trigger the search and the dropdown display. This should remove the race condition.
- Small cleanup: clickFieldDropdownItem replaces the hardcoded ".dropdown-item:nth-child(1)" click, allowing selecting by product name instead of position.
runbot-error: 941390
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#278813Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an amount of 0 is created. But, the 0 invoice is never emailed to the customer. Whereas, for orders where the total is more than 0, an invoice is created, then emailed to the customer. The customer should be emailed the invoice, even if its amount is 0. Steps to reproduce: 1. Enable automatic invo
Original PR description
Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an…
Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an amount of 0 is created. But, the 0 invoice is never emailed to the customer. Whereas, for orders where the total is more than 0, an invoice is created, then emailed to the customer. The customer should be emailed the invoice, even if its amount is 0. Steps to reproduce: 1. Enable automatic invoicing. 2. Create a code for a 100% discount. 3. As a shopper, add a product to cart on the website. 4. While checking out, apply the 100% discount code to the order. 5. Complete the checkout. 6. Confirm that an invoice was created and posted, but was not emailed to the customer. Explanation: Normally, order confirmation and invoicing are handled by the `_post_process` method on the `payment.transaction` model. With automatic invoicing enabled, `_post_process` confirms the sale order, creates the invoice, and sends the invoice via `_send_invoice` (another method on the `payment.transaction` model). If `sale.async_emails` is enabled, `_post_process` will trigger a cron that invokes `_send_invoice` instead of invoking it directly. When an order is fully covered by a reward, there's nothing to pay. In this case, no payment.transaction record is ever created, and `_post_process` never runs. Instead, the order is confirmed through the `_validate_order` method on the `sale.order` model. The `sale_loyalty` module extends `_validate_order` so that, with automatic invoicing enabled, it will create and post an invoice for zero-amount orders. But, nothing in this path ever calls `_send_invoice` or an equivalent. So, the invoice is created and posted but never sent. Solution: This adds logic for sending invoices to the extension of `_validate_order` in the `sale_loyalty` module. We mirror the logic used in `_send_invoice` in the `payment.transaction` model. Notes: There is duplicated code from `_send_invoice` in this fix. That is because `_send_invoice`, a method on the `payment.transaction` model, can't be used in this flow. A fix that avoids code duplication would require serious refactoring. This will never trigger a cron to send the invoice, even if `sale.async_emails` is enabled. That is because the cron invokes `_send_invoice`. Since fully reward-covered orders are probably not common, any performance benefits of using a cron are probably not significant. But, making a new cron to be used in this case is also an option. opw-6363334 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277970 Forward-Port-Of: odoo/odoo#275190
The bugfix introduced in https://github.com/odoo/odoo/commit/01d11a770f89c391f7c6a2c46a3d770d10afb428 made it so that "personal" outgoing mail servers are blocked/ignored from being used in email marketing as dedicated servers. In doing so, it missed the fact that while in *Email Marketing > Settings*, the "Dedicated Server" picker now correctly ignores personal OMS entries, the selection widget for `mail_server_id` in `view_mail_mass_mailing_form` still let's you manually select a OMS with a
Original PR description
The bugfix introduced in https://github.com/odoo/odoo/commit/01d11a770f89c391f7c6a2c46a3d770d10afb428 made it so that "personal" outgoing mail servers are blocked/ignored from being used in email…
The bugfix introduced in https://github.com/odoo/odoo/commit/01d11a770f89c391f7c6a2c46a3d770d10afb428 made it so that "personal" outgoing mail servers are blocked/ignored from being used in email marketing as dedicated servers.
In doing so, it missed the fact that while in *Email Marketing > Settings*, the "Dedicated Server" picker now correctly ignores personal OMS entries, the selection widget for `mail_server_id` in `view_mail_mass_mailing_form` still let's you manually select a OMS with an owner set. As there is not warning or an explicit error, this can lead to accidental miss configurations on an email marketing campaign, where the selected OMS will be actually ignored by the backend.
To align the changes introduced by the bugfix, we add a search domain to the selection field, so that only OMS with `('owner_user_id', '=', False)` will be presented as a choice.
OPW-6388562
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#278918
Forward-Port-Of: odoo/odoo#278595Miscellaneous changes
Deleting `loyalty.card` records causes severe performance bottlenecks due to a sequential scan during the foreign key constraint check on `pos_order_line.coupon_id` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278555
Original PR description
Deleting `loyalty.card` records causes severe performance bottlenecks due to a sequential scan during the foreign key constraint check on `pos_order_line.coupon_id` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278555
On a production-sized database the `hr.leave.attendance.report` SQL view took over three hours to run. This caused a significant load on the upgrade platform when processing databases that have a large number of `hr.employee` records. Specifically, when running `test_mock_crawl` and mocking the `Attendances > Reporting > Time Off Ledger` menu. PostgreSQL mis-estimated the row counts produced by the view: - The public-holiday check combined an `OR` with a function-wrapped `BETWEEN` (
Original PR description
On a production-sized database the `hr.leave.attendance.report` SQL view took over three hours to run. This caused a significant load on the upgrade platform when processing databases that have a…
On a production-sized database the `hr.leave.attendance.report` SQL view took over three hours to run. This caused a significant load on the upgrade platform when processing databases that have a large number of `hr.employee` records. Specifically, when running `test_mock_crawl` and mocking the `Attendances > Reporting > Time Off Ledger` menu. PostgreSQL mis-estimated the row counts produced by the view: - The public-holiday check combined an `OR` with a function-wrapped `BETWEEN` (`... AT TIME ZONE ... ::date`), which the planner cannot estimate; it predicted ~1 surviving row (actual: 29.4M) and chose nested loops that re-aggregated whole tables once per output row. - The attendance sub-query aggregated the entire `hr_attendance` table with no date bound, and was re-executed per output row. - The working schedule was resolved with a per-(employee, day) `LIMIT 1` lookup into `hr_version` (29.4M index probes). Rewrite the view as a set of CTEs: every heavy table is scanned once, joins use plain equality keys (hash-joinable), public holidays are pre-expanded so their exclusion stays an anti-join, the attendance aggregate is bounded to the report window, and hr_version is resolved by expanding each version over the days it covers. None of the CTEs are explicitly materialized: left to its own heuristic, PostgreSQL inlines a CTE referenced only once as a plain subquery and materializes the ones referenced more than once, which benchmarked faster than forcing materialization everywhere. This rewrites the body of the SQL view only: no schema change, no new field, no index, no migration. The report output is unchanged. Measured with EXPLAIN (ANALYZE, BUFFERS) on the same database: | metric | before | after | factor | |----------------|---------------|----------|--------| | execution time | 11 852 235 ms | 1 834 ms | ~6500x | | buffer hits | 115 311 556 | 71 890 | ~1600x | upg-4288902 Forward-Port-Of: odoo/odoo#278690 Forward-Port-Of: odoo/odoo#266108
Both the `bus.bus` long-polling logic and the garbage collector vacuum cron rely on filtering by `create_date` to fetch new messages and purge expired notifications. Under heavy real-time notification usage or high client concurrency, this table grows significantly. Without an index on `create_date`, these frequent operations are forced to run full sequential scans. This commit adds a dedicated index on `create_date` to enable efficient index scans. --- I confirm I have signed the CLA
Original PR description
Both the `bus.bus` long-polling logic and the garbage collector vacuum cron rely on filtering by `create_date` to fetch new messages and purge expired notifications. Under heavy real-time notification usage or high client concurrency, this table grows significantly. Without an index on `create_date`, these frequent operations are forced to run full sequential scans. This commit adds a dedicated index on `create_date` to enable efficient index scans. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278542
When a bus process restarts, every client on every database it serves loses its websocket at the same instant and immediately schedules a reconnection. Each database that comes back triggers a registry recompute server-side, so a tight reconnection window makes all of those recomputes land together and pile up on the freshly started process. The jitter added to each reconnection delay was only one second, far too narrow to break up that wave. Widen it to thirty seconds so the retries of the m
Original PR description
When a bus process restarts, every client on every database it serves loses its websocket at the same instant and immediately schedules a reconnection. Each database that comes back triggers a…
When a bus process restarts, every client on every database it serves loses its websocket at the same instant and immediately schedules a reconnection. Each database that comes back triggers a registry recompute server-side, so a tight reconnection window makes all of those recomputes land together and pile up on the freshly started process. The jitter added to each reconnection delay was only one second, far too narrow to break up that wave. Widen it to thirty seconds so the retries of the many clients fan out over a much wider window and the registry recomputes spread over time instead of colliding. Raise the ceiling on the retry delay to two minutes to match that wider spread, and drop the exponential growth factor: with a thirty-second jitter accumulating on every attempt, the delay already climbs on its own, so scaling it further only pushed clients toward the ceiling sooner without spreading them any better. 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#278599 Forward-Port-Of: odoo/odoo#276869
### before ~1.4s <img width="1869" height="819" alt="image" src="https://github.com/user-attachments/assets/168e3831-929b-43fd-9aa1-ea5bfb22d3fe" /> ### after ~570ms <img width="1862" height="860" alt="image" src="https://github.com/user-attachments/assets/ea907064-fe12-4711-9707-67926f05cedf" /> See commit messages --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274360
Original PR description
### before ~1.4s <img width="1869" height="819" alt="image" src="https://github.com/user-attachments/assets/168e3831-929b-43fd-9aa1-ea5bfb22d3fe" /> ### after ~570ms <img width="1862" height="860" alt="image" src="https://github.com/user-attachments/assets/ea907064-fe12-4711-9707-67926f05cedf" /> See commit messages --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274360