Friday, September 4, 2026
50 changes · master
Resolved issues and error corrections
This update fixes reliability issues in guided onboarding tours, especially when tours switch modes, open the editor, or move across pages. Users should experience fewer interrupted or incorrectly validated tour steps in areas such as website forum onboarding.
Original PR description
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
This fixes an issue where Odoo live chat embeds could block browser keyboard shortcuts such as Alt+D on external websites. The shortcut overlay now only intercepts the Alt key when there are actual Odoo shortcut hints to show, preserving normal browser behavior otherwise.
Original PR description
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called…
Description of the issue/feature this PR addresses: When `im_livechat` (or any page) boots the OWL web stack with no `[data-hotkey]` / `withOverlay` targets, pressing Alt still called `preventDefault()` in `hotkey_service`. That blocked browser shortcuts such as Alt+D (focus address bar) on websites that embed the livechat scripts. See https://github.com/odoo/odoo/issues/267928 Current behavior before PR: - Pressing the overlay modifier (Alt, or Ctrl on macOS mapped to `"alt"`) always set `overlaysVisible = true` and called `preventDefault()`, even when there were no hotkeys to overlay. - A follow-up key (e.g. D while Alt is held) then also hit `preventDefault()` because overlays were considered visible. - Loading `/im_livechat/loader/...` + `assets_embed.js` on an external site therefore broke browser Alt shortcuts. Desired behavior after PR is merged: - `addHotkeyOverlays` returns whether overlays were actually displayed. - `preventDefault` on the overlay modifier runs only when there is at least one overlay target. - Pages with no hotkeys (typical livechat embed) leave browser shortcuts alone. - When hotkeys exist, Alt still shows overlays and cancels the default, same as before. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284145
This fixes a Point of Sale issue where the cash drawer could fail silently after refreshing the session because the default printer was not always remembered. The system now consistently saves the printer choice and prompts staff to select one when needed, helping checkout operations run smoothly.
Original PR description
Steps to reproduce: - Set only one printer with cashdrawer - Open PoS - Try to open the casdrawer => Silently won't open Issue: defaultPrinter was only saved in localstorage when various printer was set on the config. Fix: Now the printer is always saved in the localstorage so that when the pos is refresh, defaultPrinter is always set. Also when trying to open the cashdrawer if various printer are set in the config, make sure that the select default printer popup is shown. Task-6519466 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#285689
Product pages now keep multi-checkbox options unselected unless the shopper chooses them. This prevents confusing automatic selections and unwanted URL changes after refreshing a product page.
Original PR description
**Steps to reproduce:** 1. Create a product. 2. Add two attributes: multi-checkbox and any single-select type (e.g. Radio). 3. Publish the product and open its page on eCommerce. 4. Verify that no…
**Steps to reproduce:**
1. Create a product.
2. Add two attributes: multi-checkbox and any single-select type (e.g. Radio).
3. Publish the product and open its page on eCommerce.
4. Verify that no multi-checkbox option is selected by default.
5. Refresh the page twice.
**Issue:**
On the second page refresh, the first option of the multi-checkbox attribute is automatically selected, and the URL is updated with its parameter.
**Cause:**
- `_prepare_product_values` construct attribute combinations by mapping requested `attribute_values` query parameters line by line.
- When URL query parameters were present (e.g., set after the first refresh), the fallback logic `or ptal.product_template_value_ids.filtered('ptav_active')[:1]` treated unselected `multi_checkbox` attribute lines as missing required selections rather than empty selections, forcing them to default to their first active option.
**Fix:**
If no selection is provided for `multi_checkbox` line, return an empty recordset instead of falling back to the first active option.
opw-6494697
Forward-Port-Of: odoo/odoo#286492
Forward-Port-Of: odoo/odoo#284798The website shop editor no longer crashes when users choose the Grid catalog preset in the products design panel. This makes product page customization more reliable by preventing duplicate preset controls from conflicting internally.
Original PR description
Steps to reproduce: 1. Open the website editor on /shop. 2. Select the products grid and expand Products Page in the sidebar. 3. Click the Design button to open the Products Design sliding panel. 4. Click the 4th preset (Grid catalog) in the Preset picker. Before this pr: - The page crashed with a render loop error. After this pr: - presets can be picked normally, no crash. The preset list was being drawn twice at once, and both copies were registering under the same internal ID. Now each copy gets its ownnsuffixed ID, so they don't clash anymore. Also removed a redundant duplicate t-if on BuilderSelectItem already covered by its parent t-if. Also removed a dead label.translate="Presets" attribute that ProductsDesignPanelPresets never reads. opw-6478531 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284636
Users can now download spreadsheets with inserted images as Excel files without hitting an access error. The export process now uses the image access permissions already used in the web interface, so shared spreadsheet images remain available during export.
Original PR description
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the…
Since https://github.com/odoo/enterprise/pull/97488, the images are shared across spreadsheets and the attachments are no longer related to a specific record. Due to the management of the IrAttachment access rights, it means that those attachments are not accessible by anyone by default and we rely on the access_token to display them in the webclient. However, the method that builds the final xlsx file fetches the images from the server and did not use the access token, meaning that it could never access the attachment. Such situation raised a UserError that was caught by the webclient. How to reproduce: - As admin, create a spreadsheet and insert an image inside of it - try to download the spreadsheet as an xlsx file counterpart of https://github.com/odoo/enterprise/pull/126384 Task-6432724 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#285981 Forward-Port-Of: odoo/odoo#279788
Italian electronic invoices now list only the relevant linked down payment document instead of including unrelated past invoices or the current credit note. This helps avoid incorrect FatturaPA XML content and reduces confusion or compliance issues when issuing credit notes and final invoices.
Original PR description
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without…
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without reconciling it with anything else. - Generate the FatturaPA XML for credit note A. - Observe that DatiFattureCollegate lists both down payment invoice A and credit note A itself, instead of only down payment invoice A. On an order with several down payment invoices/credit notes over time, every one of them is listed instead of just the document the current credit note actually reverses. Cause of the issue: _l10n_it_edi_export_data built downpayment_moves from self.invoice_line_ids._get_downpayment_lines().move_id with no filtering. _get_downpayment_lines() (sale override) returns every invoice line ever created against the same down payment sale order line, across the whole life of the order, not just the invoice the current document actually relates to. The template renders DatiFattureCollegate for every one of those moves unconditionally, on top of the already-correct reversed_entry_id/reconciled_moves fallback, including the current document itself. Solution: Restrict downpayment_moves to lines with a negative price_subtotal, mirroring the existing down payment deduction-line detection a few lines above, and explicitly exclude the current document. This limits DatiFattureCollegate's down payment entries to the case they are meant for: a final invoice actually deducting a previously invoiced down payment. opw-6429716 Forward-Port-Of: odoo/odoo#285555 Forward-Port-Of: odoo/odoo#279938
This fixes an issue in Project Forecast where automatic planning could behave incorrectly when several roles were involved. Businesses using role-based forecasting should see more reliable planning results and fewer manual corrections.
Original PR description
task-6484707 Forward-Port-Of: odoo/enterprise#130166 Forward-Port-Of: odoo/enterprise#128541
DHL shipping in Odoo no longer automatically requests a package pickup. This removes the need to set pickup windows, reduces DHL configuration errors, and makes DHL behave more consistently with other shipping carriers.
Original PR description
Before this commit, the DHL REST module had the pick-up request set, which led to an unnatural use flow, needing to set up an scheduled date for pick-up, and several errors from DHL when these were not properly configured. This feature was unique to this module among the shipping carriers as we don't normally request pick-up for packages and leave it to user to decide how to do it. This commit removes the request for pick up and the logic that was needed to make it work. This will make the behavior consistent with other carriers in Odoo, and avoids the need to set scheduled pick-up windows and having to deal with unnecessary errors. opw-6148927 Forward-Port-Of: odoo/enterprise#123773
The event booth registration page now ignores outdated booth lists when visitors switch categories quickly. This prevents the page from showing booths from the wrong category and helps exhibitors complete booth selection without needing to reload.
Original PR description
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones…
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones back. The tour webooth_exhibitor_register timed out on that list: FAILED: [5/13] Tour webooth_exhibitor_register -> Step Choose Booth (trigger: .o_wbooth_booths div:contains(OpenWood Demonstrator 2) input:not(:visible)). TIMEOUT step failed to complete within 10000 ms. This happens because the page fetches the booths of the category checked on load, and clicking another category fetches its booths while that first answer is still on its way. The answer coming back last fills the list, and the widget caches it under the category active on arrival, so the booths of the first category land in the cache of the clicked one. This commit fixes the issue by caching an answer under the category it was asked for, and by filling the list only while that category is still the chosen one. https://runbot.odoo.com/odoo/error/110527 https://runbot.odoo.com/odoo/error/161834 Forward-Port-Of: odoo/odoo#286552 Forward-Port-Of: odoo/odoo#286266
French tax closing entries now correctly include VAT credits carried over from the previous period. This helps ensure VAT returns and journal entries comply with French accounting requirements and avoid missing credit balances.
Original PR description
### Issue before this commit: When generating a tax closing entry for France, the VAT credit carryover from the previous period is missing from the journal entry lines. ### Steps to reproduce the…
### Issue before this commit: When generating a tax closing entry for France, the VAT credit carryover from the previous period is missing from the journal entry lines. ### Steps to reproduce the issue: 1. Download Accounting and l10n_fr 2. Go to Vendor > Bills 3. Create a vendor bill with date in June and price > 0 (ex. 1000$) and the 20% G tax that produce a 200$ VAT tax 4. Go to Customers > Invoices 5. Create an invoice with price > 0 (ex. 9000$), the date in July and the 20% G tax that will produce a 1800$ VAT tax 6. Go to Tax Return and validate all opened months up to July and see that for June the balance is -200$ 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 200$ carry over of credit from the month before ### Cause of the issue: The issue stems from a structural change in the core code where the logic to evaluate and balance the tax receivable account was removed from the _add_tax_group_closing_items method. Consequently, the VAT closing entry now only processes the current period's taxes and there is no reporting of any carried-forward VAT credits. ### Reason to introduce the fix: French accounting rules strictly require the carried-forward VAT credit to be explicitly integrated into the current period's tax closing entry. opw-6438648 Forward-Port-Of: odoo/enterprise#130377 Forward-Port-Of: odoo/enterprise#129181
The French VAT reporting flow now checks for duplicate XML declarations before sending them to Aspone. This helps avoid rejected submissions and prevents unnecessary paid contacts with the service provider.
Original PR description
When sending an xml to aspone, they will check if a duplicate declaration exist, and if it's the case, then they will refuse it. Since contacting aspone cost us money we will block the sending before that. task-6420220 Forward-Port-Of: odoo/enterprise#130406 Forward-Port-Of: odoo/enterprise#127953
This fix prevents point-of-sale online payments from appearing twice in an order's payment history after confirmation. It also avoids an error when online self-ordering settings are unavailable, improving reliability for stores using POS online payments.
Original PR description
Also, bug noticed while working on task-6472188
Opening a project task in the customer portal could fail when the Timesheets app was not installed. The fix keeps time formatting available from the Project app itself, so portal task pages load reliably in more setups.
Original PR description
Before this commit, opening the portal page of a task in a database without hr_timesheet answers a 500:
AttributeError: 'account.analytic.line' object has no attribute
'_format_portal_hours'
This happens because project's portal_my_task_allocated_hours_template formats the allocated time with `_format_portal_hours`, a method hr_timesheet adds on `account.analytic.line`.
This commit moves that method to project, next to the template that calls it.
https://runbot.odoo.com/odoo/error/946944Belgian payroll meal voucher reports now correctly include postponed vouchers for employees assigned to a branch company. This prevents missing carryovers when payroll documents are created under a branch while the report is managed by the parent company.
Original PR description
Steps to reproduce: - Create a July payslip for Bernice and mark it as paid - Generate the meal voucher report for july - Create 3 time off for Bernice in July - Correct the payslip of July, in total…
Steps to reproduce: - Create a July payslip for Bernice and mark it as paid - Generate the meal voucher report for july - Create 3 time off for Bernice in July - Correct the payslip of July, in total we have 3 payslips now - Create an August payslip for Bernice - Create the meal voucher report for august Pay attention to: everything has been created under "My Belgian Company" while Bernice is in the branch "My Belgian Office" Current Behaviour: When the august mealvoucher report is not marked as done, the postponed shows 0 for Bernice. Expected Behavior: When the report is not marked as done, august should have 3 mealvoucher postponed for Bernice. The technical reasons behind this bug is that the SQL query for unreported payslips mapped to draft/ready reports filtered the payslips `company_id` on the mealvoucher `company_id`. But payslips for Bernice are under the branch "My Belgian Office" and the mealvoucher is created in "My belgian company". The SQL query should take company branches into account, same as in `_get_corrected_payslips`. task-6428719
Discount reward products are now created without automatically applying the company's default sales tax. This prevents discounts from being taxed incorrectly and keeps loyalty reward calculations aligned with expected untaxed discount behavior.
Original PR description
The reward's discount_line_product_id was created without an explicit taxes_id, so it silently fell back to the company's default sale tax (account_sale_tax_id) instead of staying untaxed like the rest of the program's discount/reward mechanics expect. Explicitly clear taxes_id when creating the discount product. task-6528122
Point of Sale screens now keep category images inside their buttons and use higher-resolution product images. This makes the sales interface cleaner and easier to recognize items quickly during checkout.
Original PR description
Category images overflowed their buttons because width-based ratio classes conflicted with the fixed button heights. Furthermore, product images appeared blurry because the low-resolution `image_128` was stretched to fit modern product cards. This ensures category images respect parent boundaries and fetches higher resolution product images. task-6479160
Time entries recorded outside an employee's normal schedule now behave correctly. Working time is counted as requested, while absences outside the schedule now show a clear message instead of silently recording zero time.
Original PR description
…dule Encoding a time type outside an employee's working schedule silently did nothing: 0 duration, no feedback, for both working time and absence types. Working time entries now count the requested time instead of 0. Absences still can't be taken outside the schedule, but now say so instead of silently doing nothing. Task-6501553
Accounting now correctly flags a draft journal entry as creating a numbering gap when a later entry is posted after it. This helps businesses maintain clearer audit trails and avoids unnoticed gaps in journal entry sequences.
Original PR description
To reproduce: * create and post 2 journal entries in the same journal * reset to draft the entry with the highest number * create and post a third entry in the same journal The draft entry is not flagged as having made a gap, because at the time of resetting it to draft, it was the last entry and therefore didn't really make a gap. 3 options to solve were considered: * flag all moves when reseting to draft even if they were the last of the chain * if we reset the last move of the sequence to draft, also remove its sequence and put it back to `/` * when posting, check if the previous number was draft. If it was the case, flag it. The last options was taken in this fix to avoid changing the previous behavior while fixing the current issue. Forward-Port-Of: odoo/odoo#286204
Creating a salary contract offer for Belgian employees no longer triggers an unintended Dimona update. This prevents incorrect or meaningless data from being sent to the government service during salary simulations.
Original PR description
### Issue When creating a salary contract offer (smart button 'New Offer' in the employee form), a dimona action can be triggered, sending no-sense value to the government api. ### Reproducing steps…
### Issue
When creating a salary contract offer (smart button 'New Offer' in the employee form), a dimona action can be triggered, sending no-sense value to the government api.
### Reproducing steps
I've only been able to observe that by placing a breakpoint in the `hr.version.write` of the `hr_version.py` of `l10n_be_hr_payroll`:
- Create a master (1 sept. 2026) database with demo data and the following modules installed: `l10n_be_hr_contract_salary,test_l10n_be_hr_payroll_account`
- Set the dimona environment of the demo belgian company to 'Sandbox'
- Go to the employee Laurie Poiret
- Set the current contract end date to 1 day before today
- Create a dimona IN declaration for the last contract of Laurie Poiret (the one that has just been modified) (ctrl + k / DIMONA)
- On employee form: click on 'Check Dimona' and apply the response, the state of the dimona should appear ('Done')
- Create a new contract ('New Contract' button on the employee payroll tab) and set it to today
- Change the contract start date to tomorrow so that the dimona state become 'In progress'
- Click on the smart button 'New Offer'
- See that the `hr.version.action_update_dimona` is executed (but it shouldn't)
task-6521357This fix prevents Belgian payroll payslips from creating duplicate input lines when a payslip is recalculated. It keeps payroll records cleaner and helps avoid incorrect or confusing payroll calculations after edits.
Original PR description
Steps to reproduce: - Create a December BE payslip so SIMPLE_DECEMBER/DOUBLE_DECEMBER_BASIC get seeded (or any struct feeding a BE-specific input code). - Trigger a recompute (edit date_from and save again). - Input line for that code is duplicated instead of replaced. _compute_input_line_ids's BE override only appended input lines, never unlinking the one it created last pass. Unlink existing line for a code before recreating it Task 6516068
Hourly and half-day time off requests on weekends or public holidays will no longer disappear without explanation. The system now sends these requests to the server so valid working-time entries are created and invalid absence requests return a clear message.
Original PR description
Issue: Creating an hourly or half-day (AM/PM) time off entry on a day flagged as a weekend/public holiday for the employee did nothing: no entry, no error. Cause: multiCreateRecords() pre-filtered out any such day client side via get_unusual_days(), regardless of the work entry type. Fix: Remove the client-side filter and let every selected day go through create(), with multi_leave_request set in the context so the server decides and notifies. working time entries get a real duration, absences get dropped with a clear message (see community PR). Task-6501553
Expense-created vendor receipts in Mexico now correctly include and display their CFDI XML attachment. This helps users access required tax documents directly from the bill and ensures these receipts can be checked with SAT services when needed.
Original PR description
**Current behavior:** Currently, account moves created from expenses (type receipt) don't include the XML when it is a CFDI. Causing users cannot see their attachment even though it is created on DB. https://docs.google.com/videos/d/1eFSAA-wvUzPDwIJDeiS2dkne94lGM7QBxRfjN3HxaLw/play **Versions:** 19+ **Fix:** Implementing a new helper to identify those moves that can actually hold a CFDI document (for now, all `is_invoice()` documents + vendor bill receipt, `in_receipt`), so now, we include 'in_receipts' in _compute_l10n_mx_edi_cfdi_state_and_attachment, _compute_l10n_mx_edi_document_ids, _compute_l10n_mx_edi_update_sat_needed and l10n_mx_edi_cfdi_try_sat. As these documents might need to fetch SAT services as well. Task-id: [6397080](https://www.odoo.com/odoo/project/49/tasks/6397080) Forward-Port-Of: odoo/enterprise#130045 Forward-Port-Of: odoo/enterprise#126493
Uploading a refund from bank transactions now creates a vendor credit note in the correct purchase journal instead of a customer credit note. This helps refunds reconcile properly against vendor payables and prevents accounting errors during bank reconciliation.
Original PR description
**Steps to reproduce:** * Install **Accounting** module. * Go to **Accounting → Bank → Transactions** (bank reconciliation widget). * Open a transaction with a **positive** amount (e.g. a vendor…
**Steps to reproduce:** * Install **Accounting** module. * Go to **Accounting → Bank → Transactions** (bank reconciliation widget). * Open a transaction with a **positive** amount (e.g. a vendor sending money back). * Click the three-dot menu on the transaction line and choose **Upload a Refund**. * Upload any document (XML or PDF). **Observed behavior:** * A **Customer Credit Note** (`out_refund`) is created in a **sale** journal instead of a **Vendor Credit Note** (`in_refund`) in a **purchase** journal. * The wrong document type means the reconciliation fails to link the refund against the correct payable account. **Cause:** * In `create_document_from_attachment` (`account_bank_statement.py`), the JS widget sends `type='sale'` in context when the transaction amount is positive (JS: `amount > 0 ? "sale" : "purchase"`). * The original code mapped `type='sale'` → `default_move_type='out_refund'` (customer credit note) and searched for a `sale` journal — both wrong. * Uploading from a bank statement is always a **vendor-side** operation: negative amount = vendor bill (`in_invoice`), positive amount = vendor refund (`in_refund`). The `type` context key from JS reflects transaction direction, not the accounting document type. * Additionally, `in_refund` is a purchase document; Odoo's `_check_journal_move_type` constraint raises a `ValidationError` if a purchase document is created in a non-purchase journal, meaning the old code would crash at the ORM level for the refund path. **Fix:** * Map `type='sale'` → `default_move_type='in_refund'` (vendor credit note) instead of `out_refund`. * Always search for a **purchase** journal regardless of the `type` context value, since both `in_invoice` and `in_refund` are purchase-side documents. opw-6468779 Forward-Port-Of: odoo/enterprise#129385 Forward-Port-Of: odoo/enterprise#127912
Odoo now calculates Mexican CFDI payment complements using the same rounded amounts that appear in the XML. This prevents valid USD invoices with MXN payments from being rejected by the tax certification provider with error CRP20268.
Original PR description
**Steps to reproduce:** * Install the **l10n_mx** localization. * Enable **USD** and fetch the latest currency exchange rate from the settings. * Configure **Quadrum** as the **PAC** in the settings.…
**Steps to reproduce:**
* Install the **l10n_mx** localization.
* Enable **USD** and fetch the latest currency exchange rate from the settings.
* Configure **Quadrum** as the **PAC** in the settings.
* Create and sign a USD invoice with **16% IVA** (e.g. 4,500 USD + 720 USD tax = 5,220 USD total).
* Send the invoice to **CFDI**.
* Register a **partial MXN payment** that does not convert to a round USD amount (e.g. 30,000 MXN = 1,762.45 USD).
* Send the payment complement to the PAC by clicking **Update Payments** on the invoice.
**Observed behavior:**
The PAC rejects the CFDI with error **CRP20268**:
> El campo BaseP que corresponde a Traslado, no es igual a la suma de
> los importes de las bases registrados en los documentos relacionados
> donde el impuesto del documento relacionado sea igual al campo
> ImpuestoP de este elemento y la TasaOCuotaDR del documento
> relacionado sea igual al campo TasaOCuotaP de este elemento.
**Cause:**
* The SAT validator enforces a strict arithmetic relationship between three fields that are printed in the payment complement XML:
```
BaseP == round(BaseDR / EquivalenciaDR, 6)
```
* When `percentage_paid` is a non-terminating decimal (which happens for any partial MXN payment against a USD invoice), the internal `raw_base` float carries more precision than the 6-decimal-place `BaseDR` that is actually written to the XML:
```
percentage_paid = 1762.45 / 5220 = 0.33763409961685825...
raw_base = 4500 × 0.33763... = 1519.353448275862...
BaseDR (XML) = float_round(raw_base, 6) = 1519.353448
```
* The old code then used `raw_base` (the unrounded internal value) as the dividend when computing BaseP:
```
BaseP (old) = float_round(1519.353448275862... / 0.0587483333, 6)
= 25862.068980 ← written to <TrasladoP BaseP="...">
```
* The PAC performs the same division using only what it can read from the XML. The already-rounded BaseDR:
```
BaseP (PAC) = round(1519.353448 / 0.0587483333, 6)
= 25862.068975
```
* The 6th-decimal mismatch (25862.068980 ≠ 25862.068975) triggers CRP20268 and the document is rejected.
* The same issue occurs with a full MXN payment on a different date than the invoice when multiple invoice lines cause the rounded aggregate to diverge from the sum of per-line raw values.
**Fix:**
In `_l10n_mx_edi_add_payment_cfdi_values` (`account_move.py`), the block that builds the BaseP aggregation list was changed from iterating over raw per-`base_line` amounts to building a single synthetic entry per invoice using the already-rounded document-level `BaseDR` values (`tax_details['base']`) as the dividend:
- Before : wrong: raw_base ≠ BaseDR printed in the XML
'raw_base': tax_details['raw_base'] / inv_rate
- After : correct: 'base' == BaseDR, the exact value in the XML
'raw_base': tax_details['base'] / inv_rate
This guarantees that Odoo and the PAC divide the identical value, producing the identical 6-decimal result.
The `base_line_cfdi_values_mx_curr_list` path (used only for the `Totales` summary fields rounded to 2 dp) is left unchanged because the CRP20268 rule does not apply to that block.
opw-6468077
Forward-Port-Of: odoo/enterprise#128357Consolidated Malaysian e-invoice reports now group point-of-sale receipts only when they are truly consecutive on the same device. This prevents unrelated receipts or refund-only transactions from being hidden inside misleading ranges, making reports easier to verify and more accurate for compliance.
Original PR description
A consolidated invoice reports a range of PoS receipts per line, so every line claims that the receipts between its two endpoints follow each other. That claim was only loosely enforced: lines were…
A consolidated invoice reports a range of PoS receipts per line, so every line claims that the receipts between its two endpoints follow each other. That claim was only loosely enforced: lines were built by walking every order of the sessions involved and breaking whenever one was not part of the batch. Two receipts that were both in the batch were therefore always merged, whatever sat between them. This breaks down as soon as several devices sell under one config. Receipt numbers come from a counter local to each device, so two orders taken on two devices can carry adjacent numbers without being consecutive receipts, and were reported as a range covering receipts that were never part of it. Lines are now built from an explicit continuity test on both `sequence_number`, gapless within a config, and the receipt reference read from `pos_reference`, which must name the same device and the next receipt number. Neither is sufficient alone: an order can reach the database later than its ticket was opened, so following sequence numbers do not imply consecutive receipts either. A receipt made exclusively of refund lines is no longer merged into the line of the sales it follows. Refunds are usually rung up shortly after the sale they cancel, which made them contiguous with it and hid both inside a netted total - two sales and the two refunds cancelling them were reported as a single line of 0.00. An order mixing refund lines with new sales remains a receipt of its own, reported like any other with the amounts it refunded already deducted from it. The orders linked to a consolidated invoice are also listed in the order in which they are reported on it, rather than in the reverse chronological one used by default for PoS orders, so that the two can be read together. [task-6166533](https://www.odoo.com/odoo/my-tasks/6166533)
Fixes a billing issue where manually increasing a timesheet invoice could prevent later time entries from being invoiced. Businesses can now bill future timesheet periods correctly while keeping refund-related safeguards intact.
Original PR description
## Issue When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent…
## Issue
When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent logged hours are already covered by the previously over-invoiced amount, blocking the billing of the most recent timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P
- Product Type: Service
- Invoicing Policy: Based on Timesheets
- Create on Order: (Project &) Task
3. Create an SO:
- Customer: Any
- Product: P (any quantity)
- Confirm
4. Record hours on the task created:
- 06/01/2026 (June 1st): 1 hour
- 07/01/2026 (July 1st): 1 hour
5. Create the invoice for the June timesheet entry (by setting a timesheets period when creating the invoice), then **change the quantity to any value strictly greater than 2** and confirm.
6. Create the invoice for July
7. **An "Invalid Operation" error appears, stating that there's nothing to invoice, even though the timesheet entry from July was never invoiced.**
## Cause
Since https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20, the computation of the quantity to invoice changed to include the difference between the quantity delivered and the quantity already invoiced. In the flow described by the steps to reproduce above, the quantity to invoice is larger than the quantity delivered, making the `qty_to_invoice` equal to `0.0`.
https://github.com/odoo/odoo/blob/626d31fa0191bfe7ca8e1fcf177357eeeb60f2c7/addons/sale_timesheet/models/sale_order.py#L331-L334
The reason for the fix above being the partial refunding of timesheet-related invoices, we can keep that solution when working with refunded invoices, and keep the previous behavior for other cases.
This solution solves the issue in the steps to reproduce above, but was also manually tested on the issues from https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20 and https://github.com/odoo/odoo/commit/64cf4afabd0c5ef040cf87fc6f3125fe0bb81bbb.
opw-6485674
Forward-Port-Of: odoo/odoo#286301
Forward-Port-Of: odoo/odoo#284470This fix ensures Adyen payment information is read from the correct part of the payment notification. It helps prevent payment records from being updated with wrong or missing values, improving transaction reliability for businesses using Adyen.
Original PR description
opw-6512723 Forward-Port-Of: odoo/odoo#286391 Forward-Port-Of: odoo/odoo#284773
Website theme installation now correctly applies the selected theme's layout changes, such as headers and footers. This prevents customers from seeing an incomplete or unchanged website design after using the website configurator.
Original PR description
--- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286407 Forward-Port-Of: odoo/odoo#283174
This fixes a mobile usability issue in the HTML editor where users could not drag and drop table cells inside Todo items. The change prevents the browser's touch handling from interrupting the drag action, making table editing work as expected on phones and tablets.
Original PR description
Steps to Reproduce - Insert a table inside a Todo item. - Long-press the table menu to open the drag-and-drop overlay. - Try to drag and drop table cells. Issue: - Table cells cannot be dragged and dropped on mobile devices. Cause: - On mobile devices, the browser fires `pointercancel`/`pointerleave` during a drag operation, which ends the drag operation prematurely. As a result, subsequent `pointermove` events are not triggered causing the drag-and-drop operation to fail. Solution: - Add `touch-action: none` to the table menu element. This prevents the browser default touch handling from interfering with the drag operation, allowing `pointermove` events to continue and drag-and-drop to work correctly on mobile devices. task-6201176 Forward-Port-Of: odoo/odoo#283866 Forward-Port-Of: odoo/odoo#267680
Odoo now records a VoIP call more accurately when it is answered or rejected in another phone application such as Linphone. This prevents calls from being incorrectly shown as missed and gives users a clearer call history when multiple VoIP tools are open.
Original PR description
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and…
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and Linphone ring - Answer or reject using Linphone => The VoIP call record in Odoo immediately switches from "Trying to call" to "Missed". While there was no guarantee for our VoIP integration to work alongside Linphone in 19.0, we decided this should be an easy safe enough fix. Starting 19.2 (with [1]), the fix will be simplified and hopefully prettier thanks to the ameliorations that were made. After this fix, provided Odoo is open while Linphone is used, the call records will now switch to the right terminated / rejected status, still immediately once Linphone answers / rejects. In future versions and especially 20.0+, the system will be different and will allow way more features like this one to work better (e.g. here the call record only even exists if Odoo is opened while using Linphone and we won't have any information about the call duration). [1]: https://github.com/odoo/enterprise/commit/942f32316ab02d8c739fe7fdd5ec2bdde472a68e task-6449259 Forward-Port-Of: odoo/enterprise#130047 Forward-Port-Of: odoo/enterprise#127077
Refund payslips are now recalculated correctly whenever worked days or payslip lines are recomputed, not only after a reset. This helps ensure payroll corrections remain accurate and reduces the risk of incorrect refund amounts.
Original PR description
Instead of only resetting correctly the refund payslip in the reset, ensure that anytime we recompute worked days or lines, the refund is correctly computed. Forward-Port-Of: odoo/enterprise#129332
Belgian payroll now correctly treats public holidays as unpaid when they fall after an employee's guaranteed sick pay period has ended, even if a new medical certificate starts that same day. This prevents accidental overpayment and improves payroll accuracy for long-term sickness cases.
Original PR description
Steps to reproduce: - Employee on full-time sickness certified month by month (one hr.leave per medical certificate), guaranteed salary period already exhausted. - Compute payroll for the month whose public holiday falls on the first day of a new certificate. - Public holiday work entry stays paid, every other day that month is correctly unpaid. Cause: the 30-day lookback compared exact datetimes instead of calendar dates, so a certificate starting the same day as the holiday but later in clock-time got excluded. Also ignored timezone: date_from is stored in UTC and needs localizing before taking .date(). Fix: compare local calendar dates via hr.leave's own request_date_from/request_date_to instead of raw UTC datetimes. Added a regression test for the split-certificate case. Task 6512860 Forward-Port-Of: odoo/enterprise#129889
Dark mode now uses the full expected color palette and avoids mixing light-mode colors into dark-mode styling. This prevents missing or incorrect colors in screens and labels that rely on automatic color selection.
Original PR description
1) The "$o-colors" css variable was rebuilt in "secondary_variables.dark.scss", which loads after community's "primary_variables.scss" already assigned it via !default. The dark "$o-colors: ()!default;" reset was silently skipped, so dark colors were appended onto the light ones instead of replacing them, doubling "$o-colors" to 24 entries. => Moving this logic to "primary_variables.dark.scss", which correctly loads first. 2) "$o-colors-secondary-original" only listed 18 colors instead of the 44 used in the original "$o-colors-secondary" of light mode, meaning the dark mode's total palette ($o-colors-complete) had far fewer entries than the 56 that the JS helper "getColor()" assumes leading to missing coloring. => Extending the list so that the dark mode "$o-colors-secondary-original" matches the original "$o-colors-secondary" light mode's 44 colors.
Analytic plan applicability now falls back to the default setting when a screen does not provide a business context, even if a company-specific rule exists for invoices. This prevents unrelated areas like Work Centers or Employee views from incorrectly requiring analytic distribution.
Original PR description
### Issue: When an analytic plan has applicability lines with a company filter, the applicability is incorrectly applied on views that do not define a `business_domain`, such as Work Centers or…
### Issue: When an analytic plan has applicability lines with a company filter, the applicability is incorrectly applied on views that do not define a `business_domain`, such as Work Centers or Employee views For example, if a plan has: - Default Applicability: Unavailable - A line with Domain: Invoice, Company: My Company, Applicability: Mandatory Opening the analytic distribution on a Work Center shows `Mandatory` instead of the default `Unavailable` ### Cause: In commit https://github.com/odoo/odoo/commit/ffcf2ee1a3185ef73db93bfd95625844506692c5 `_get_score` was updated to return `0.5` when the applicability line's company matches the caller's company, even when no `business_domain` is provided In `_get_applicability`, the loop selects the first rule whose score exceeds the current minimum, which starts at `0`: https://github.com/odoo/odoo/blob/710e056e5171af2ab72d7d7793da3518f12faf5e/addons/analytic/models/analytic_plan.py#L255-L264 A score of `0.5` is enough to win over the default applicability, so a company-only match on a domain-specific rule incorrectly overrides the default when no `business_domain` is passed ### Steps to reproduce: - Install `mrp` and `accountant` with demo data - Enable Analytic Accounting in Settings - Open the Internal analytic plan and edit its applicability line: -- Remove the account prefix -- Default Applicability: Unavailable -- Domain: Invoice, Company: My Company (SF), Applicability: Mandatory - Go to Manufacturing > Configuration > Work Centers - Open any work center and click on Analytic Distribution Before the fix, Internal is shown as Mandatory instead of Unavailable Removing the company from the applicability line confirms the issue opw-6404884 Forward-Port-Of: odoo/odoo#280312
Accountants without company access rights can now generate BOE files for Spanish Modelo 115 tax reports without receiving an access error. The change prevents the export wizard from attempting an unnecessary company update, keeping the workflow available to accounting users as intended.
Original PR description
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax…
Users who only have accounting rights (not the Settings > Users Companies > Access Rights group) get an access error on the Companies model when generating the BOE file for the Modelo 115 tax reports, even though exporting a BOE file has nothing to do with editing company configuration and should be available to any accountant. Steps to reproduce: ------------------- * Log in as a user with accounting rights only (not part of the Companies > Access Rights group) * Go to Accounting > Reporting > Tax Return, open the Mod 115 report * Click the gear icon > BOE > Generate BOE > Observation: An AccessError is raised: "You are not allowed to modify 'Companies' (res.company) ... This operation is allowed for the following groups: Access Rights", even though the user is not trying to edit the company. Why the fix: ------------ The BOE wizard shared by Mod 111/115/303 declares a `company_partner_id` field, related to `company_id.partner_id`, with `readonly=False`. That field is invisible in every view and only exists to compute the domain of `partner_bank_id`; it was never meant to be edited by the user. Because it is declared writable, the ORM attaches an inverse to the related field, so saving the wizard (which happens when generating the BOE, since the field, though invisible, is still part of the view and thus of the saved values) writes `company.partner_id` back onto `res.company`, even though the value never actually changes. That implicit write requires write access on res.company, which is only granted to the Access Rights group, causing the error for regular accountants. Dropping `readonly=False` keeps the field as a plain readonly related field, still usable for the bank account domain, without ever triggering that spurious write. opw-6388869 Forward-Port-Of: odoo/enterprise#130168 Forward-Port-Of: odoo/enterprise#126661
This fixes an issue where certain changed and recombined manufacturing orders could incorrectly produce double the intended quantity or fail during valuation. Manufacturing validation now correctly splits quantities across related finished product lines and values them together, helping avoid inventory and costing errors.
Original PR description
When the quantity of a make-to-order manufacturing order is changed, its finished move is copied to propagate the extra quantity downstream. In some cases (after splitting then merging back…
When the quantity of a make-to-order manufacturing order is changed, its finished move is copied to propagate the extra quantity downstream. In some cases (after splitting then merging back productions) that copy is not merged back, so the order ends up with two finished moves for the same product. On validation, two things then go wrong: - _post_inventory writes the produced quantity on each finished move, so both get the full quantity and the production is doubled - mrp_account._cal_price prices the finished move and calls ensure_one(), which raises "Expected singleton" for an average/fifo product, so "Produce All" fails Split the produced quantity across the finished moves with unit_factor (like _set_qty_producing already does), and price them as a whole instead of expecting a single finished move. Steps to reproduce: - Create a BoM for product A with the MTO route, and a component B - Create a Sale Order for 30 units of A, confirm it - Split the MO into 3 productions of 10, merge two of them - On the third, Update Quantity 10 -> 15, then Produce All - The product should be produced once (15, not 30), with A valued in average/fifo it instead of an error. opw-6242504 opw-6310972 opw-6307025 opw-6354739 Forward-Port-Of: odoo/odoo#286173 Forward-Port-Of: odoo/odoo#269254
This fixes the import of Factur-X e-invoices received through Peppol by correctly reading the embedded XML inside the PDF. It prevents missing or empty invoice records and improves detection of self-billed documents, helping French e-invoicing flows process documents reliably.
Original PR description
When importing new documents from Peppol into the database, we determine whether they are self-billed by checking a Type Code in the XML file. Factur-X is an hybrid format where the XML is embedded inside a PDF. Currently, we are not extracting the XML before searching for that Type Code, and it leads to an error that prevents the document from being imported correctly: - V17, V18: An empty invoice is created and linked with the attachment. - V19+: Only the attachment is created. Additionnaly, we only check for InvoiceTypeCode or CreditNoteTypeCode, but the CII XML format embedded inside the hybrid Factur-X format use TypeCode instead. This PR aims at fixing both these issues. Ticket: opw-6417682 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285552 Forward-Port-Of: odoo/odoo#280714
This fixes an issue where signatures could disappear from downloaded signed PDFs when the original file had unusual page positioning. Signed documents now place fields within the visible page area, so users can trust that completed PDFs match the preview.
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#127711 Forward-Port-Of: odoo/enterprise#121960
Appraisals now keep the generic template chosen by the user when they are confirmed or reset. This prevents records from being silently reassigned to a different template, so reporting and filtering by appraisal template remain accurate.
Original PR description
Issue: When a user selects a generic appraisal template other than the first one, confirming or resetting the appraisal silently replaces that selection with the first generic template. Filtering…
Issue: When a user selects a generic appraisal template other than the first one, confirming or resetting the appraisal silently replaces that selection with the first generic template. Filtering appraisals by the originally selected template then fails to return the appraisal. Steps to reproduce: * Configure multiple appraisal templates without department restrictions. * Create an appraisal and select a template other than the first one. * Confirm the appraisal. * Filter appraisals by the selected template. Cause: `_compute_appraisal_template()` only preserved templates directly linked to the appraisal's department. Generic templates have no department relation, so a valid selected template was discarded whenever the computation was triggered by a state dependent department recomputation. The generic fallback then stored the first template instead. https://github.com/odoo/enterprise/blob/5c57ccbb13269af28de0a6c7f35454be52424f43/hr_appraisal/models/hr_appraisal.py#L195-L209 Solution: We need to distinguish the generic default loaded on an unsaved form from a compatible generic template already stored on an appraisal. Preserve the latter across recomputations while retaining department template priority during creation and rejecting templates that no longer match the appraisal's department or company. opw-6449041 Forward-Port-Of: odoo/enterprise#129635 Forward-Port-Of: odoo/enterprise#127566
This fixes a navigation issue where opening Helpdesk tickets from an email alias could crash because the system reused the wrong background context. Users can now move from an alias to its related Helpdesk team and view tickets normally, improving reliability for support workflows.
Original PR description
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe…
### Steps to Reproduce: 1. In Debug mode, go to Aliases 2. Click on any active Alias, ex. customer-care 3. Click on "Open Parent Document" smart button 4. Click on "Tickets" smart button and observe error ### Description of the issue/feature this PR addresses: **Issue:** When navigating from an email alias to its parent document (e.g., a Helpdesk Team), the web client incorrectly retains the `active_id` and `active_model` of the alias in the context. **Solution:** We updated the `action_view_ticket` method in `helpdesk.team` to explicitly inject the correct `active_model` and `active_id` into the context before calling `_for_xml_id`. This overwrites the polluted Alias data before the window action is evaluated. ### Current behavior before PR: Clicking the "Tickets" smart button passes the old alias context into the action. This bad data flows into the ticket view, which attempts to look up a Helpdesk Team using the Alias's ID to generate the empty list help message, resulting in a `MissingError`. ### Desired behavior after PR: The Python action sanitizes the context at the source, ensuring that the XML action and subsequent view evaluations receive the correct Helpdesk Team ID. Ultimately, the view will load normally without crashing. opw-6395638 Forward-Port-Of: odoo/enterprise#130088 Forward-Port-Of: odoo/enterprise#125232
The Helpdesk SLA Status Analysis report now measures Hours Open from ticket creation until closure, matching the main Ticket Analysis report. This prevents tickets that were assigned immediately from showing no open time and gives managers a more accurate view of service performance.
Original PR description
1. Open Helpdesk > Tickets and create a ticket on the team "Customer Care", assigned to yourself 2. More than an hour later, move it to the "Solved" stage to close it 3. Open Helpdesk > Reporting > Ticket Analysis, switch to the pivot view and pick the "Hours Open" measure -> the ticket holds the hour it stayed open 4. Open Helpdesk > Reporting > SLA Status Analysis and pick the "Hours Open" measure as well -> the ticket holds nothing, as it was assigned as soon as it was created odoo/enterprise#47454 added the "Hours Open" measure of the ticket analysis to the SLA status analysis, but computes it up to the assignment date instead of the closing date. The measure therefore holds the hours until the ticket was assigned, which the report already offers as "Working Hours to Assign". With this commit, both reports count the hours from the creation of the ticket to its closing. Forward-Port-Of: odoo/enterprise#130263 Forward-Port-Of: odoo/enterprise#130170
Fixed an issue in the HTML editor where pressing Enter in a list item containing a table did not split the bullet as expected. This makes editing mixed content in bullet lists more predictable and prevents accidentally moving larger list content out of the list.
Original PR description
### Steps to reproduce: - Insert a bullet list - Inside of the list, insert a table - Write before and/or after the table (in the same list item) - Press enter before and/or after the inserted text - Notice that the bullet is not split like in a normal list ### Root Cause: - On Enter, list plugin checked whether the list item contained unsplittable element. Since the table was inside the list item, it always treated the list item as unsplittable, even when the cursor was outside the table. As a result, the list item could never be split. ### Solution: - Instead of checking the whole list item, walk up from the split target to the list item and look for an unsplittable element along the way. This allows the list item to split normally when the cursor is outside the unsplittable. task-6449843 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286028 Forward-Port-Of: odoo/odoo#280903
This fix prevents already-paid online orders from being reopened as abandoned carts during a short timing window after redirect payments. It helps avoid incorrect order totals and false payment mismatch warnings on the confirmation page, improving checkout reliability for customers using alternate pricelists or currencies.
Original PR description
**Steps to reproduce:** - Install the `website_sale` module. - Configure the Mollie payment provider and publish it. - Enable the PLN currency. - Create a PLN pricelist and make it selectable on the…
**Steps to reproduce:** - Install the `website_sale` module. - Configure the Mollie payment provider and publish it. - Enable the PLN currency. - Create a PLN pricelist and make it selectable on the website. - Open the Mitchell Admin customer record and, under the `Sales & Purchase` tab, assign the USD pricelist. - Go to the website shop, switch the pricelist to PLN, add a product to the cart, and proceed to checkout. - Pay through Mollie and mark the transaction as paid in Mollie. **Issue:** - After a successful redirect payment, the confirmation page displays an amount-mismatch warning, even though the payment was accepted by the provider for the correct amount. **Root cause:** - Mollie's return request and webhook can arrive within milliseconds of each other. Both requests attempt to create a `payment_data` record referencing the same `payment_transaction`. - At the same time, the cron holds a `FOR UPDATE` lock on the transaction while processing the first payload. The `FOR KEY SHARE` lock automatically acquired by PostgreSQL during the foreign-key check of the `payment_data` insertion conflicts with this `FOR UPDATE` lock. This results in a serialization failure and rolls back the post-processing job that would have confirmed the sale order (`draft` → `sale`). - This temporarily leaves the transaction in `done` state while the sale order is still in `draft`. - `sale_reset()` then clears both the session cart key and the selected- pricelist key. When `/shop/confirmation` renders, `request.cart` is resolved through `_get_and_cache_current_cart()`. The abandoned-cart recovery branch finds the still-draft sale order and calls `_update_address()` on it. - Since the selected-pricelist key is no longer present in the session, the customer's default USD pricelist is applied. `_recompute_prices()` then changes the sale order total. However, the payment transaction still contains the original PLN amount, so `_get_status_message()` detects the difference and displays the amount-mismatch warning. **Solution:** - Before calling `_update_address()` and `_verify_cart()` on an abandoned-cart candidate, check the state of its latest transaction. - If the transaction is `pending`, `authorized`, or `done`, discard the candidate and return an empty cart. - This matches the guard already present in the session-cart branch of the same method, keeping both paths consistent. - The serialization failure itself is expected and handled by Odoo's retry mechanism. This fix prevents the resulting side-effect window from allowing an already-paid order to be recovered as an abandoned cart and repriced. opw-6470325 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Italian invoice generation no longer fails when a company does not use email or SDI delivery checks. The attachment selection option is also shown more reliably for Italian companies, helping users complete invoice sending without interruptions.
Original PR description
When generating an invoice with an Italian company not checking mail nor SDI, a traceback is raised This commit also update the condition to show attachment selector on `account.move.send` for Italian companies Task [link](https://www.odoo.com/odoo/project.task/6514516) task-6514516
Translated table of contents navigation now keeps plain, consistent link text even when translated headings are styled. Social and sharing snippets also reuse the same styling rules in translation mode, reducing display inconsistencies and maintenance risk.
Original PR description
### [REF] website, *: prevent style in navbar of translated table of content *: html_builder The snippet "Table of Content" copies the content of the title in its navbar, taking care of removing…
### [REF] website, *: prevent style in navbar of translated table of content *: html_builder The snippet "Table of Content" copies the content of the title in its navbar, taking care of removing style to have plain text in the navbar. In translate, to try to achieve this as well, it uses some special logic with `o_translation_without_style`, a second translation hash,... But, if one of the title has no style in the original language, but a translation adds a style, then the term with style is used in the navbar. This commit uses `o_translate_inline` on the links of the navbar. With this, they make together a single translation term which is always distinct from the terms of the titles. The copy of the translated titles is made with the same plugin as in normal mode (with some adaptation to take into account the translation spans). Steps to reproduce: - Open website builder - Drop the "Table of Content" snippet - Save - Open website builder in translate mode - Select text in a title of the "Table of Content" snippet - Change its style: make it bold - Save - Bug: the term in the navbar is bold as well task-6304267 ### [REF] website: define style for translated social snippet in main css The snippets "Social Media" and "Share" needs additional rules to appear correctly in translate mode (if they have `o_translate_inline` on their links). Those were defined in a separate file for inside the builder. This is error prone because the content of those rules needs to be kept in sync with the general rules. To avoid the duplication, this commit moves those additional rules with the normal ones, so they share their content. task-6304267
This fix corrects how employee occupations are calculated for Belgian holiday attestations. It helps ensure payroll and departure documents reflect accurate occupation information, reducing the risk of incorrect HR payroll records.
Original PR description
This commit fixes the occupation computations which were wrong. Forward-Port-Of: odoo/enterprise#129837
Swiss payroll payment reports no longer fail when an employee is paid through a Revolut bank account. This keeps ISO20022 payment file generation working after recent bank account data model changes.
Original PR description
res.bank and res.partner.bank.bank_id were removed in saas-19.2: the bank's identity now lives directly on the bank account as bank_bic and bank_name. The Revolut detection added in fba51a6a77a still read bank_account.bank_id, raising an AttributeError when generating the ISO20022 payment report for Swiss payslips. Forward-Port-Of: odoo/enterprise#129915 Forward-Port-Of: odoo/enterprise#129156
This fixes an issue where adding delivery charges could cause Odoo to recalculate the unit of measure and then calculate shipping-related sales line amounts incorrectly. The change keeps the intended unit of measure when the delivery line is created, helping prevent wrong prices or totals on sales orders.
Original PR description
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the…
In this PR, https://github.com/odoo/odoo/pull/186250, the `product_uom` field was renamed to `product_uom_id`. However, in the `delivery` module, `product_uom_id` is dropped from the values when the delivery line is created. This causes `product_uom_id` to be recomputed. This commit reintroduces `product_uom_id` in the values to prevent the field from being recomputed. **Description of the issue/feature this PR addresses:** For a strange reason, when a module inherits from `sale.order.line` and adds some computed fields with `precompute=True`. `price_unit`, `price_subtotal`, and `price_total` are computed incorrectly. I have attached a module to demonstrate the issue. https://github.com/user-attachments/assets/ebdd8695-c9d8-477b-b5cf-ba6d8d41e84a Without this change, the test fails, and Odoo incorrectly recomputes the fields, as shown in the video. <img width="1232" height="515" alt="image" src="https://github.com/user-attachments/assets/27370f1f-e7b7-4102-a606-0181b9d1a97a" /> When the ORM computes fields marked as `precompute=True`, in this function `_add_precomputed_values` https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/odoo/orm/models.py#L4836, `price_unit` is 0, but the records get `price_unit` from the product. Therefore, when [_compute_amount](https://github.com/odoo/odoo/blob/0d44f26d9b0fb1c1a5db463cf1f8dd0d3c72ba26/addons/sale/models/sale_order_line.py#L855) is called, the values are computed with an incorrect `price_unit`. <img width="1087" height="940" alt="image" src="https://github.com/user-attachments/assets/92feadf0-7f93-4acc-8f1a-931db0655fcc" /> **Steps to reproduce the issue:** - Install the attached module. [sale_precompute.zip](https://github.com/user-attachments/files/31265993/sale_precompute.zip) - Configure a delivery carrier as free for orders over 1, and set the fixed price to 5, for example. - Create a sales order and add a product with a value greater than 1. - Add the shipping method. The price should be 0. In the sales order line, `price_unit` is 0, but `price_subtotal` and `price_total` are equal to 5 (the product's sale price). For more context, this module is a simple example extracted from the OCA `product_secondary_unit` module, which adds a mixin with these fields: https://github.com/OCA/product-attribute/blob/18.0/product_secondary_unit/models/product_secondary_unit_mixin.py. In the `sale_order_secondary_unit` module, `sale.order.line` inherits from this mixin. You can see the error in this PR: https://github.com/OCA/sale-workflow/pull/4535. https://github.com/OCA/sale-workflow/actions/runs/32259842816/job/96090194021?pr=4535#step:8:509 I understand that this requires a deeper investigation into precompute to solve the underlying issue, but I propose setting `product_uom_id` in the `_prepare_delivery_line_vals` method as a temporary solution while the final solution is being investigated. I understand that this field should not have been removed from `_prepare_delivery_line_vals`; the referenced PR only renamed the field and did not intend to remove the value from this method. @Tecnativa @pedrobaeza @kcv-odoo @Feyensv coudl you please review this? --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284316 Forward-Port-Of: odoo/odoo#283551
The kitchen preparation display now correctly chooses an available point-of-sale configuration when it is shared across multiple setups. This prevents failures when no single configuration is selected and keeps shared kitchen displays working reliably, with minor visual adjustments included.
Original PR description
In this commit: ------------------- - We were passing the pos config id when loading the preparation display, but multiple configs can be configured, and no config is set when using all configs. Instead, the config is now resolved within the service from the loaded configs. task: 6512063 Related PR: https://github.com/odoo/odoo/pull/286169