Daily updates from Odoo
Thursday, July 30, 2026
59 changes · saas-19.4
Enhancements to existing features
TikTok Shop configuration is now found under the new Marketplaces menu. This makes marketplace-related setup easier to find and keeps sales channel settings organized in one place.
Original PR description
- Move tiktok shop configuration to the new 'Marketplaces' menu PR Ref: https://github.com/odoo/enterprise/pull/113307
Chilean electronic documents can include a maximum of four company activities, but the system previously allowed more. This update limits the selection to four, helping prevent rejected electronic invoices and delivery-related documents.
Original PR description
The Chilean XML schema supports maximum of 4 activities (l10n_cl_company_activity_ids), but we allow to add more than that. If this happens, it causes rejections since electronic documents are being sent with more than 4 options selected, and returning rejection errors. Adding constraint to limit l10n_cl_company_activity_ids task-id: 6329320 Forward-Port-Of: odoo/enterprise#126026 Forward-Port-Of: odoo/enterprise#123856
Belgian payroll bicycle reimbursement amounts are updated for the rates effective from October 1, 2026. This helps payroll teams apply the correct tax-exempt allowance of €0.32 per km, capped at €12.80 per day.
Original PR description
This PR updates the Belgian bicycle reimbursement rates to reflect the amounts applicable from October 1, 2026. ### Changes - Increase the bicycle reimbursement rate from the previous amount to €0.32/km. - Increase the maximum daily tax-exempt reimbursement to €12.80/day. These values are aligned with the latest Belgian regulations and are required for payroll calculations from October 1, 2026. Task-6385742 Forward-Port-Of: odoo/enterprise#125862 Forward-Port-Of: odoo/enterprise#124481
Pakistan payroll calculations have been updated to use the 2026 income tax brackets. The previous extra tax surcharge mechanism has been removed, helping payroll teams apply the latest tax rules more accurately.
Original PR description
[IMP] l10n_pk_hr_payroll: update 2026 tax brackets . tax brackets are updated . extra tax surcharge mechanism is deleted task-6401729 Forward-Port-Of: odoo/enterprise#125711 Forward-Port-Of: odoo/enterprise#124988
Give clearer, more accurately attributed error messages, and add validation to catch classification code / General Public TIN mismatches before they reach MyInvois. task-4651934 Forward-Port-Of: odoo/odoo#278805 Forward-Port-Of: odoo/odoo#278208
Original PR description
Give clearer, more accurately attributed error messages, and add validation to catch classification code / General Public TIN mismatches before they reach MyInvois. task-4651934 Forward-Port-Of: odoo/odoo#278805 Forward-Port-Of: odoo/odoo#278208
Version: owl v3.0.0-alpha.45 Release notes: https://github.com/odoo/owl/releases/tag/v3.0.0-alpha.45 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
Original PR description
Version: owl v3.0.0-alpha.45 Release notes: https://github.com/odoo/owl/releases/tag/v3.0.0-alpha.45 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 PR adapts the update dialog to fit the new update logic which is individual to every iot box related PR: https://github.com/odoo/odoo/pull/278661 Forward-Port-Of: odoo/odoo#278914
Original PR description
This PR adapts the update dialog to fit the new update logic which is individual to every iot box related PR: https://github.com/odoo/odoo/pull/278661 Forward-Port-Of: odoo/odoo#278914
1) Previously, we were supposing that only one <cbc:Description> tag could be found on InvoiceLine item. After checking the UBL XSD, I found we could have multiple Description tags for one item. 2) The import order of <cbc:Name> and <cbc:Description> on the invoice line now has been changed to be more accurate and prevent loss of information. The export has been adapted to this change too. Now, we export the actual description written by the user. task-6153895 --- I confirm I have sig
Original PR description
1) Previously, we were supposing that only one <cbc:Description> tag could be found on InvoiceLine item. After checking the UBL XSD, I found we could have multiple Description tags for one item. 2) The import order of <cbc:Name> and <cbc:Description> on the invoice line now has been changed to be more accurate and prevent loss of information. The export has been adapted to this change too. Now, we export the actual description written by the user. task-6153895 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275089 Forward-Port-Of: odoo/odoo#261949
Resolved issues and error corrections
Sendcloud shipping declarations now correctly handle partial quantities, such as 0.5 kg of a product sold by kilogram. This prevents international deliveries from being rejected because declared item weights did not match the parcel weight.
Original PR description
Issue ----- International deliveries for fractions of items (eg 500g of a product sold by kg) are rejected by Sendcloud. Steps to reproduce ----- - Setup Sendcloud - Fedex international - Create a…
Issue ----- International deliveries for fractions of items (eg 500g of a product sold by kg) are rejected by Sendcloud. Steps to reproduce ----- - Setup Sendcloud - Fedex international - Create a product - weight: 1kg - valid hs code - Create a contact (outside EU if the company is in EU) - Deliver 0.5 of the product to the contact > Error "... parcel not returned from Sendcloud" Cause ----- Sendcloud returns he folloing error: > "The total weight for declared items exceeds the total weight set for the shipment." This is because the `weight` set on the shipment https://github.com/odoo/enterprise/blob/99a13453cac8a7a4677cef3df8896329c90c9e99/delivery_sendcloud/models/sendcloud_service.py#L449-L464 corresponds to the weight of the package, whereas the weight set on the description of the product in `parcel_items` corresponds to the weight of one "full" unit of the product. https://github.com/odoo/enterprise/blob/99a13453cac8a7a4677cef3df8896329c90c9e99/delivery_sendcloud/models/sendcloud_service.py#L327-L335 We cannot change the quantity in `parcel_items` to match the actual delivered one because the field should be an integer. https://sendcloud.dev/api/v2/parcels/create-a-parcel-or-parcels#body-one-of-0-parcel-parcel-items-items-quantity The price is also off, because it gets taken from the `move_line`, so it reflects the price of the actual quantity and not a "full" item. https://github.com/odoo/odoo/blob/208a8a6a5adb8ec1f2710453c2ee3b54c55a9f1e/addons/stock_delivery/models/delivery_carrier.py#L235 Note that this issue is common to **all UoM types**. Solution ----- Since we cannot change the quantity, we can instead adapt the description and weight sent in `parcel_items`. For example, sending 300g of sugar, we would send - description: "Sugar (0.3 kg)" - weight: "0.300" ----- Ticket: opw-6346330 Forward-Port-Of: odoo/enterprise#125893 Forward-Port-Of: odoo/enterprise#124686
The French accounting reports test suite was updated to align with recent changes in how FEC export data is prepared. This helps ensure the system continues to validate French compliance exports correctly after related platform changes.
Original PR description
Adjust the FEC export test expectations to match the updated `EcritureLib` fallback logic introduced in the related community change. Related: https://github.com/odoo/odoo/pull/257242 task-5346068 Forward-Port-Of: odoo/enterprise#125684 Forward-Port-Of: odoo/enterprise#112822
Uploading documents could trigger an error after the AI Documents app was uninstalled if auto-sort had previously been enabled on a folder. The fix cleans up the leftover automation rules during uninstall so document uploads continue normally.
Original PR description
Currently, an error occurs when a user uploads a document. **Steps to Reproduce:** - Install the `ai_documents` module. - Go to `Documents` and create a `folder`, or use an `existing one`. - Open the…
Currently, an error occurs when a user uploads a document.
**Steps to Reproduce:**
- Install the `ai_documents` module.
- Go to `Documents` and create a `folder`, or use an `existing one`.
- Open the `folder` > click `Actions` > `Auto-sort`, and `save`.
- Uninstall the `ai_documents` module.
- Go back to `Documents`, open the `same folder`, and `upload any document`.
- Error is logged in the `terminal`.
`ValueError: Invalid field documents.document.ai_sortable in condition ('ai_sortable', '=', True)`
When the ai_documents module is installed and the user enables Auto-sort for a folder [1], an
automation rule and its linked server action are created [2] (if they do not already exist).
Whenever a document is uploaded to that folder, the automation rule triggers the server action,
which runs the AI prompt to classify and sort the document.
However, when the ai_documents module is uninstalled, the related automation rule and server
action are not removed. As a result, uploading a document to the same folder still triggers the
automation rule. While evaluating its domain, it attempts to access the ai_sortable field, which
no longer exists because it is defined by the ai_documents module, raise the error [3].
This commit ensures that uninstalling the ai_documents module removes the related automation
rules. The linked server actions are then deleted automatically through the field's ondelete='cascade' [4].
[1]: https://github.com/odoo/enterprise/blob/c6d3efb164a23d555e30d0f0f7de3de2ef1d08d3/ai_documents/wizard/ai_documents_sort.py#L100
[2]- https://github.com/odoo/enterprise/blob/c6d3efb164a23d555e30d0f0f7de3de2ef1d08d3/ai_documents/models/documents_document.py#L313-L330
[3]- https://github.com/odoo/enterprise/blob/c6d3efb164a23d555e30d0f0f7de3de2ef1d08d3/ai_documents/models/documents_document.py#L321
[4]: https://github.com/odoo/odoo/blob/2cb2f33c871bf83c74098ada568e167aad24f2a5/addons/base_automation/models/ir_actions_server.py#L17
sentry-7607903354
Forward-Port-Of: odoo/enterprise#124488UPS commercial invoices now use the customer's main commercial address as the Sold To address when appropriate, instead of always using the delivery address. If UPS requires the Sold To country to match the delivery country, the system falls back to the delivery address and warns the user, helping avoid failed international shipments.
Original PR description
Issue ----- When making an international delivery to a partner with different invoice and delivery addresses, we send the delivery address as the `Sold To` address as well. Problematic case 1 ----- -…
Issue ----- When making an international delivery to a partner with different invoice and delivery addresses, we send the delivery address as the `Sold To` address as well. Problematic case 1 ----- - Create a belgian company - Setup UPS - Create a French customer - Add a different french delivery address - Create a product (with some weight) - Create a SO (with UPS delivery) to the customer & confirm - Validate the transfer > Commercial invoice `Sold To` uses the delivery address Solution for case 1 ----- Use the delivery address' `commercial_partner_id`. This leads to another issue in some edge cases... Problematic case 2 (caused by case 1 fix) ----- - Create a belgian company - Setup UPS - Create a French customer - Add a delivery address in Switzerland - Create a product (with some weight) - Create a SO (with UPS delivery) to the customer & confirm - Validate the transfer > UPS error `The Sold To party's country code must be the same as the Ship To party's country code with the exception of Canada and satellite countries.` Solution for case 2 ----- Default back to delivery address for the `Sold To` field when countries don't match, as this is a limitation of the UPS API. Warn the user, either on the SO or the transfer itself (if no SO). Warning looks like this (on SO): <img width="1914" height="716" alt="image" src="https://github.com/user-attachments/assets/f7aa73c4-f24c-42da-8f3e-6a58765ef020" /> ----- Ticket: opw-6200263 Forward-Port-Of: odoo/enterprise#123163 Forward-Port-Of: odoo/enterprise#118031
The bank reconciliation report now includes all unreconciled transactions up to the selected date, not just those from the latest bank statement. This helps finance teams see the full set of pending items and avoid missing older transactions during reconciliation.
Original PR description
The reconciliation report lists only the unreconciled transactions from the last statement instead of all of them Steps: - Create 4 statements with one statement line each with different dates - Go to the reconciliation report (via the three dot menu on bank journal kanban card) - select date as Today -> only the line from the last statement is displayed opw-6250370 Forward-Port-Of: odoo/enterprise#124837 Forward-Port-Of: odoo/enterprise#119386
The payslip correction wizard no longer asks users to choose between correcting one or multiple payslips when launched from a specific payslip's Correct button. This prevents accidental bulk corrections and keeps the action focused on the payslip the user opened.
Original PR description
Steps to reproduce: - Validate and pay two payslips for the same employee - Change a payroll field (e.g. wage) on the employee form, flagging both payslips as having wrong data - Open one of the paid payslips and click "Correct" - The wizard shows the single/multi radio selection Hide the radio selection in the button flow, like the other button-flow-specific elements of the wizard view. The wizard then falls back to its default correction_choice 'single', correcting only the opened payslip. task-6391197 Forward-Port-Of: odoo/enterprise#124468
Deleting a quality check in the middle of a manufacturing work order now keeps the remaining checks properly connected. This prevents later checks from disappearing on the shop floor, helping operators continue quality control without missing required steps.
Original PR description
Steps to reproduce the bug: - Create a BOM for product P1 with one work order WO1 - Create 3 quality points linked to WO1 via the `operation_id` field - Confirm a manufacturing order for P1: - 3…
Steps to reproduce the bug:
- Create a BOM for product P1 with one work order WO1
- Create 3 quality points linked to WO1 via the `operation_id` field
- Confirm a manufacturing order for P1:
- 3 quality checks A → B → C are generated
- Open the shop floor for the work order:
- Observe that all 3 quality checks are displayed
- Delete quality check B (the middle one)
- come back to the shop floor for the work order:
- Observe that quality check C is no longer displayed in the shop floor
Problem:
After deleting check B, check C disappeared from the shop floor. Quality checks are stored as a doubly-linked list via the `next_check_id` and `previous_check_id` fields on `quality.check`. The shop floor JS (`mrp_display_record.js`) traverses this list starting from the check with no `previous_check_id`, then follows `next_check_id` until the chain ends. When check B was deleted, it nullified the FK references pointing to it, leaving check A with `next_check_id = False` and check C with `previous_check_id = False`. The traversal from A therefore stopped immediately, and C was never reached.
No `unlink` override existed on `quality.check` to repair the chain before deletion.
Solution:
Added an `unlink` override that, before deleting each check, reconnects its predecessor and successor: if the deleted check has both a previous and a next, `prev.next_check_id` is set to `next` and `next.previous_check_id` is set to `prev`, preserving a valid chain for the remaining checks.
opw-6369298
Forward-Port-Of: odoo/enterprise#124118This fix prevents expense card authorization updates from accidentally switching to the company's default currency when the merchant currency is harder to match. It improves accuracy for employees and finance teams reviewing Stripe expense transactions in foreign currencies.
Original PR description
During updates of the authorization amounts the currency may revert to the company one Specifically, if the merchant currency cannot be found, it defaults to the company currency. We now broaden the search search on currency with the `ilike` operator Task [link](https://www.odoo.com/odoo/project.task/6345203) opw-6345203 Forward-Port-Of: odoo/enterprise#125971 Forward-Port-Of: odoo/enterprise#123772
This fix stabilizes Australian payroll accounting tests by ensuring they use a consistent date. It helps prevent false test failures when payroll reporting rules change over time, supporting smoother maintenance and releases.
Original PR description
The new qualifying earning rule introduced was breaking the tests that were not frozen in the past as it changed over to the new reporting code. runbot-940160 related too [11736](https://github.com/odoo/enterprise/pull/117367#event-26694075694) Forward-Port-Of: odoo/enterprise#122576
Odoo now imports Lazada and Shopee orders with discounts, vouchers, coins, shipping fees, and small rounding differences handled more accurately. This helps sales teams reconcile marketplace orders against the amounts reported by each platform and reduces manual correction work.
Original PR description
Marketplace orders with discount lines could not match the platform total: Odoo accumulated a small rounding residue vs Shopee's total_amount / Lazada's order price. sale_shopee ----------- Changes:…
Marketplace orders with discount lines could not match the platform total: Odoo accumulated a small rounding residue vs Shopee's total_amount / Lazada's order price. sale_shopee ----------- Changes: - Fetch buyer-side escrow amounts via `_fetch_order_income` and pass them through `self.env.context` (`order_income`). - Build item lines from the buyer-paid item price with `discount=0` and a recomputed tax-exclusive `price_unit`. - Distribute order-level discounts (seller/platform vouchers and coins) as dedicated negative lines per product tax group via `_prepare_discount_lines_values`. - Append a shipping line from `buyer_paid_shipping_fee` with fiscal-position mapped taxes. - Reconcile any leftover residue with `_adjust_order_total` using a single tax-free amount-adjustment line. - Register `default_discount_product` and configure it on upgrade (v1.1). sale_lazada ----------- - Port the same reconciliation model as shopee: reconciled line specs, discount=0 with discounted unit from paid_price, shipping line from shipping_fee, order-level "Discount line" distributed at order-level. task-6112062 Forward-Port-Of: odoo/enterprise#125601 Forward-Port-Of: odoo/enterprise#117561
Fixed an issue that could prevent the Payroll dashboard from opening when a payroll structure type did not have a scheduled pay value set. This ensures payroll users can access the dashboard reliably even when some configuration fields are left blank.
Original PR description
If one of the Payroll Structure Types has the Scheduled Pay field unset, opening the Payroll dashboard raises a traceback. Steps to reproduce the error: - Install ``hr_payroll`` module - Go to…
If one of the Payroll Structure Types has the Scheduled Pay field unset, opening the Payroll dashboard raises a traceback. Steps to reproduce the error: - Install ``hr_payroll`` module - Go to Payroll > Configuration > Settings > Set Payroll Closing Date > Save - Go to Payroll > Configuration > Structure Types > Create a new Structure Type > Unset Scheduled Pay - Open Dashboard Traceback: ```py AttributeError: 'bool' object has no attribute 'title' ``` https://github.com/odoo/enterprise/blob/9a3ea83a432f42f62076a50fca6bc771a2de96bf/hr_payroll/models/hr_payroll_warning.py#L413-L419 The dashboard collects the scheduled pay values from all structure types and later calls ``schedule.title()`` to build the labels. When a Structure Type has no Scheduled Pay configured, so ``schedule`` becomes ``False``, leading to the traceback. ``_get_schedule_pay`` method can return False at [1], So, It will generate the traceback from below line also. https://github.com/odoo/enterprise/blob/8a3d87d51a9a3c4df656a328a9179ee43541022d/hr_payroll/models/hr_payroll_warning.py#L401 Solution: Added a fallback value when default scheduled pay is False. [1]: https://github.com/odoo/enterprise/blob/8a3d87d51a9a3c4df656a328a9179ee43541022d/hr_payroll/models/hr_payroll_warning.py#L149-L154 sentry-7583037488 Forward-Port-Of: odoo/enterprise#125836 Forward-Port-Of: odoo/enterprise#122492
Ecuadorian electronic invoices using Special Consumptions (ICE) taxes no longer fail during processing. The fix ensures the correct tax details are included in the electronic invoice XML, helping affected companies validate and send these invoices reliably.
Original PR description
Currently, an error occurs when processing Ecuadorian EDI invoices that use taxes from the `Special Consumptions (ICE)` tax group. **Steps to reproduce:** - Install `l10n_ec_edi` and switch to an EC…
Currently, an error occurs when processing Ecuadorian EDI invoices that
use taxes from the `Special Consumptions (ICE)` tax group.
**Steps to reproduce:**
- Install `l10n_ec_edi` and switch to an EC Company.
- Create a new tax with the `Tax Group` set to `Special Consumptions (ICE)`.
- Create an invoice using this tax.
- Confirm the invoice and click `Process Now`.
**Error:**
```
File "/home/odoo/odoo/enterprise/saas-18.4/l10n_ec_edi/models/account_edi_format.py", line 373, in _l10n_ec_get_base_lines
code_percentage = L10N_EC_VAT_SUBTAXES[tax_data['tax'].tax_group_id.l10n_ec_type]
KeyError: 'ice'
```
**Root Cause:**
At [1], non-VAT tax groups such as `ICE` are explicitly not supported
and are not included in `L10N_EC_VAT_SUBTAXES`.
At [2], the code assumes that every Ecuadorian tax group exists in
`L10N_EC_VAT_SUBTAXES` and directly indexes the mapping using
`tax_group_id.l10n_ec_type`. When an invoice uses an `ICE`
tax, causing an error.
**Fix:**
This commit prevents errors by using the tax's `Code ATS` as the
`codigoPorcentaje` value and the tax's `amount` as the `tarifa` in
the XML when the tax group is not present in `L10N_EC_VAT_SUBTAXES`.
(Confirm with the PO [here], just to fix it from 18.4, that the problem is
that it is not working on the versions where we already introduced the feature.)
[1]:
https://github.com/odoo/enterprise/blob/a388b9298268eead53ffa9d33bff7e1927dae33c/l10n_ec_edi/models/account_move.py#L17-L38
[2]:
https://github.com/odoo/enterprise/blob/a388b9298268eead53ffa9d33bff7e1927dae33c/l10n_ec_edi/models/account_edi_format.py#L372-L377
[here]:
https://www.odoo.com/mail/message/1121493378
opw-6373984
opw-6430589
opw-6423812
Forward-Port-Of: odoo/enterprise#123918The Swedish SIE4 general ledger export now uses the actual configured fiscal year dates instead of assuming a fixed one-year period. This prevents exported accounting files from showing mismatched reporting periods when companies use shortened or extended fiscal years.
Original PR description
## Issue: Exporting the general ledger as SIE4 set the duration of fiscal years to one year from the starting date of the fiscal year. ## Steps to reproduce: - Create a fiscal year A of one month for…
## Issue: Exporting the general ledger as SIE4 set the duration of fiscal years to one year from the starting date of the fiscal year. ## Steps to reproduce: - Create a fiscal year A of one month for year X-1 (December 1st to December31th year X-1) - Create a fiscal year B of 1 year and 1 month (January 1st Year X to January 31th year X+1) - Create Invoices in November year X-1, December year X-1, year X and in January year X+1 and confirm them - Go to General Leder - Set date to the fiscal year B - Export as SIE4 ### Current behavior: - **for previous fiscal year** - declared fiscal year (#RAR field) goes : - from fiscal year X date_from -1 year - to fiscal year X date_to -1 year - However data are computed: - from fiscal year X date_from -1 year - to fiscal year X date_from -1 day - **for current fiscal year** - declared fiscal year (#RAR field) goes : - from fiscal year X date_from - to fiscal year X date_to - However data are computed: - from fiscal year X date_from - to fiscal year X date_from +1 year ### Expected behavior: Declared fiscal year match the one that is use for computation. - for previous fiscal year - from fiscal year X-1 date_from - to fiscal year X date_from -1 day - for current year - from fiscal year X date_from - to fiscal year X date_to Cause: Current year length was wrong because it [relied on](https://github.com/odoo/enterprise/blob/22b4100006ec24bf6e4b64042cbfdba360fcf470/l10n_se_sie4_export/models/account_general_ledger.py#L139-L140) the next_date_from which was wrong. opw-6264766 Forward-Port-Of: odoo/enterprise#125859 Forward-Port-Of: odoo/enterprise#125354
This fix prevents an error when users propose adding a step from the Shop Floor for manufacturing orders whose bill of materials has very similar operations. It ensures Product Lifecycle Management improvement suggestions can proceed reliably in this scenario.
Original PR description
[FIX] mrp_workorder_plm: avoid singleton traceback when matching BOM operations **Issue** Adding a step in a manufacturing order with two operations that are too similar could lead to a traceback…
[FIX] mrp_workorder_plm: avoid singleton traceback when matching BOM operations **Issue** Adding a step in a manufacturing order with two operations that are too similar could lead to a traceback when proposing an improvement from the Shop Floor. **Steps to reproduce** - Install the Product Lifecycle Management app. - Create a BOM for any product with two operations that: - Have the same name and work center - Have no variant - Create and confirm a Manufacturing Order for that product. - Open the Shop Floor view. - Click the gear icon -> Update Instructions -> Improvement Suggestion -> Add a Step -> Propose a Change. -> A traceback occurs **Cause** When adding a step: https://github.com/odoo/enterprise/blob/1c02615b73a2b4fb9f1ab91167a3c8d7c9ac824c/mrp_workorder_plm/models/mrp_workorder.py#L10 It tries to find the corresponding operation in the ECO's new BoM. This relies on `_get_sync_values()`: https://github.com/odoo/enterprise/blob/1c02615b73a2b4fb9f1ab91167a3c8d7c9ac824c/mrp_plm/models/mrp_routing.py#L9-L13 Because two operations share the same name, work center, and no variant, both match the filter: https://github.com/odoo/enterprise/blob/1c02615b73a2b4fb9f1ab91167a3c8d7c9ac824c/mrp_workorder_plm/models/mrp_workorder.py#L39 This results in a singleton error when accessing `operation.id`: https://github.com/odoo/enterprise/blob/1c02615b73a2b4fb9f1ab91167a3c8d7c9ac824c/mrp_workorder_plm/models/mrp_workorder.py#L42 opw-6241880 Forward-Port-Of: odoo/enterprise#123673 Forward-Port-Of: odoo/enterprise#119404
This fix prevents an error when an Australian employee's Tax Treatment Category is removed. Payroll records now handle the missing value safely and recalculate the tax treatment code once the category is set again.
Original PR description
Currently, an error occurs when a user removes the Tax Treatment Category of an Australian employee. Steps to Reproduce: - Install the `l10n_au_hr_payroll` module with demo data. - Switch to an…
Currently, an error occurs when a user removes the Tax Treatment Category of an Australian employee. Steps to Reproduce: - Install the `l10n_au_hr_payroll` module with demo data. - Switch to an `Australian company`. - Open any `Employee` > `Payroll` > remove the `Tax Treatment Category` value. `UnboundLocalError: cannot access local variable 'code' where it is not associated with a value` After the [change] in selection field behavior, users can clear the value of the field. When the user removes the Tax Treatment Category value, the system computes the tax treatment code [1]. During this process, if no condition matches, the code variable is not initialized. Converting this uninitialized variable to a string [2] raises an error. This commit ensures that when the tax treatment category is not set, the tax treatment code is set to False with an early return. Since the tax treatment category is required field and compute the correct tax treatment code, once the category is set. [change]: https://github.com/odoo/odoo/pull/214422/changes/8d2a42ac419fdf7943a0c11beb8c5de6c6f85bef [1]- https://github.com/odoo/enterprise/blob/017743cbe97b629e9d9f1895b655014d4c3abbcb/l10n_au_hr_payroll/models/hr_version.py#L450-L451 [2]- https://github.com/odoo/enterprise/blob/017743cbe97b629e9d9f1895b655014d4c3abbcb/l10n_au_hr_payroll/models/hr_version.py#L515 No task ID Forward-Port-Of: odoo/enterprise#124067
New contacts that are assigned a Partner Level before saving will now automatically receive a barcode. This prevents missing barcodes and ensures front desk partnership processes work correctly from the moment the contact is created.
Original PR description
Steps to reproduce: - Go to Contacts. - Create a new contact and assign a Partner Level before saving. - Save the record. Current behavior: - When creating a new contact with a Partner Level, the barcode is not generated automatically. Solution: - Add barcode generation logic to the create() method so that a barcode is automatically generated when a new contact is created with a Partner Level. TaskId-6236375
Restaurant appointment scheduling no longer crashes when a table has no linked resource. This keeps the Gantt schedule view usable even when table setup is incomplete or missing resource details.
Original PR description
When a table doesn't have a resource, the appointment_resource_id is undefined and the gantt renderer was crashing when trying to access its id. This commit adds a check to ensure that the appointment_resource_id exists before trying to access its id. Forward-Port-Of: odoo/enterprise#125331
When the same delivery order is processed in multiple POS browser sessions, the system now shows a proper user-facing message instead of a technical error. This reduces confusion for restaurant staff and makes the message available for translation.
Original PR description
`* = pos_platform_order, pos_urban_piper` ## Steps to Reproduce: - Install POS Restaurant. - Configure Urban Piper. - Enable "Auto Acknowledge Orders" for a platform(e.g; Zomato) to automatically print delivery orders. - Configure a printer for the POS shop. - Open the same POS session in two different browsers. - Place a test order from Atlas (UrbanPiper). ## Error: `ValueError - This delivery order has already been printed automatically.` ## Cause: When the same delivery order is processed concurrently for both sessions, the backend raises an error when it attempts to print the order again. ## Fix: Replace ValueError with UserError and mark the error message as translatable. sentry-7609743093
Creating a Google Reserve Merchant record no longer fails when the Website app is not installed. This prevents an error during Google Booking configuration and lets users complete merchant setup more reliably.
Original PR description
Currently, an error occurs when a user creates a Google Reserve Merchant record. **Steps to Reproduce:** - Install the `appointment_google_reserve` module. - Go to `Appointments` > `Configuration` >…
Currently, an error occurs when a user creates a Google Reserve Merchant record. **Steps to Reproduce:** - Install the `appointment_google_reserve` module. - Go to `Appointments` > `Configuration` > `Google Booking`. - Click `New` to create a record. `AttributeError: 'website' object has no attribute 'homepage_url'` After this [recent commit], as part of the context-based website resolution refactoring, the `default_website` record is now available even without the `website` module because it is created in `base` [1]. When a user creates a Google Reserve Merchant record, it attempts to set the default URL using the default website from `base`. However, it then tries to access the `homepage_url` field, which is defined in the `website` module [2]. Since the `website` module is not installed, this raises the error [3]. This commit ensures that before accessing `homepage_url`, it first checks whether the `homepage_url` field exists when creating the merchant record, since this field depends on the `website` module. [recent commit]: https://github.com/odoo/odoo/commit/ae81b6f6074632d1a609387e53f97a61ee6c95f4 [1]: https://github.com/odoo/odoo/blob/ea0e2750a43e337bc6d9a00489a30df3fc0616c7/odoo/addons/base/data/website.xml#L5-L9 [2]: https://github.com/odoo/odoo/blob/ea0e2750a43e337bc6d9a00489a30df3fc0616c7/addons/website/models/website.py#L168 [3]- https://github.com/odoo/enterprise/blob/01b0cf4e2f2e2c63c02c3429d029c90639de1322/appointment_google_reserve/models/google_reserve_merchant.py#L21-L22 Task-6395376 sentry-7630852653
Brazilian fiscal reform invoices now include the required commerce tax unit conversion factor when sent to Avalara. This helps ensure invoice tax calculations use the correct quantity conversion and reduces the risk of validation or tax reporting issues.
Original PR description
This commit adds the comexTaxUnitFactor to the json sent to Avalara when sending an invoice. comexTaxUnitFactor is a factor that convert sales quantity to comexTaxUnit, its value should be the same as cbsIbsUnitFactor. opw-6396462 Forward-Port-Of: odoo/enterprise#125695
This fix prevents blank paragraphs in Studio report layouts from being automatically removed after users delete their last character. It helps preserve report formatting and reduces accidental layout changes while editing.
Original PR description
Problem: In Studio reports, deleting the last character of a paragraph removes the entire paragraph. Cause: `cleanEmptyStructuralContainers` removes the empty paragraph because it is considered empty. Solution: Disable `cleanEmptyStructuralContainers` for reports same as website builder. Steps to reproduce: - Create a new report. - Add multiple paragraphs. - Leave one paragraph with a single character. - Delete the character. - Observe that the paragraph is removed. task-6368965 Forward-Port-Of: odoo/enterprise#126150 Forward-Port-Of: odoo/enterprise#124834
Issue caused because of https://github.com/odoo/odoo/commit/619d8ce9f31349ed06b11ead4988a4a1d9ca6bdd Steps to reproduce- Create db with demo and install l10n_in Create a new company for India (other than IN Company) Go to Settings -> Accounting/Invoice Tab -> Enable Ewaybill traceback ```py Traceback (most recent call last): File "/home/odoo/odoo18/community/odoo/orm/models.py", line 5385, in ensure_one _id, = self._ids ^^^^ ValueError: not enough values to unpack (expected
Original PR description
Issue caused because of https://github.com/odoo/odoo/commit/619d8ce9f31349ed06b11ead4988a4a1d9ca6bdd Steps to reproduce- Create db with demo and install l10n_in Create a new company for India (other…
Issue caused because of https://github.com/odoo/odoo/commit/619d8ce9f31349ed06b11ead4988a4a1d9ca6bdd Steps to reproduce-
Create db with demo and install l10n_in
Create a new company for India (other than IN Company) Go to Settings -> Accounting/Invoice Tab -> Enable Ewaybill
traceback
```py
Traceback (most recent call last):
File "/home/odoo/odoo18/community/odoo/orm/models.py", line 5385, in ensure_one
_id, = self._ids
^^^^
ValueError: not enough values to unpack (expected 1, got 0)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/odoo/odoo18/community/odoo/http/router.py", line 273, in __call__
response = serve_db(request)
^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/http/router.py", line 380, in serve_db
registry = Registry(request.db)
^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/orm/registry.py", line 105, in __new__
return cls.new(db_name)
^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/tools/func.py", line 67, in locked
return func(inst, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/orm/registry.py", line 217, in new
cr.rollback()
File "/home/odoo/odoo18/community/odoo/sql_db.py", line 568, in rollback
with rollbacking:
File "/home/odoo/.pyenv/versions/3.12.0/lib/python3.12/contextlib.py", line 144, in __exit__
next(self.gen)
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 1004, in rollbacking
self.restore_state()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 1067, in restore_state
self.reset()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 912, in reset
self._reset_registry_change()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 832, in _reset_registry_change
registry._setup_models__(cr)
File "/home/odoo/odoo18/community/odoo/tools/func.py", line 67, in locked
return func(inst, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/orm/registry.py", line 422, in _setup_models__
env.invalidate_all()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 395, in invalidate_all
self.flush_all()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 413, in flush_all
self._recompute_all()
File "/home/odoo/odoo18/community/odoo/orm/environments.py", line 406, in _recompute_all
self[field.model_name]._recompute_field(field)
File "/home/odoo/odoo18/community/odoo/orm/models.py", line 6462, in _recompute_field
field.recompute(records)
File "/home/odoo/odoo18/community/odoo/orm/fields.py", line 2042, in recompute
apply_except_missing(self.compute_value, recs)
File "/home/odoo/odoo18/community/odoo/orm/fields.py", line 2012, in apply_except_missing
func(records)
File "/home/odoo/odoo18/community/odoo/orm/fields.py", line 2066, in compute_value
records._compute_field_value(self)
File "/home/odoo/odoo18/community/addons/mail/models/mail_thread.py", line 498, in _compute_field_value
return super()._compute_field_value(field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/odoo/orm/models.py", line 4387, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/odoo18/community/odoo/orm/fields.py", line 82, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 228, in _compute_document_partners_details
seller_buyer_details = ewaybill._get_seller_buyer_details()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in_ewaybill/models/l10n_in_ewaybill.py", line 185, in _get_seller_buyer_details
return move._get_l10n_in_seller_buyer_party()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo18/community/addons/l10n_in/models/account_invoice.py", line 629, in _get_l10n_in_seller_buyer_party
self.ensure_one()
File "/home/odoo/odoo18/community/odoo/orm/models.py", line 5388, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: account.move()
```
In this commit, we resolve the above traceback
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276216To improve compliance with French requirements for FEC exports. Now we avoid exporting empty or placeholder labels ('/') by improving the fallback logic for EcritureLib. So now we, - Use existing line label when valid - For receivable/payable lines, fallback to 'partner - reference' - Otherwise fallback to move reference or name - Replace '/' with 'Balance initiale' for opening entries Related: https://github.com/odoo/enterprise/pull/112822 task-5346068 Forward-Port-Of: odoo/odoo#2786
Original PR description
To improve compliance with French requirements for FEC exports. Now we avoid exporting empty or placeholder labels ('/') by improving the fallback logic for EcritureLib. So now we,
- Use existing line label when valid
- For receivable/payable lines, fallback to 'partner - reference'
- Otherwise fallback to move reference or name
- Replace '/' with 'Balance initiale' for opening entries
Related: https://github.com/odoo/enterprise/pull/112822
task-5346068
Forward-Port-Of: odoo/odoo#278644
Forward-Port-Of: odoo/odoo#257242Description of the issue/feature this PR addresses: Updates `test_auth_ldap` to retrieve the company associated with the current environment instead of relying on a hardcoded value. This prevents test failures in environments where the hardcoded company does not exist. Resolves https://runbot.odoo.com/odoo/error/243763 opw-6353706 Forward-Port-Of: odoo/odoo#275642
Original PR description
Description of the issue/feature this PR addresses: Updates `test_auth_ldap` to retrieve the company associated with the current environment instead of relying on a hardcoded value. This prevents test failures in environments where the hardcoded company does not exist. Resolves https://runbot.odoo.com/odoo/error/243763 opw-6353706 Forward-Port-Of: odoo/odoo#275642
In the current state, running `test_qris_link_with_pos_order` results in an access error. This is because the user is missing the PoS group user access group. This PR changes the test class inheritance from `AccountTestInvoicingHttpCommon` to `TestPointOfSaleHttpCommon`. This provides the necessary PoS setup. I also had to change mentions of `pos_user`, as the qris test class created its own `pos_user` distinct from its ancestor's. Error page: https://runbot.odoo.com/odoo/error/938918 Fo
Original PR description
In the current state, running `test_qris_link_with_pos_order` results in an access error. This is because the user is missing the PoS group user access group. This PR changes the test class inheritance from `AccountTestInvoicingHttpCommon` to `TestPointOfSaleHttpCommon`. This provides the necessary PoS setup. I also had to change mentions of `pos_user`, as the qris test class created its own `pos_user` distinct from its ancestor's. Error page: https://runbot.odoo.com/odoo/error/938918 Forward-Port-Of: odoo/odoo#276502
Issue ----- International deliveries for fractions of items (eg 500g of a product sold by kg) are rejected by Sendcloud. Steps to reproduce ----- - Setup Sendcloud - Fedex international - Create a product - weight: 1kg - valid hs code - Create a contact (outside EU if the company is in EU) - Deliver 0.5 of the product to the contact > Error "... parcel not returned from Sendcloud" Cause ----- Sendcloud returns he folloing error: > "The total weight for declared items e
Original PR description
Issue ----- International deliveries for fractions of items (eg 500g of a product sold by kg) are rejected by Sendcloud. Steps to reproduce ----- - Setup Sendcloud - Fedex international - Create a…
Issue ----- International deliveries for fractions of items (eg 500g of a product sold by kg) are rejected by Sendcloud. Steps to reproduce ----- - Setup Sendcloud - Fedex international - Create a product - weight: 1kg - valid hs code - Create a contact (outside EU if the company is in EU) - Deliver 0.5 of the product to the contact > Error "... parcel not returned from Sendcloud" Cause ----- Sendcloud returns he folloing error: > "The total weight for declared items exceeds the total weight set for the shipment." This is because the `weight` set on the shipment https://github.com/odoo/enterprise/blob/99a13453cac8a7a4677cef3df8896329c90c9e99/delivery_sendcloud/models/sendcloud_service.py#L449-L464 corresponds to the weight of the package, whereas the weight set on the description of the product in `parcel_items` corresponds to the weight of one "full" unit of the product. https://github.com/odoo/enterprise/blob/99a13453cac8a7a4677cef3df8896329c90c9e99/delivery_sendcloud/models/sendcloud_service.py#L327-L335 We cannot change the quantity in `parcel_items` to match the actual delivered one because the field should be an integer. https://sendcloud.dev/api/v2/parcels/create-a-parcel-or-parcels#body-one-of-0-parcel-parcel-items-items-quantity The price is also off, because it gets taken from the `move_line`, so it reflects the price of the actual quantity and not a "full" item. https://github.com/odoo/odoo/blob/208a8a6a5adb8ec1f2710453c2ee3b54c55a9f1e/addons/stock_delivery/models/delivery_carrier.py#L235 Note that this issue is common to **all UoM types**. Solution ----- Since we cannot change the quantity, we can instead adapt the description and weight sent in `parcel_items`. For example, sending 300g of sugar, we would send - description: "Sugar (0.3 kg)" - weight: "0.300" ----- Ticket: opw-6346330 Forward-Port-Of: odoo/odoo#277440
When `cash_rounding` is enabled on a POS config but `rounding_method` is not set,`get_tax_totals_summary` is called with undefined (instead of null). Fix: ensure the `rounding_method` is set when `cash_rounding` is enabled, otherwise pass null to `get_tax_totals_summary`. task-id: 6388234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279122 Forward-Port-Of: odoo/odoo#276914
Original PR description
When `cash_rounding` is enabled on a POS config but `rounding_method` is not set,`get_tax_totals_summary` is called with undefined (instead of null). Fix: ensure the `rounding_method` is set when `cash_rounding` is enabled, otherwise pass null to `get_tax_totals_summary`. task-id: 6388234 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279122 Forward-Port-Of: odoo/odoo#276914
Issue: If a product has a pricelist that updates on quantity and in quotation preview the customer changes the quantity the unit price doesn't update. Steps to reproduce: Create a product with price list that change depending on quantity. In a new quotation create a section and set it to optional. Add your product under this new section, and enter preview. Notice that if you change the quantity so that a different pricelist would apply the unit price still does not change. Cause: The va
Original PR description
Issue: If a product has a pricelist that updates on quantity and in quotation preview the customer changes the quantity the unit price doesn't update. Steps to reproduce: Create a product with price list that change depending on quantity. In a new quotation create a section and set it to optional. Add your product under this new section, and enter preview. Notice that if you change the quantity so that a different pricelist would apply the unit price still does not change. Cause: The value for unit price was not getting updated. Fix: After changing quantities update the price unit if there are active pricelists. As not all customers might want this change, it also checks for `sale.disable_sale_update`, so that the user can choose whether to update or not. opw-6377679 Forward-Port-Of: odoo/odoo#278802 Forward-Port-Of: odoo/odoo#275868
Since a cron can be manually or automatically disabled, it is interesting to track when and by who a cron a disabled or re-enabled. So we just add tracking on the active field. 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#279037
Original PR description
Since a cron can be manually or automatically disabled, it is interesting to track when and by who a cron a disabled or re-enabled. So we just add tracking on the active field. 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#279037
**Steps to reproduce:** Both problem are reproducible on runbot aswell but for more clarity (and for empty accounting), those steps are on a fresh db with no demo data Problem 1 : - create a new db with stock_account, purchase and accountant - In the companies view, select your company - in the branch tab, create a branch for your company - for both the branch and the company, in the settings set the valuation as periodic daily With only the branch company selected: - create
Original PR description
**Steps to reproduce:** Both problem are reproducible on runbot aswell but for more clarity (and for empty accounting), those steps are on a fresh db with no demo data Problem 1 : - create a new db…
**Steps to reproduce:** Both problem are reproducible on runbot aswell but for more clarity (and for empty accounting), those steps are on a fresh db with no demo data Problem 1 : - create a new db with stock_account, purchase and accountant - In the companies view, select your company - in the branch tab, create a branch for your company - for both the branch and the company, in the settings set the valuation as periodic daily With only the branch company selected: - create a warehouse for your branch - create a storable prod with a cost of 10 - validate a receipt for 1 unit of the prod - open inventory valuation view and check that there is variation lines for 10 - open 'scheduled actions' view - select 'inventory valuation closing' - click on 'run manually' With the main company selected: - Open journal items - click on the journal entry of any of the move line with label 'closing stock variation global for company [branch]' - select the 'other info' tab Problem 2: - create a new db with stock_account, purchase and accountant - create a company 2 - for both companies, in the settings set the valuation as periodic daily With company 2 selected - create a warehouse for company 2 - in the settings for fiscal localization set the 'generic chart of account' - create a storable product with a cost of 10 - validate a receipt for 1 quantity of the product - open inventory valuation view and check that there is variation lines for 10 - open 'scheduled actions' view - select 'inventory valuation closing' - click on 'run manually' **Current behavior:** Problem 1: the company of the account move is the main company Problem 2: There is a traceback including 'UserError: Everything is correctly closed' **Expected behavior:** Problem 1: It should be the branch company Problem 2: Everything is closed in company 1, but it shouldn't prevent to generate the entries for company 2 **Cause of the issue:** Problem 1: Inside _cron_post_stock_valuation we call action_close_stock_valuation for each company (if periodic daily or periodic monthly and we're the last day of the month) https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/stock_account/models/res_company.py#L143-L144 Inside action_close_stock_valuation when creating the account move we add a context to be sure that the move is created for the main company selected https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/stock_account/models/res_company.py#L72 The fix comes from this PR https://github.com/odoo/odoo/pull/263828 and was improved starting from 19.1 to simply add a the company_id on the moves_vals. But the problem is that this fix failed to consider the case where we come from cron because in this case self.env.company is the main company of the user, which is a problem because as we iterate through the companies we want each account move to be created for its own company. This other PR https://github.com/odoo/odoo/pull/269152 corrects this by using self.id instead of self.company.id but only starting from 19.1. The fix is essentially a back port of those 2 PR. Problem 2: When you call action_close_stock_valuation from _cron_post_stock_valuation for the company that has no inventory valuation and thus no account move to create we will raise the user error https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/stock_account/models/res_company.py#L58-L60 This makes sense if the method was called from the inventory valuation view. But in our case it's called from cron so we might also call it on other companies and therefore we don't want to raise an exception if there is no account move to create on one of the companies. opw-6144294 Forward-Port-Of: odoo/odoo#277969 Forward-Port-Of: odoo/odoo#275294
Unlocking a validated MO to add a new component move should, naturally, bring about a validated move. Although there exists a `state` check in `stock.move`'s `create()` as of odoo/odoo#196161, it only checks for `picking_id`, whereas a component move has a `raw_material_production_id` (and a finished (by)product has a `production_id`), so we replicate the check here. Task ID: [6226710](https://www.odoo.com/odoo/project/966/tasks/6226710) Forward-Port-Of: odoo/odoo#278995 Forward-Port-Of: odo
Original PR description
Unlocking a validated MO to add a new component move should, naturally, bring about a validated move. Although there exists a `state` check in `stock.move`'s `create()` as of odoo/odoo#196161, it only checks for `picking_id`, whereas a component move has a `raw_material_production_id` (and a finished (by)product has a `production_id`), so we replicate the check here. Task ID: [6226710](https://www.odoo.com/odoo/project/966/tasks/6226710) Forward-Port-Of: odoo/odoo#278995 Forward-Port-Of: odoo/odoo#267186
**Current behavior before PR:** Steps to reproduce the issue: - Go to a To-Do. - Insert a heading, write something. - Insert a table of content. - Make sure that editable is not scrollable. Clicking on heading label inside toc leads to traceback. The issue occurs after merging commit [1]. When editable is not scrollable then `closestScrollableY` returns null which leads to traceback. **Desired behavior after PR:** If editable is not scrollable then clicking on heading inside to
Original PR description
**Current behavior before PR:** Steps to reproduce the issue: - Go to a To-Do. - Insert a heading, write something. - Insert a table of content. - Make sure that editable is not scrollable. Clicking on heading label inside toc leads to traceback. The issue occurs after merging commit [1]. When editable is not scrollable then `closestScrollableY` returns null which leads to traceback. **Desired behavior after PR:** If editable is not scrollable then clicking on heading inside toc should hightlight the corresponding heading without traceback. [1]: https://github.com/odoo/odoo/commit/f5cf8565e7d09edd3a29fd95537381fb70d75785 task-6405894 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277657
**Issue** A traceback is raised when creating a batch if the batch sequence prefix does not contain the expected '/' separator. **Steps to reproduce** - Go to settings > technical > sequences & identifiers > sequence: - batch transfer: - prefix: BATCH- - Create a new batch: - operation type: delivery orders - Try to save -> a traceback is triggered: ```ValueError: not enough values to unpack (expected 2, got 1)``` **Cause** `_prepare_name()` assumes that the sequence
Original PR description
**Issue**
A traceback is raised when creating a batch if the batch sequence prefix does not contain the expected '/' separator.
**Steps to reproduce**
- Go to settings > technical > sequences & identifiers > sequence:
- batch transfer: - prefix: BATCH-
- Create a new batch:
- operation type: delivery orders
- Try to save -> a traceback is triggered:
```ValueError: not enough values to unpack (expected 2, got 1)```
**Cause**
`_prepare_name()` assumes that the sequence returned by
`next_by_code()` contains a '/' separator and directly unpacks the
result of `rsplit('/', 1)`.
When no '/' is present, `rsplit()` returns a list containing a single
element, causing the unpacking to fail.
https://github.com/odoo/odoo/blob/18407651d2912b7d30463ec72cd379177530ef4f/addons/stock_picking_batch/models/stock_picking_batch.py#L418-L419
opw-6304270
Forward-Port-Of: odoo/odoo#277281
Forward-Port-Of: odoo/odoo#272212Description of the issue this commit addresses: When a member of the Invoicing group tries to create a payment trough the L10nPlAccountPaymentRegister wizard, upon clicking "Create Payment", an AccessError is thrown. Invoicing group members should be able to handle payments so this is an issue. --- Steps to reproduce: 1. Make sure sale_management and l10n_pl_bank_verification are installed. 2. Create a new user with "Invoicing" Accounting group. 3. Create a new sales order with sai
Original PR description
Description of the issue this commit addresses: When a member of the Invoicing group tries to create a payment trough the L10nPlAccountPaymentRegister wizard, upon clicking "Create Payment", an…
Description of the issue this commit addresses: When a member of the Invoicing group tries to create a payment trough the L10nPlAccountPaymentRegister wizard, upon clicking "Create Payment", an AccessError is thrown. Invoicing group members should be able to handle payments so this is an issue. --- Steps to reproduce: 1. Make sure sale_management and l10n_pl_bank_verification are installed. 2. Create a new user with "Invoicing" Accounting group. 3. Create a new sales order with said user (any customer, any product) 4. Confirm the quotations and, from its form view, "Create Invoice". 5. Confirm the invoice and, from its form view, "Pay". 6. Upon clicking "Create Payment", an Access Error is thrown. --- Desired behavior after this commit is merged: This commit makes sure an Invoicing group member is able to create the payment withtout AccessErrors being thrown. --- task-none feedback from: https://github.com/odoo/odoo/pull/267992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279033 Forward-Port-Of: odoo/odoo#270008
Windows nightly builds have been failing for several days with this obscure NSIS error message in Odoo 19.0: Internal compiler error #12345: error mmapping datablock to 30283637 Root cause: the accumulated size of `.po` files across all modules has grown past a threshold where the NSIS solid compressor tries to mmap a buffer larger than the ~2 GiB address space available to the 32-bit makensis running under Wine. Simply dropping the `/SOLID` option makes the build pass but nearly doubl
Original PR description
Windows nightly builds have been failing for several days with this obscure NSIS error message in Odoo 19.0: Internal compiler error #12345: error mmapping datablock to 30283637 Root cause: the…
Windows nightly builds have been failing for several days with this obscure NSIS error message in Odoo 19.0:
Internal compiler error #12345: error mmapping datablock to 30283637
Root cause: the accumulated size of `.po` files across all modules has grown past a threshold where the NSIS solid compressor tries to mmap a buffer larger than the ~2 GiB address space available to the 32-bit makensis running under Wine. Simply dropping the `/SOLID` option makes the build pass but nearly doubles the size of the final installer, which is not acceptable.
The chosen fix is to pre-bundle all `.po` files into a single solid 7z archive and extract it at install time using the `Nsis7z` plugin. This keeps the NSIS datablock well below the and yields comparable or better final installer size than the previous approach, along with faster build times.
While at it, this commit also modernizes the Windows build environment to unblock a separate wine-devel install regression that has been affecting Odoo 17.0 nightlies on Debian Bookworm.
Changes:
- Bundle `.po` files into `i18n_bundle.7z` inside the build container prior to invoking makensis; extract it at install time via the `Nsis7z` plugin.
- Bump the base image from Debian Bookworm to Trixie.
- Switch from `wine-devel` to `wine-stable`, which resolves the install regression on Bookworm-based builds.
- Upgrade NSIS to the latest release.
- Refactor the NSIS installation step to remove the hardcoded version from `package.py`.
This fix is made in Odoo 17.0 to unblock the wine-devel issue there and to benefit from the smaller installer size on supported stable branches.
Forward-Port-Of: odoo/odoo#278681
Forward-Port-Of: odoo/odoo#278378Currently, the property value is not displayed on the Kanban card, even when the `Display in Cards` option is enabled. This PR ensures that if `Display in Cards` option is enabled for a property, its value is displayed on the corresponding equipment Kanban card. **Steps to reproduce:** - Install the Maintenance module. - Open `Equipment`. - Open an existing equipment record or create a new one and save it. - Click the actions (gear) menu from the equipment form view. - Select `Add Prop
Original PR description
Currently, the property value is not displayed on the Kanban card, even when the `Display in Cards` option is enabled. This PR ensures that if `Display in Cards` option is enabled for a property, its value is displayed on the corresponding equipment Kanban card. **Steps to reproduce:** - Install the Maintenance module. - Open `Equipment`. - Open an existing equipment record or create a new one and save it. - Click the actions (gear) menu from the equipment form view. - Select `Add Properties`. - Add a property and enable the `Display in Cards` option. Open the Kanban view. **Expected behavior:** The property value should be displayed on the Kanban card when the `Display in Cards` option is enabled. Issue: [#277479](https://github.com/odoo/odoo/issues/277479) Forward-Port-Of: odoo/odoo#278511 Forward-Port-Of: odoo/odoo#277757
**Steps to reproduce:** * Install the **Accounting** module. * Open a **Bank Journal** and go to the **Inbound Payment Methods** tab. * Add the **Manual** payment method multiple times and give each payment method line a different name. * Go to **Customers → Payments** and create a payment using any of these payment method lines. * Print the **Payment Receipt** from the gear menu. **Observed behavior:** * The receipt always displays the **Manual** payment method name. * The custom name
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Open a **Bank Journal** and go to the **Inbound Payment Methods** tab. * Add the **Manual** payment method multiple times and give each…
**Steps to reproduce:** * Install the **Accounting** module. * Open a **Bank Journal** and go to the **Inbound Payment Methods** tab. * Add the **Manual** payment method multiple times and give each payment method line a different name. * Go to **Customers → Payments** and create a payment using any of these payment method lines. * Print the **Payment Receipt** from the gear menu. **Observed behavior:** * The receipt always displays the **Manual** payment method name. * The custom name configured on the selected payment method line is ignored. **Cause:** * The report retrieves the payment method name from `account.payment.method`. * However, the displayed name is configured on `account.payment.method.line`, so the report always shows the generic payment method name. **Fix:** * Read the payment method name from the selected `account.payment.method.line`. * The payment receipt now displays the configured payment method line name. opw-6395829 Forward-Port-Of: odoo/odoo#277104
**Steps to reproduce:** 1. Enable debug mode and add a snippet. 2. Add a YouTube video with options like autoplay enabled. 3. Double click the video or use "Replace Media". 4. A traceback occurs while reopening the video options. **Root Cause:** When the media dialog is reopened on an existing video, `syncOptionsWithUrl()` derives each switch option's value from the URL: `value = this.state.urlInput.includes(urlParameter)`, which stores Boolean `true` when e.g. autoplay is enabled.
Original PR description
**Steps to reproduce:** 1. Enable debug mode and add a snippet. 2. Add a YouTube video with options like autoplay enabled. 3. Double click the video or use "Replace Media". 4. A traceback occurs…
**Steps to reproduce:**
1. Enable debug mode and add a snippet.
2. Add a YouTube video with options like autoplay enabled.
3. Double click the video or use "Replace Media".
4. A traceback occurs while reopening the video options.
**Root Cause:**
When the media dialog is reopened on an existing video,
`syncOptionsWithUrl()` derives each switch option's value from the URL:
`value = this.state.urlInput.includes(urlParameter)`, which stores
Boolean `true` when e.g. autoplay is enabled. Similarly, toggling an
enabled option off stores Boolean `false`
The `value` prop was already `Boolean` but after commit https://github.com/odoo/odoo/commit/ae894892e048b
we declared the prop as `value: { type: String, optional: true }` on
`VideoOption`. In debug mode, OWL validates props and throws
"Invalid props for component 'VideoOption': 'value' is not a string"
when it receives those booleans. The declaration does not match the
values the component actually receives.
**Fix:**
Normalizing the state to strings at the source would require rewriting
the option-state logic for no behavioral gain. Since booleans are
produced deliberately by two code paths, the honest prop type for value
is `[String, Boolean]`.
task-6233423
Forward-Port-Of: odoo/odoo#266304We change the log level of the probe exception to to warning to avoid spamming sentry with unrelevant information. sentry-7641717507
Original PR description
We change the log level of the probe exception to to warning to avoid spamming sentry with unrelevant information. sentry-7641717507
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/9308e17b14 [REL] 19.4.5 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/4a390af94f [FIX] DefaultPlugin: no format should be preserved [Task: 6345022](https://www.odoo.com/odoo/2328/tasks/6345022) https://github.com/odoo/o-spreadsheet/commit/5a19a3e344 [FIX] pivot: prevent drag & drop in pivot side panel [Task: 6268403](https://www.odoo.com/odoo/2328/tasks/6268403) http
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/9308e17b14 [REL] 19.4.5 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/9308e17b14 [REL] 19.4.5 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/4a390af94f [FIX] DefaultPlugin: no format should be preserved [Task: 6345022](https://www.odoo.com/odoo/2328/tasks/6345022) https://github.com/odoo/o-spreadsheet/commit/5a19a3e344 [FIX] pivot: prevent drag & drop in pivot side panel [Task: 6268403](https://www.odoo.com/odoo/2328/tasks/6268403) https://github.com/odoo/o-spreadsheet/commit/5c7b5a0afd [FIX] ui_sheet: `getCellWidth` getter for non-active sheet [Task: 6409426](https://www.odoo.com/odoo/2328/tasks/6409426) https://github.com/odoo/o-spreadsheet/commit/31ff8d1991 [FIX] dashboard: clickable cell is not triggered by a right click [Task: 6365823](https://www.odoo.com/odoo/2328/tasks/6365823) https://github.com/odoo/o-spreadsheet/commit/b48a8737ec [FIX] calendar chart: skip missing value tooltips [Task: 6276800](https://www.odoo.com/odoo/2328/tasks/6276800) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
Three bugs from the openLinkPopup refactor: missing simulateLotOfMenusAsOnRunbot so the extra-menu-items dropdown never appeared, a label used to match the link popover that can never work for a "#" href (the popover shows the target page's title, not the anchor text), and a toggleMegaMenu dropped after the final save. runbot-error-id~940291 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I h
Original PR description
Three bugs from the openLinkPopup refactor: missing simulateLotOfMenusAsOnRunbot so the extra-menu-items dropdown never appeared, a label used to match the link popover that can never work for a "#" href (the popover shows the target page's title, not the anchor text), and a toggleMegaMenu dropped after the final save. runbot-error-id~940291 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
# How to reproduce - Go to a website page - Add atleast one image to the page - Go to Site > Optimize SEO - Add a description to an image - Save # The issue The description is not applied to the image. It is not present in the image's alt in the page and if we re-open the SEO modal, it is not there either. # Cause This [PR] changed the way the image's id are generated in `get_alt_image()`, but did not change the id generation in the check done in `update_alt_images()`, so this check
Original PR description
# How to reproduce - Go to a website page - Add atleast one image to the page - Go to Site > Optimize SEO - Add a description to an image - Save # The issue The description is not applied to the image. It is not present in the image's alt in the page and if we re-open the SEO modal, it is not there either. # Cause This [PR] changed the way the image's id are generated in `get_alt_image()`, but did not change the id generation in the check done in `update_alt_images()`, so this check always fails: https://github.com/odoo/odoo/blob/36eee85dbaed927ff76b2d44a44ba476e9588c72/addons/website/controllers/main.py#L894 [PR]: https://github.com/odoo/odoo/commit/1f1980a1d9835567597ca239d404f47d4677dc47 # Proposed solution Added an helper function for the id generation. This is not necessary but might prevent this from happening again, up to the reviewer to keep it or not opw-6389523 Forward-Port-Of: odoo/odoo#278675 Forward-Port-Of: odoo/odoo#277588
Currently, an error occurs when grouping invoice lines by tax. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting` > `Customers` > `Invoices` and `create an invoice`. - Add an `invoice line`, set a `price`, and select an `account`. - Go to `Accounting` > `Configuration` > `Chart of Accounts`, open the account used on the invoice line, and clear its `Code` field. - Return to the `invoice`, click `Action` > `(Un)Group lines by tax`. `ValueError: Typ
Original PR description
Currently, an error occurs when grouping invoice lines by tax. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting` > `Customers` > `Invoices` and `create an invoice`. -…
Currently, an error occurs when grouping invoice lines by tax.
**Steps to Reproduce:**
- Install the `Accounting` module.
- Go to `Accounting` > `Customers` > `Invoices` and `create an invoice`.
- Add an `invoice line`, set a `price`, and select an `account`.
- Go to `Accounting` > `Configuration` > `Chart of Accounts`, open the account used on
the invoice line, and clear its `Code` field.
- Return to the `invoice`, click `Action` > `(Un)Group lines by tax`.
`ValueError: TypeError('sequence item 1: expected str instance, bool found') while evaluating`
`'if records:\n records.action_group_ungroup_lines_by_tax()'`
After this [recent commit], account codes became optional and can be removed. As a result,
when a user adds an account with no code to an invoice line and groups the lines by the same
tax, it creates the grouped line from here [1]. Then, when it gets the account code from the
grouped line to prepare the name, the code is False, which raises the error [2].
This commit ensures that the grouped line name is built using filter(None, ...), which removes
None and other falsy values, so that the account code is included if it exists; otherwise,
it is not included.
[recent commit]: https://github.com/odoo/odoo/commit/c3313b336b9f1305c363097745926f2bdf61e277
[1]- https://github.com/odoo/odoo/blob/92ced215895d027a2b5606dbecbf3eb96b9dcd10/addons/account_edi_ubl_cii/models/account_move.py#L156-L158
[2]- https://github.com/odoo/odoo/blob/92ced215895d027a2b5606dbecbf3eb96b9dcd10/addons/account_edi_ubl_cii/models/account_move.py#L189-L193
sentry-7598941609
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#275026When refunding an order that had a fixed-amount global discount, the refunded total was inflated: e.g. a 65.00 order (80.00 - 15.00 fixed discount) was refunded as 95.00 instead of 65.00. On the refund order `amount_paid` then differed from `amount_total`, so the POS session's closing entry could not balance; `_validate_session` rolled back and no `account.move` was created, leaving the session closed with no accounting. When a refund is created, the product lines are negated and the discount
Original PR description
When refunding an order that had a fixed-amount global discount, the refunded total was inflated: e.g. a 65.00 order (80.00 - 15.00 fixed discount) was refunded as 95.00 instead of 65.00. On the…
When refunding an order that had a fixed-amount global discount, the refunded total was inflated: e.g. a 65.00 order (80.00 - 15.00 fixed discount) was refunded as 95.00 instead of 65.00. On the refund order `amount_paid` then differed from `amount_total`, so the POS session's closing entry could not balance; `_validate_session` rolled back and no `account.move` was created, leaving the session closed with no accounting. When a refund is created, the product lines are negated and the discount line is excluded, then `pos_discount` re-applies the global discount to the refund order through `applyDiscount`. For a fixed amount, `reduce_base_lines_to_target_amount` targets an absolute value and does not follow the sign of the (now negative) base, so the discount keeps the sale sign. Percentages scale with the base and are not affected. Negate the fixed discount amount when the destination order is a refund so it mirrors the negative base. The stored `discount_value` is left unchanged so the auto-resync of the discount stays idempotent. opw-6400738 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278180
## Steps to Reproduce: - Install `website_sale` with demo data. - Website > eCommerce > Attributes - Delete the 'Size' record. - Upgrade the `website_sale` module. ## Error: `ParseError - while parsing /home/odoo/src/odoo/saas-19.4/addons/website_sale/data/product_attribute_data.xml:16, somewhere inside` ## Cause: Upgrading the module processes the same XML data again; if any data is deleted, it will throw an error. ## Fix: Added the tag 'noupdate' true, so it is skipped during m
Original PR description
## Steps to Reproduce: - Install `website_sale` with demo data. - Website > eCommerce > Attributes - Delete the 'Size' record. - Upgrade the `website_sale` module. ## Error: `ParseError - while parsing /home/odoo/src/odoo/saas-19.4/addons/website_sale/data/product_attribute_data.xml:16, somewhere inside` ## Cause: Upgrading the module processes the same XML data again; if any data is deleted, it will throw an error. ## Fix: Added the tag 'noupdate' true, so it is skipped during module upgrade. sentry-7625354307
When we update the IoT Box certificate, we inform the db about the validity calling a controller. However, on old db, this controller doesn't exist. We avoid displaying the traceback in this case. sentry-7641717371
Original PR description
When we update the IoT Box certificate, we inform the db about the validity calling a controller. However, on old db, this controller doesn't exist. We avoid displaying the traceback in this case. sentry-7641717371
### Steps to reproduce: - Create a table (e.g. /table). - Write text inside a cell and select it partially. - Border options (color, width, style) are shown in the toolbar, but width and style cannot be applied. ### Description of the issue/feature this PR addresses: - Table border styles can only be applied when table cells are fully selected, so these toolbar options should not be shown during partial cell content selections. - Applying custom borders to table cells caused the defa
Original PR description
### Steps to reproduce: - Create a table (e.g. /table). - Write text inside a cell and select it partially. - Border options (color, width, style) are shown in the toolbar, but width and style cannot…
### Steps to reproduce:
- Create a table (e.g. /table).
- Write text inside a cell and select it partially.
- Border options (color, width, style) are shown in the toolbar, but width and style cannot be applied.
### Description of the issue/feature this PR addresses:
- Table border styles can only be applied when table cells are fully selected, so these toolbar options should not be shown during partial cell content selections.
- Applying custom borders to table cells caused the default borders of other unselected cells to disappear on the website, though they worked in the backend. The default border style is `inset`. In the backend, the border color is solid gray ('$o-gray-300'), which renders fine. On the website, the border color is semi-transparent. Browsers cannot render semi-transparent colors properly with the `inset` style, making the borders invisible.
### Desired behavior after PR is merged:
- Show border toolbar options only when table cells are fully selected, and hide them for partial text selections.
- Resolve this by keeping the `inset` style but forcing the default cell border color to use the solid opaque '$o-gray-300' in `base_style.scss`.
task-6356370
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prFollowing odoo/odoo#230736, the function now includes in its count the leaves that have no calendar, regardless of the company. This commit fixes this by grouping them by company as well, and including that count. 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#276472
Original PR description
Following odoo/odoo#230736, the function now includes in its count the leaves that have no calendar, regardless of the company. This commit fixes this by grouping them by company as well, and including that count. 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#276472
e39bf6c6ab31e57d472a552cb9b902496e6323ae introduced a blacklist to email sending, to filter out "alias emails" (catchall...) & the root partner email (odoobot) from being sent certain emails, such as mailings. By default, odoobot email is odoobot@example.com. However, there are cases where users will have a legitimate partner that matches the odoobot email; an example of this is saas config, which automatically changes the odoobot email to the admin email set on database spin-up. This
Original PR description
e39bf6c6ab31e57d472a552cb9b902496e6323ae introduced a blacklist to email sending, to filter out "alias emails" (catchall...) & the root partner email (odoobot) from being sent certain emails, such as mailings. By default, odoobot email is odoobot@example.com. However, there are cases where users will have a legitimate partner that matches the odoobot email; an example of this is saas config, which automatically changes the odoobot email to the admin email set on database spin-up. This prevents the database admin from receiving their own mailings. To fix this, the root partner email is now only added to the blacklist if no active partner has the same email. Steps to reproduce: - Add a mailing contact with the same email as the root partner - Send a mailing to that mailing contact task-4893615 Forward-Port-Of: odoo/odoo#279245 Forward-Port-Of: odoo/odoo#264112
odoo/odoo#155588 was a workaround to the lack of location permissions in the iOS app, skipping geolocation entirely for check in/out. Since odoo/mobile#118 added support for them, we can now revert that fix so iOS users get prompted for their location again. task-6279460 Forward-Port-Of: odoo/odoo#278685 Forward-Port-Of: odoo/odoo#276934
Original PR description
odoo/odoo#155588 was a workaround to the lack of location permissions in the iOS app, skipping geolocation entirely for check in/out. Since odoo/mobile#118 added support for them, we can now revert that fix so iOS users get prompted for their location again. task-6279460 Forward-Port-Of: odoo/odoo#278685 Forward-Port-Of: odoo/odoo#276934
When displaying lots or serial numbers on invoices, the quantity and UoM were rendered without any separator in the SN/LN table, e.g. `1.00Units`. - Add a non-breaking space between the quantity and UoM so the table renders as `1.00 Units`. - Also align the SN/LN value cell consistently with its right-aligned column header. - Update the `sale_stock` and `sale_mrp` report tests to expect whitespace between the quantity and UoM in the rendered invoice output. Task [link](https://www.odoo.
Original PR description
When displaying lots or serial numbers on invoices, the quantity and UoM were rendered without any separator in the SN/LN table, e.g. `1.00Units`. - Add a non-breaking space between the quantity and UoM so the table renders as `1.00 Units`. - Also align the SN/LN value cell consistently with its right-aligned column header. - Update the `sale_stock` and `sale_mrp` report tests to expect whitespace between the quantity and UoM in the rendered invoice output. Task [link](https://www.odoo.com/odoo/project/967/tasks/6365178) task-6365178 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279214 Forward-Port-Of: odoo/odoo#274660