Daily updates from Odoo
Wednesday, August 12, 2026
50 changes
5 changes
Enhancements to existing features
Document attachment link calculations have been optimized to reduce unnecessary searches and improve performance. This should make document-related operations feel faster, especially where many attachments are involved, without changing user-facing features.
Original PR description
* Prefetching attachment_ids in sudo allows to limit the scope of the documents search * Removing the location filter on the document, not worth the performance hit. Follow-up of Task-5882406 Forward-Port-Of: odoo/enterprise#127062
Resolved issues and error corrections
This fix updates how mandatory SSS and Pag-IBIG payroll contributions are calculated for Philippine payroll. It ensures the contribution bases use the correct salary categories, improving payroll accuracy and compliance reporting.
Original PR description
. SSS Mandatory contribution is categories['TAX_CASH_EARNINGS'] . Pag-IBIG Contribution is categories['PH_BASIC'] + categories['ECOLA'] . Update the corresponding tests task-6431960
This fix prevents users in multi-company setups from hitting an unsolvable error when creating operation steps without the Quality Control module installed. It ensures quality team email aliases always have an appropriate company value, so manufacturing quality workflows continue smoothly across companies.
Original PR description
This commit actually reverts [1] and manually forwards [2]. Suppose `mrp_workorder` installed and `quality_control` uninstalled. Because of the default value provided by [1], the only existing quality team is linked to the first company. As a result, when using another company, if the user tries to create an operation step (i.e., a QCP), it will raise an error when the onchange tries to load the default team in charge: https://github.com/odoo/enterprise/blob/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e/quality/models/quality.py#L141-L142 However, the `quality` module doesn't provide any view to create such a team. tldr The module raises an error that is actually impossible to solve... Let's avoid it in the above situation. [1] https://github.com/odoo/enterprise/commit/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e [2] https://github.com/odoo/enterprise/commit/8cd5c9322bef7db49a90d4aef844dd0ba267058e Forward-Port-Of: odoo/enterprise#127438 Forward-Port-Of: odoo/enterprise#126364
Subscription products now show discounted recurring prices correctly on shop product tiles. This prevents customers from seeing a price based on the one-time sale price when a discount is meant to apply to the subscription plan.
Original PR description
Steps to reproduce: =================== 1. Create a subscription product, allow one-time sale, sale price 5 2. Add a recurring price 10/month 3. On the pricelist, add an advanced rule: -10% for the…
Steps to reproduce: =================== 1. Create a subscription product, allow one-time sale, sale price 5 2. Add a recurring price 10/month 3. On the pricelist, add an advanced rule: -10% for the monthly plan 4. Open the shop page and look at the product tile Cause: ======= On the /shop page, the subscription price displayed on a product tile is computed by `_get_sales_prices`. The cart has no plan selected yet at that point, so `request.cart.plan_id.id` is empty and was passed as `plan_id` to `_compute_price`. In `product.pricelist.item._compute_base_price`, the recurring base price is only looked up when a `plan_id` is given: if rule_base == 'list_price' and product.recurring_invoice and plan_id: ... # find the recurring rule -> base = recurring price With `plan_id` empty, that branch is skipped and the percentage rule falls back on the product's one-time `list_price` instead of the recurring price. Example: one-time price 5, recurring price 10/month, pricelist rule -10% on the monthly plan. => Tile showed 4.5/month (5 * 0.9) instead of 9/month (10 * 0.9). Solution: ========= The chosen pricing already targets a plan, so pass `pricing.plan_id.id` to `_compute_price`, matching what the product page does in `_get_additionnal_combination_info`. opw-6307398 Forward-Port-Of: odoo/enterprise#124815 Forward-Port-Of: odoo/enterprise#120872
Payslip CFDIs in Mexican payroll now have their SAT validation status updated correctly in Odoo. This prevents validated payroll documents from incorrectly showing an undefined status, improving compliance visibility for payroll teams.
Original PR description
l10n_mx_hr_payroll_account_edi introduces new l10n_mx_edi.document states (payslip_sent, payslip_sent_failed, payslip_cancel, payslip_cancel_failed) but never extends the two hooks the base l10n_mx_edi module relies on to keep sat_state in sync: - _get_update_sat_status_domains(), which builds the domain used by the SAT-status cron (and manual refresh) to pick documents to poll. Payslip states were missing from it, so their SAT status was never fetched at all. - _update_document_sat_state(), which routes a fetched SAT status to a per-source-document handler. It has no branch for the payslip states, so even a manual poll would silently do nothing. As a result, payslip CFDIs validated in the SAT always appeared as "not_defined" in Odoo. opw-6192651 Forward-Port-Of: odoo/enterprise#126294 Forward-Port-Of: odoo/enterprise#124006
4 changes
Resolved issues and error corrections
Australian payroll now matches unused leave balances to the correct employee when processing multiple payslips at once. This prevents eligible unused leave from being missed, helping final pay and related payroll calculations stay accurate.
Original PR description
`_l10n_au_get_unused_leave_by_type` compared leave allocations to `self.employee_id` while looping payslips. On a multi-recordset that is the whole employee set, so the match never holds and unused leave is skipped. Use `payslip.employee_id` so each payslip keeps its own allocations. task-6458480 Forward-Port-Of: odoo/enterprise#127330
Spreadsheet side panel items now use the same drag-and-drop behavior and visual feedback across list dimensions, sorting rules, pivot dimensions, and global filters. This makes reordering items more predictable and prevents accidental drag actions when using controls like delete buttons or selectors.
Original PR description
Current behavior before PR: - Dragging list dimensions and sorting rules felt visually different from pivot dimensions and global filters in the side panel. - The list side panel used a separate drag-and-drop utility that did not match the consistent UX of other spreadsheet components. Desired behavior after PR is merged: - List dimensions and sorting rules now share the same drag-and-drop behavior and visual feedback as pivot dimensions and global filters. - All reorderable items in the side panel now look and feel the same, providing a consistent user experience across the spreadsheet. Task: [6219600](https://www.odoo.com/odoo/project/2328/tasks/6219600)
Mexican payroll CFDIs now have their SAT validation status updated correctly in Odoo. This prevents validated payslips from incorrectly appearing with an undefined status, improving compliance visibility for payroll teams.
Original PR description
l10n_mx_hr_payroll_account_edi introduces new l10n_mx_edi.document states (payslip_sent, payslip_sent_failed, payslip_cancel, payslip_cancel_failed) but never extends the two hooks the base l10n_mx_edi module relies on to keep sat_state in sync: - _get_update_sat_status_domains(), which builds the domain used by the SAT-status cron (and manual refresh) to pick documents to poll. Payslip states were missing from it, so their SAT status was never fetched at all. - _update_document_sat_state(), which routes a fetched SAT status to a per-source-document handler. It has no branch for the payslip states, so even a manual poll would silently do nothing. As a result, payslip CFDIs validated in the SAT always appeared as "not_defined" in Odoo. opw-6192651 Forward-Port-Of: odoo/enterprise#126294 Forward-Port-Of: odoo/enterprise#124006
This fixes an issue where signatures or other fields could disappear from downloaded signed PDFs when the original document had unusual page positioning. Signed documents should now match the preview more reliably, reducing failed signing workflows and customer confusion.
Original PR description
Steps to reproduce (version 16+): 1) Obtain a pdf with a negative origin point: This can occur when a customer exports a pdf from another software, or it can be made manually using a python script 2) In the sign app, upload the pdf and create a new template, add a signature field to the document. 3) Sign the document. The preview will load correctly and the signature will be visible 4) Download and open the signed pdf. The signature is not on the document Notes: Issue occurs because the signature was added to the pdf outside of the visible area. The preview works because the signature is rendered on top of the unsigned document in the correct location. The issue can be fixed applying a translation to the canvas. Ticket: [6317223](https://www.odoo.com/odoo/project/49/tasks/6317223?debug=assets) Forward-Port-Of: odoo/enterprise#126921 Forward-Port-Of: odoo/enterprise#121960
6 changes
Resolved issues and error corrections
Fixed an error that could occur when users turned the No Follow-Up option on or off for invoices with multiple payment installments. This keeps the Follow-Up Report usable when some installments are already paid and others remain open.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156This fix ensures Mexican payroll CFDI payslips are included when Odoo checks their official SAT status. Businesses will now see the correct validation or cancellation status instead of an undefined status, improving payroll compliance visibility.
Original PR description
l10n_mx_hr_payroll_account_edi introduces new l10n_mx_edi.document states (payslip_sent, payslip_sent_failed, payslip_cancel, payslip_cancel_failed) but never extends the two hooks the base l10n_mx_edi module relies on to keep sat_state in sync: - _get_update_sat_status_domains(), which builds the domain used by the SAT-status cron (and manual refresh) to pick documents to poll. Payslip states were missing from it, so their SAT status was never fetched at all. - _update_document_sat_state(), which routes a fetched SAT status to a per-source-document handler. It has no branch for the payslip states, so even a manual poll would silently do nothing. As a result, payslip CFDIs validated in the SAT always appeared as "not_defined" in Odoo. opw-6192651 Forward-Port-Of: odoo/enterprise#126104 Forward-Port-Of: odoo/enterprise#124006
Tax closing entries now correctly include VAT credit carried over from the previous period. This prevents overstated tax payable amounts and ensures tax return journal entries match the expected carryover without extra user configuration.
Original PR description
### Issue before this commit: When generating a tax closing entry, the VAT credit carryover from the previous period is missing from the journal entry lines. This results in an incorrect net payable…
### Issue before this commit: When generating a tax closing entry, the VAT credit carryover from the previous period is missing from the journal entry lines. This results in an incorrect net payable amount. ### Steps to reproduce the issue: 1. Download Accounting 2. Go to Vendor > Bills 3. Create a vendor bill with date in June and price > 0 (ex. 1000$) and the 15% tax that produce a 150$ VAT tax 4. Go to Customers > Invoices 5. Create an invoice with price > 0 (ex. 9000$), the date in July and the 15% tax that will produce a 1350$ VAT tax 6. Go to Tax Return and validate all opened months up to July and see that for June the balance is -150$ 9. Then go to View Entries of July using the 3 dots next to the "submit" button and see that there is no mentioning of the 150$ carry over of credit from the month before ### Cause of the issue: During a forward-port merge conflict resolution, the code block responsible for retrieving the historical balance from the receivable_account_id was wrongly removed ([diff](https://github.com/odoo/enterprise/compare/b760c1dbee6bfb628bae32041c5b5c7dcaa623a4..4ea95b0c15f5f791d088330596c64ece58effa44)). Consequently, the system only checked the advance_account_id for previous balances, completely ignoring existing credits parked in the standard receivable account. ### Reason to introduce the fix: Restore the dropped logic and the _create_tax_receivable_current_line function. This ensures the tax closing process automatically factors in previous VAT credits from the receivable account, providing accurate closing entries out-of-the-box without requiring extra configuration from the user. opw-6438648
Australian payroll now correctly matches unused leave allocations to each employee when processing multiple payslips at once. This prevents unused leave from being skipped, helping ensure final pay calculations are accurate.
Original PR description
`_l10n_au_get_unused_leave_by_type` compared leave allocations to `self.employee_id` while looping payslips. On a multi-recordset that is the whole employee set, so the match never holds and unused leave is skipped. Use `payslip.employee_id` so each payslip keeps its own allocations. task-6458480 Forward-Port-Of: odoo/enterprise#127330
Fixed an issue where rental returns could fail after using stock transfers for serialized rental products. The system now keeps the pickup and return serial number history correctly, so users can complete later returns without manual errors or blocked workflows.
Original PR description
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return…
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return wizard. **Steps to reproduce** - Activate "Rental Transfers" in the settings - Create a rental product P, tracked by serial number - Create two serial numbers for P - Create and confirm a rental order for 2 units of P - Validate the pickup transfer - Partially validate the return transfer without creating a backorder - Open the rental order and click on "Return" -> The return wizard opens without any available serial number and validation fails with a serial number-related error. **Cause** When clicking on "Return", if there is no pending pickup/return transfer: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/models/sale_order.py#L62-L68 the rental return wizard is opened directly: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/models/sale_order.py#L316 No serial number is prefilled in the wizard because `returned_lot_ids` is empty: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L122-L124 This is because `returnable_lot_ids` is empty as well. `returnable_lot_ids` is computed while generating the wizard lines: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L38 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L47-L48 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L99-L106 and `returnable_lots` is empty because both `pickedup_lots` and `returned_lots` are. Those fields are currently only populated through the rental wizard flow: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L42-L43 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L160-L161 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L166-L167 Since this flow uses stock pickings instead of the rental wizard, those fields are never updated, preventing the wizard from determining any returnable serial number. opw-6150305 Forward-Port-Of: odoo/enterprise#126594 Forward-Port-Of: odoo/enterprise#119257
Refunding a POS order linked to Ecuador's generic "Consumidor Final" customer now shows the intended business error message instead of crashing. This helps cashiers understand why the refund cannot be validated and avoids an unexpected interruption at checkout.
Original PR description
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to…
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to reproduce: ------------------- In POS with l10n_ec_edi module activated: * Create a new order with "Consumidor Final" as customer * Add products and pay the order * Validate the order * Attempt to refund this order > Observation: The refund validation would crash with: TypeError: Cannot read properties of undefined (reading 'add') at OrderPaymentValidation.isOrderValid Why the fix: ------------ The code was trying to access `this.dialog` which is undefined in the OrderPaymentValidation class context. The dialog service should be accessed via `this.pos.dialog`, which is the correct pattern used throughout the base OrderPaymentValidation class. This fix ensures the error dialog is properly displayed when attempting to refund orders for the anonymous final consumer, instead of crashing with a TypeError. opw-6427113 Forward-Port-Of: odoo/enterprise#126308
3 changes
Resolved issues and error corrections
Refunding Ecuador POS orders made with the “Consumidor Final” customer now shows the intended warning instead of crashing. This helps cashiers understand why the refund cannot be validated and avoids an unexpected interruption at checkout.
Original PR description
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to…
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to reproduce: ------------------- In POS with l10n_ec_edi module activated: * Create a new order with "Consumidor Final" as customer * Add products and pay the order * Validate the order * Attempt to refund this order > Observation: The refund validation would crash with: TypeError: Cannot read properties of undefined (reading 'add') at OrderPaymentValidation.isOrderValid Why the fix: ------------ The code was trying to access `this.dialog` which is undefined in the OrderPaymentValidation class context. The dialog service should be accessed via `this.pos.dialog`, which is the correct pattern used throughout the base OrderPaymentValidation class. This fix ensures the error dialog is properly displayed when attempting to refund orders for the anonymous final consumer, instead of crashing with a TypeError. opw-6427113 Forward-Port-Of: odoo/enterprise#126308
This fixes an issue where unused leave could be missed when processing payslips for multiple employees at once. Each payslip now uses the correct employee’s leave allocations, helping ensure Australian payroll amounts are calculated accurately.
Original PR description
`_l10n_au_get_unused_leave_by_type` compared leave allocations to `self.employee_id` while looping payslips. On a multi-recordset that is the whole employee set, so the match never holds and unused leave is skipped. Use `payslip.employee_id` so each payslip keeps its own allocations. task-6458480 Forward-Port-Of: odoo/enterprise#127330
Fixes an issue where using AI to update website SEO could fail when the AI agent relied on certain document-generated files, such as invoice PDFs. The AI feature can now correctly use those document sources, reducing interruptions for users optimizing website content.
Original PR description
### Problem When an AI agent includes a document source whose underlying `ir.attachment` has `res_field` set (e.g., an invoice PDF generated from a Sale Order), triggering **Update With AI** from…
### Problem
When an AI agent includes a document source whose underlying `ir.attachment`
has `res_field` set (e.g., an invoice PDF generated from a Sale Order),
triggering **Update With AI** from **Website → Site → Optimize SEO**
raises a `KeyError` in `_build_rag_context`.
### Steps to Reproduce
1. Open the **AI** app.
2. Configure the **Odoo Agent**.
3. Add a source → **Add From Documents**.
4. Select a document whose underlying `ir.attachment` has `res_field` set
(e.g., an invoice PDF generated from a Sale Order).
5. Go to **Website → Site → Optimize SEO**.
6. Click **Update With AI**.
7. Observe the following error:
```
KeyError: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```
[Video](https://drive.google.com/file/d/1AAg0AvC3TRQzMLhI9hWFuSarOTHzQE-b/view?usp=sharing)
### Root Cause
In `ai/models/ai_agent.py`, `_build_rag_context()` retrieves the
`ai.agent.source` records corresponding to the embeddings by searching on the
attachment checksum:
```python
agent_sources = self.env["ai.agent.source"].search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
```
This domain traverses `attachment_id.checksum`, which internally calls
`ir.attachment._search()`.
As part of the standard attachment search behavior,
`ir.attachment._search()` automatically injects a
`('res_field', '=', False)` filter unless:
- `skip_res_field_check=True` is set in the context,
- the domain explicitly references `id` or `res_field`, or
- `bypass_access` is enabled.
```python
domain = Domain(domain)
if (
not self.env.context.get("skip_res_field_check")
and not any(d.field_expr in ("id", "res_field") for d in domain.iter_conditions())
and not bypass_access
):
disable_binary_fields_attachments = True
domain &= Domain("res_field", "=", False)
```
[Reference](https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_attachment.py#L630)
Because of this implicit filter, attachments with `res_field` set are excluded
from the search. Consequently, `agent_sources` does not contain all the sources
corresponding to the retrieved embeddings.
Later, `_build_rag_context()` builds a checksum-to-source mapping:
```python
source_map = {
source.attachment_id.checksum: source
for source in agent_sources
}
for embedding in similar_embeddings:
checksum = embedding.attachment_id.checksum
agent_source = source_map[checksum]
```
Since `source_map` is built from the incomplete `agent_sources` recordset, it is
missing entries for attachments filtered by `ir.attachment._search()`.
However, `similar_embeddings` still contains embeddings for those attachments.
As a result, the lookup:
```python
agent_source = source_map[checksum]
```
raises a `KeyError`.
### Solution
Bypass the implicit `res_field` filter when searching `ai.agent.source`:
```python
agent_sources = (
self.env["ai.agent.source"]
.with_context(skip_res_field_check=True)
.search([
("attachment_id.checksum", "in", embeddings_attachment_checksums),
("agent_id", "=", self.id),
])
)
```
This ensures that all `ai.agent.source` records matching the requested
attachment checksums are returned, including those referencing attachments with
`res_field` set. As a result, `source_map` contains all expected entries and
`_build_rag_context()` no longer raises a `KeyError`.
opw-63798161 change
Resolved issues and error corrections
This fix prevents cancelled point-of-sale refunds from being counted when creating Mexican global invoices. Businesses can now generate the invoice correctly when an order has both a cancelled refund attempt and a completed refund, avoiding erroneous negative amounts.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404 Forward-Port-Of: odoo/enterprise#127454 Forward-Port-Of: odoo/enterprise#120996
3 changes
New functionality added to Odoo
A new FedEx certified delivery module adds the API changes required to meet FedEx certification guidelines. This helps businesses continue using FedEx shipping services with an approved integration and updated branding/configuration support.
Original PR description
For the certification process of FedEx there were some changes needed in the delivery_fedex_rest module. This modules made those changes according to the FedEx guidelines. Task-id: 6164275 Forward-Port-Of: odoo/enterprise#122663
Resolved issues and error corrections
The Dutch tax reporting status process now skips or handles records that are missing their linked closing entry instead of crashing. This helps keep Digipoort tax return status updates running even when one record has incomplete data.
Original PR description
The `l10n_nl_reports_sbr_status_info` contains the `l10n_nl_reports_sbr.status.service` class. The class is responsible for fetching the status of sent Digipoort tax returns. The status is then posted as a chatter message to the tax return's closing entry. Issues can arise when one of the status service records is, for whatever reason, missing a closing entry. In such case, the message cannot be posted, resulting in an exception being raised. Since the records are processed in a loop without a try-catch, this causes the whole action to fail. This can lead to one broken record effectively shutting down the whole module's functionality. This PR adds some if-else checks to gracefully handle the case where the closing entry is missing. Related tickets: opw-5901446 and opw-6410082 Forward-Port-Of: odoo/enterprise#127389 Forward-Port-Of: odoo/enterprise#125996
Users can now interact with Search More dialogs from document fields without the dialog unexpectedly closing. This prevents interruptions when choosing related records such as owners or customers in the Documents app.
Original PR description
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the…
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the field dropdown, click "Search More..." to open a modal dialog. 5. Click inside the "Search More..." modal (e.g., to sort columns or resize headers). Issue: - The modal dialog immediately closes, and the contact cannot be selected. Root cause: - When an inspector field is edited, the record row is put into edit mode. While in edit mode, the documents list renderer listens for global clicks. Clicking inside the "Search More..." modal dialog targets elements that have `.o_list_renderer` (since the modal dialog renders a list view). Because the click target is within a list renderer but is not a document row, `DocumentsListRenderer.onGlobalClick` executes and clears the selection of the main list view. Clearing the selection unmounts the edited field in the inspector, thereby destroying the modal dialog stack. Solution: - Modify DocumentsListRenderer.onGlobalClick to scope click handling to the current Documents list renderer. Ignore clicks outside this.root.el, so interactions in nested UI such as Search More... do not clear the main selection and destroy the inspector field. opw-6253360 Forward-Port-Of: odoo/enterprise#126614 Forward-Port-Of: odoo/enterprise#119262
25 changes
New functionality added to Odoo
Adds Schedule III Balance Sheet and Profit and Loss reports for Indian localization. This helps Indian businesses prepare statutory financial statements in the required format more directly within Odoo.
Original PR description
WIP task-2381149
This adds a new USPS shipping integration that uses USPS's current REST API instead of the older XML-based approach. Businesses using USPS delivery can benefit from alignment with USPS's recommended technology, improving long-term compatibility and supportability.
Original PR description
This new module should replace the existing implementation for USPS integration which uses XML which is not the recommended API currently by USPS. The new integration uses USPS's latest RESTful APIs: https://developer.usps.com/apis. Task-3759325
Enhancements to existing features
The Master Production Schedule forecast wizard is now easier to use when updating forecasts for one or many products. Users can start with the current period prefilled, see all forecasting basis options, and apply the same update settings across multiple selected products at once.
Original PR description
This commit improves the Master Production Schedule forecast suggestion wizard with the following enhancements: - Prefill current period when opening wizard from product - Show all "Based On" options even when a specific period is selected (Previously hidden when period was set) - Replace "Toggle Indirect Demand" with "Update Forecast" in Actions menu - Add multi-product "Update Forecast" wizard for bulk forecast updates - Shows product count instead of product selector - Preview calculation based on first product - Applies settings to all selected products simultaneously
Starting a work order from the list view now records time under the employees assigned to that work order instead of always using the currently logged-in user. If no employee is assigned, the system still falls back to the logged-in user, helping keep production time records accurate without disrupting existing workflows.
Original PR description
This commit changes the behavior of starting a work order from the `mrp_workorder` tree view in terms of the employee(s) who perform(s) the time logs of the workorder. Previosuly, the logged in user was always used to perform these time logs, irrespective of the assigned employees of the workorder. This commit uses the assigned employees instead and fallbacks to the logged-in user in case there was no assigned employee. Task-4105643
Saudi payroll now allows companies to set a threshold for unpaid leave days that should be excluded from end-of-service benefit calculations. This improves compliance and accuracy by ensuring extended unpaid absences are handled consistently in employee benefit reports.
Original PR description
[IMP] l10n_sa_hr_payroll: exclude unpaid days from EOS New field is added to company and company settings l10n_sa_unpaid_leave_eos_threshold, unpaid holidays above this threshold should be excluded from EOS calculation Instead of using function _l10n_sa_get_eosb_compensation in salary rule python amount compute, we put the function directly to the salary rule's itself. EOS benefit wizard is changed because it was using the function _l10n_sa_get_eosb_compensation and now it uses the salary rule directly. Test is written to test the implemented functionality by testing the EOS benefit report for different scenarios. task - 6393974
Indian GST reporting now lets businesses with turnover below 5 crore choose whether to include HSN details for B2C transactions. A new setting allows users to turn this reporting on or off, helping them align reports with the updated GSTIN rules.
Original PR description
The GSTIN has updated the rules for B2C HSN reporting. Now it has become optional for businesses having a turnover of less than 5 Cr. This improvement aims to integrate this change into our system by providing a boolean in settings. The user can switch on/off the reporting mechanism using this boolean. task-6097595 Community PR - https://github.com/odoo/odoo/pull/269014
Map route calculations are no longer enabled by default across field service planning views. Routing is now limited to the Maps and Live Map menus, helping reduce unnecessary Mapbox token usage and related customer costs.
Original PR description
Currently, the map view calculates routing by default whenever Mapbox is enabled. This ends up consuming unnecessary tokens (which cost the customer money) in views where routing isn't actually needed or relevant. To prevent this waste, we are turning off the default routing on the main map view. Moving forward, the routing feature is only explicitly enabled in the "Maps" and "Live Map" menus, where seeing the route actually makes sense for the user. task-6351070
When a bank statement line is mistakenly matched and then unreconciled, the system now removes the related bank account if it is not used elsewhere. This helps keep accounting records cleaner while still letting users manually decide whether to remove the partner before reconciling again.
Original PR description
When a bank statement line was matched with a move by mistake and the user unreconcile it, the bank account should be deleted if not used anywhere else. We chose not to remove the partner from the bank statement line automatically and let the user do it manually before reconcile again task-6285463
Manufacturing teams can now include draft manufacturing orders when planning work orders, giving schedulers earlier visibility into upcoming production needs. This helps teams reserve capacity sooner and align labor or cost reporting before orders are fully confirmed.
Original PR description
task: 6365342
Belgian payroll now lets HR decide whether a union meeting absence should grant a meal voucher on a case-by-case basis. This supports situations where eligibility depends on the employee’s representative status and the meeting type, improving payroll accuracy.
Original PR description
Whether a union meeting (LEAVE249) grants a meal voucher depends on the employee being an elected representative and on the type of meeting, so it cannot be decided by the time type alone. Declare MEAL_VOUCHER as an optional category of LEAVE249, letting the HR user tick it per time off. task-6356965
Document records now determine their linked attachments more efficiently by narrowing searches and avoiding an expensive location check. This should improve performance in document-heavy workflows without changing what users do day to day.
Original PR description
* Prefetching attachment_ids in sudo allows to limit the scope of the documents search * Removing the location filter on the document, not worth the performance hit. Follow-up of Task-5882406 Forward-Port-Of: odoo/enterprise#127062
The Indian reports module now includes document type 2 for GSTR-1 document summaries, covering invoices for inward supplies from unregistered persons. This helps businesses report self-invoice data required under Table 13 of GSTR rules.
Original PR description
with this commit:- - We added document type '2' which states 'Invoice for Inward Supply from Unregistered Person' for gstr1 document summary. - This is required to sent self invoice data to the government as per Table-13 of GSTR act. task-6259173
Resolved issues and error corrections
Fixes a server error that could occur when users turned the No Follow-Up option on or off for invoices paid in multiple installments. This makes follow-up reporting more reliable when some installments are already settled and others remain open.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156Shifts for employees with flexible schedules and no fixed start or end times are now included consistently in Planning and Timesheets reports. This prevents planned work from being missing in analysis views, giving managers a more accurate view of allocated time.
Original PR description
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning…
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning / Timesheets Analysis** report. ## Steps to Reproduce 1. Create an employee with a Flexible Working Schedule in the Employee form, or configure working hours where both `Hour from` and `Hour to` are left unset. 2. Add a shift for this employee linked to a project and a task. 3. Publish the shift. 4. Navigate to **Planning → Schedule → By Project**, switch to the pivot view, and observe that the shift created in step 2 appears and is counted. 5. Navigate to **Planning → Reporting → Planning / Timesheets Analysis**, switch to the pivot view, and observe that the same shift does not appear. ## Behavior After the PR When an employee does not have explicit `hours_from` and `hours_to` values, their shift is now considered valid in both the **Schedule by X** views and the **Planning / Timesheets Analysis** report. ## Additional Notes - In earlier versions of Odoo, the `Work From` and `Work To` fields were mandatory. With a change to flexible working schedules and the option to define only the total number of hours per day, these fields may now be left empty. This change exposed the underlying issue addressed by this fix. task-[5969788](https://www.odoo.com/odoo/project/4105/tasks/5969788) Forward-Port-Of: odoo/enterprise#127300 Forward-Port-Of: odoo/enterprise#110606
Users who have both Partner Commissions access and Purchase User access can now create and view purchase orders as expected. This prevents commission-related restrictions from accidentally blocking normal purchasing work, while keeping commission-only users limited appropriately.
Original PR description
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new…
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new Purchase Orders, and existing Purchase Orders are also not visible in the Purchase module. ## Expected behavior: The expected behavior is that the user should be able to create and view Purchase Orders with these access rights. Additionally, clarification is required regarding the purpose of the new Partner Commissions access group. ## Steps to reproduce: - Go to user and assign Partner Commission rights as All or own document. - On Purchase, select group as User. ## Cause of the issue: partner_commission adds commission-specific purchase order record rules, but purchase users have no matching purchase-order rule in that module. For mixed-role users, the commission rule ends up restricting standard purchase orders as well. ## Fix: Apply the module's explicit all-purchase rule to purchase users so mixed users keep base procurement access while commission-only users remain restricted by the commission rules. opw-6366074 Forward-Port-Of: odoo/enterprise#127188 Forward-Port-Of: odoo/enterprise#126003
A spreadsheet sharing update was incorrectly running during every page load, sometimes disrupting navigation state in other parts of Odoo. This fix limits that behavior to spreadsheet pages only, reducing unexpected page navigation issues.
Original PR description
BUG:
To make make spreadsheet backend shareabable, a patch to the router was done, and ran on *every* page load. The code looks harmless (it already had a `if (pathParts.at(-2) !== "spreadsheet")` early return, but because of the implementation of replaceState, this we had global impact regardless.
Indeed the real problem is that `router.replaceState` routes through doPush(), which replaces `history.state` disregarding the previous history completly (with a debounce, explaining the indeterminism)
In our case the builder stored `{skipRouteChange: true}` in `history.state` which was sometimes wiped by the `replaceState` debounce
FIX:
As a quick fix (JPP is on holidays), we amend the code to make spreadsheet shareable only run if the targetted app is spreadsheet
runbot-error: https://runbot.odoo.com/odoo/error/945518
breaking PR: https://github.com/odoo/enterprise/pull/114484A spreadsheet sharing update was unintentionally affecting navigation data across the system on every page load. This fix limits that behavior to spreadsheet pages only, reducing unexpected navigation issues in other apps.
Original PR description
BUG:
To make make spreadsheet backend shareabable, a patch to the router was done, and ran on *every* page load. The code looks harmless (it already had a `if (pathParts.at(-2) !== "spreadsheet")` early return, but because of the implementation of replaceState, this we had global impact regardless.
Indeed the real problem is that `router.replaceState` routes through doPush(), which replaces `history.state` disregarding the previous history completly (with a debounce, explaining the indeterminism)
In our case the builder stored `{skipRouteChange: true}` in `history.state` which was sometimes wiped by the `replaceState` debounce
FIX:
As a quick fix (JPP is on holidays), we amend the code to make spreadsheet shareable only run if the targetted app is spreadsheet
runbot-error: https://runbot.odoo.com/odoo/error/945518
breaking PR: https://github.com/odoo/enterprise/pull/114484The payroll validation flow now correctly opens any required follow-up step, such as a wizard for missing employee information. This prevents the Validate button from appearing unresponsive and helps users understand what needs to be fixed before completing a payslip.
Original PR description
action_validate() called action_payslip_done() without returning its result. When action_payslip_done() returns a client action (e.g. a wizard to fix missing employee data instead of raising), that action was lost and the Validate button appeared to do nothing, with no error or warning shown. task-6373549
Generating public holidays now works consistently outside Belgium by applying the same automatic public holiday time type behavior to all localizations. This prevents errors when users load public holidays and improves the out-of-the-box payroll experience.
Original PR description
Purpose: generating Public Holidays fails with an error on all localizations except Belgium, preventing users from loading public holidays. This is because the Belgium localization includes a dedicated Public Holiday time type, which is automatically selected when creating public holidays. Extending this behavior to all localizations will make the feature work out of the box and provide a consistent user experience. - removed the inheritance for `load.public.holiday.wizard.line` as it's implemented generally in `hr_holidays` task-id: 6448261
Spreadsheet sharing logic now only runs when users are actually opening a spreadsheet. This prevents unrelated pages from losing navigation state, reducing intermittent page behavior and improving reliability across the app.
Original PR description
BUG:
To make make spreadsheet backend shareabable, a patch to the router was done, and ran on *every* page load. The code looks harmless (it already had a `if (pathParts.at(-2) !== "spreadsheet")` early return, but because of the implementation of replaceState, this we had global impact regardless.
Indeed the real problem is that `router.replaceState` routes through doPush(), which replaces `history.state` disregarding the previous history completly (with a debounce, explaining the indeterminism)
In our case the builder stored `{skipRouteChange: true}` in `history.state` which was sometimes wiped by the `replaceState` debounce
FIX:
As a quick fix (JPP is on holidays), we amend the code to make spreadsheet shareable only run if the targetted app is spreadsheet
runbot-error: https://runbot.odoo.com/odoo/error/945518
breaking PR: https://github.com/odoo/enterprise/pull/114484This fix prevents access errors when displaying salary offer details that depend on payroll information. It also updates the salary configurator test flow to reflect use by an HR user rather than an administrator, helping ensure the feature works for normal HR staff.
Original PR description
A field displayed inside the offer should have been computed with sudo as it accesses some payroll field to compute. Also, the salary configurator tour has been adapted to use a HR user instead of an admin Forward-Port-Of: odoo/enterprise#127510
This fix prevents users in multi-company setups from hitting an unavoidable error when creating quality-related operation steps. It ensures the default quality team works correctly with the current company, reducing blocked manufacturing or quality workflows.
Original PR description
This commit actually reverts [1] and manually forwards [2]. Suppose `mrp_workorder` installed and `quality_control` uninstalled. Because of the default value provided by [1], the only existing quality team is linked to the first company. As a result, when using another company, if the user tries to create an operation step (i.e., a QCP), it will raise an error when the onchange tries to load the default team in charge: https://github.com/odoo/enterprise/blob/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e/quality/models/quality.py#L141-L142 However, the `quality` module doesn't provide any view to create such a team. tldr The module raises an error that is actually impossible to solve... Let's avoid it in the above situation. [1] https://github.com/odoo/enterprise/commit/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e [2] https://github.com/odoo/enterprise/commit/8cd5c9322bef7db49a90d4aef844dd0ba267058e Forward-Port-Of: odoo/enterprise#127438 Forward-Port-Of: odoo/enterprise#126364
This update restores previous Studio behavior after a recent change caused runtime crashes. It keeps the intended flexibility for an optional setting while improving stability for users editing views in Studio.
Original PR description
This commit mostly reverts the commit d9d90bfd6805355909faa28dadb249c88317c2b9 which caused crashes at runtime while keeping the main fix (keeping the prop optional).
The point of sale now correctly blocks payment when an order contains only deleted items. This prevents staff from completing empty orders with no payment and issuing blank receipts.
Original PR description
The payment screen could still be accessed even if the order only contained deleted (struck through) order lines. The user can then complete the order with no payment and an empty receipt. This commit fixes the issue by updating the `isEmpty` function on the order to take the deleted lines into account.
This fixes Mexican payroll payslip CFDIs so their SAT validation status is properly checked and shown in Odoo. Businesses will now see the correct tax authority status instead of an undefined value, improving payroll compliance visibility.
Original PR description
l10n_mx_hr_payroll_account_edi introduces new l10n_mx_edi.document states (payslip_sent, payslip_sent_failed, payslip_cancel, payslip_cancel_failed) but never extends the two hooks the base l10n_mx_edi module relies on to keep sat_state in sync: - _get_update_sat_status_domains(), which builds the domain used by the SAT-status cron (and manual refresh) to pick documents to poll. Payslip states were missing from it, so their SAT status was never fetched at all. - _update_document_sat_state(), which routes a fetched SAT status to a per-source-document handler. It has no branch for the payslip states, so even a manual poll would silently do nothing. As a result, payslip CFDIs validated in the SAT always appeared as "not_defined" in Odoo. opw-6192651 Forward-Port-Of: odoo/enterprise#126294 Forward-Port-Of: odoo/enterprise#124006
2 changes
Resolved issues and error corrections
Australian payroll now correctly matches unused leave balances to the employee on each payslip. This helps ensure termination or leave-related payouts are not missed when payroll is processed for multiple employees at once.
Original PR description
`_l10n_au_get_unused_leave_by_type` compared leave allocations to `self.employee_id` while looping payslips. On a multi-recordset that is the whole employee set, so the match never holds and unused leave is skipped. Use `payslip.employee_id` so each payslip keeps its own allocations. task-6458480
When warehouse staff scan multiple lot numbers during a receipt, new lot lines now keep the putaway destination already assigned by the receipt. This prevents items from appearing in the wrong stock location and keeps barcode receiving aligned with warehouse putaway rules.
Original PR description
Steps to reproduce --- 1. Enable Storage Locations and Lots & Serial Numbers. 2. Add a putaway rule sending a lot-tracked product from WH/Stock to WH/Stock/Shelf 1. 3. Confirm a receipt reserving 2…
Steps to reproduce --- 1. Enable Storage Locations and Lots & Serial Numbers. 2. Add a putaway rule sending a lot-tracked product from WH/Stock to WH/Stock/Shelf 1. 3. Confirm a receipt reserving 2 units of that product; putaway sets the reserved move line destination to WH/Stock/Shelf 1. 4. In the Barcode app, scan a first lot, then a second lot. The second lot lands on a separate line at WH/Stock instead of WH/Stock/Shelf 1. Issue --- The first lot reuses the reserved line and keeps its Shelf 1 destination. The second lot cannot reuse it because its tracking number differs, so `_findLine` returns nothing and `_getNewLineDefaultValues` builds a new line with `location_dest_id` set to `_defaultDestLocation()`, the picking's default destination (WH/Stock). https://github.com/odoo/enterprise/blob/314a79b774f30dc9377b2971492576c4b84483e1/stock_barcode/static/src/models/barcode_picking_model.js#L1591-L1601 Putaway relocates the destination on the move line at reservation, never on the picking, so only the reserved line carries Shelf 1. Since `groupKey` includes `location_dest_id`, the new line does not group with the first lot and shows separately at WH/Stock. This is not a regression: new lines have always defaulted to the operation destination. https://github.com/odoo/enterprise/blob/314a79b774f30dc9377b2971492576c4b84483e1/stock_barcode/static/src/models/barcode_picking_model.js#L239-L241 The new line now inherits the selected line's `location_dest_id`, already relocated by putaway, instead of the default. opw-6317077
1 change
Resolved issues and error corrections
Fixed an issue where exporting the Deferred Revenue Report to XLSX could fail when the report included annotations. This helps accounting users reliably export annotated reports without encountering a server error.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Unhide the **Start Date** and **End Date** fields on invoice lines. * Create and post a customer invoice with deferred dates. * Go to…
**Steps to reproduce:** * Install the **Accounting** module. * Unhide the **Start Date** and **End Date** fields on invoice lines. * Create and post a customer invoice with deferred dates. * Go to **Accounting → Reports → Deferred Revenue Report**. * Add an annotation to a deferred revenue line by clicking the **annotate** from three dots next to the account. * Export the report in **XLSX** format. **Observed behavior:** * The export fails with a server error: `UnboundLocalError: cannot access local variable 'annotations_x_offset' where it is not associated with a value` **Cause:** * The variable `annotations_x_offset` is assigned inside the `for header_level_index, header_level in enumerate(options['column_headers'])` loop, which writes the "Annotations" column header for each header level. * The Deferred Revenue Report produces an empty `column_headers` list, so the loop body never executes and `annotations_x_offset` is never assigned. * When the code later tries to write annotation data for each report line, it references the unassigned variable, causing Python to raise `UnboundLocalError`. **Fix:** * Introduce a boolean flag `annotations_header_written = False` before the header loop to explicitly track whether the "Annotations" column header has already been written. * Inside the header loop, set `annotations_header_written = True` after writing the header. * After writing all individual column headers (where `x_offset` already points to the first free column after all data columns), add a fallback: if `report_annotations` is set but `annotations_header_written` is still `False`, assign `annotations_x_offset` from the current `x_offset` and write the "Annotations" header. opw-6354473