Wednesday, June 3, 2026
22 changes · saas-19.3
New functionality added to Odoo
This update introduces support for Cashmatic, a company specializing in self-service cash machines, through a new HTTP API connection. This allows point-of-sale systems to integrate with Cashmatic's cash counting and dispensing capabilities, providing customers with a convenient payment option. The change enhances payment flexibility within Odoo.
Original PR description
Cashmatic is a company providing self-service cash machines, which can automatically count cash and dispense change. This commit adds basic support for Cashmatic cash machines via an HTTP REST API connection. Task-5887125 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#253585
Resolved issues and error corrections
This update corrects a bug where dropship orders incorrectly displayed a negative delivered quantity. The issue stemmed from a recent change that allowed returns to be tracked, leading to incorrect calculations. This fix ensures accurate delivery quantities for dropship orders, preventing confusion and improving order accuracy.
Original PR description
Currently, an error occurs when the user receives a dropship order instead of delivering it directly to the customer. As a result, the sale order shows a negative delivered quantity. ## Steps to…
Currently, an error occurs when the user receives a dropship order instead of delivering it directly to the customer. As a result, the sale order shows a negative delivered quantity. ## Steps to replicate: - Install Sales, Inventory, and Purchase. - Enable Dropshipping from Inventory settings. - Create a test product with the Dropship route enabled and set a vendor for it in the Purchase tab. - Create and confirm a quotation for a customer. - Go to Purchase > `Deliver To:` and set it to `My Company: Receipts.` - Confirm the Purchase Order and validate the receipt. - Go back to the Sale Order. ## Observed Behavior: The delivered quantity is -1, which is incorrect because the customer has not returned any products, nor has the user created a sale order line with a negative quantity (which would indicate a return). ## Root cause: When computing the delivered quantity at [1], the function `_get_outgoing_incoming_moves` [2] is called to retrieve the incoming and outgoing stock moves associated with the sale order lines. Inside this function, moves are filtered and categorized as incoming or outgoing. At [3], the condition is satisfied because the default value of `to_refund` is `True`, so the move is added to `incoming_move_ids`. Later, during the computation at [1], the code iterates through the incoming moves and subtracts their quantities from the delivered quantity. Since the initial delivered quantity is 0, including such a move in `incoming_move_ids` causes the delivered quantity to become -1. <h3> Why did this behavior not occur in lower versions?:</h3> This issue was introduced by [commit], which added the functionality for users to return products that are not listed in the purchase order. As a result, their quantities appear as negative received quantities on the purchase order. Before this change (in saas-18.2), the field `move.to_refund` had a default value of `False`. Because of this, the condition at [3] was not satisfied, and the move was not included in `incoming_move_ids`. Therefore, it was not subtracted when iterating through incoming moves, and the delivered quantity did not become negative. Starting from 18.3, the default value of `to_refund` was changed to `True`. This causes the condition at [3] to be satisfied, the move to be included in `incoming_move_ids`, and its quantity to be subtracted during the computation, resulting in a delivered quantity of -1. [1]- https://github.com/odoo/odoo/blob/1256875226436a854558a1334ffdc886fa4767a8/addons/sale_stock/models/sale_order_line.py#L193-L209 [2]- https://github.com/odoo/odoo/blob/1256875226436a854558a1334ffdc886fa4767a8/addons/sale_stock/models/sale_order_line.py#L316-L353 [3]- https://github.com/odoo/odoo/blob/1256875226436a854558a1334ffdc886fa4767a8/addons/sale_stock/models/sale_order_line.py#L346-L351 ## Solution: We can make the condition stricter by ensuring that only incoming moves that are actual returns are counted as negative in the quantity delivered on a sale order. Specifically, if an incoming move has no corresponding originating return move and the customer has not created a sale order line with a negative quantity (which could also indicate a return), it should not be considered when calculating the delivered quantity. [commit]: https://github.com/odoo/odoo/pull/209110/changes/c1c86182e4b28e929bf56e79f57f33aaa13e67f1 opw-5933594 Forward-Port-Of: odoo/odoo#267531 Forward-Port-Of: odoo/odoo#252383
This update resolves an issue where paying with the 'customer account' payment method on a zero-priced POS order incorrectly treated the payment as a refund. The fix hides the 'pay_later' payment method in this scenario, aligning with business requirements and preventing incorrect accounting. This ensures accurate order settlement.
Original PR description
Step to reproduce: - install "pos_settle_due" - create a pos order, set order price = 0, select a customer - go to payment page, select "customer account" as payment method - here you can set any…
Step to reproduce: - install "pos_settle_due" - create a pos order, set order price = 0, select a customer - go to payment page, select "customer account" as payment method - here you can set any amount to pay, ex 100$ - fulfill the order. Observation: - the order amount is 0, if we pay 100$ using customer account, it is considered as change (which means we returned it to customer) - As per PO, this flow doesn't make sense Issue: - customer has 100$ due for this order, but he won't be able to settle this as fetch order to settle with amount != 0, after commit [1] - [1] https://github.com/odoo/enterprise/commit/12af23d5382e972facfaa999e4c5ab30c97e8d1f https://github.com/odoo/enterprise/blob/951e5f42884c898bc14d9c32ae6a8f08c31ff06d/pos_settle_due/static/src/app/screens/partner_list/partner_line/partner_line.js#L35 Fix: - we hide payment method of type "pay_later" in case of 0 price order opw-6123699 Forward-Port-Of: odoo/enterprise#118864 Forward-Port-Of: odoo/enterprise#116556
This update resolves an error that prevented users with standard accounting access from verifying company partners within the Türkiye - Nilvera module. The fix allows verification to proceed without requiring full system administrator privileges, improving usability for users with appropriate permissions. This ensures smoother partner setup and verification processes.
Original PR description
**Steps to reproduce:** * Install *Türkiye - Nilvera* (`l10n_tr_nilvera`) module. * Log in as *admin user*. * Create a *partner* for the company. * Set *country* as Turkey. * Configure required…
**Steps to reproduce:** * Install *Türkiye - Nilvera* (`l10n_tr_nilvera`) module. * Log in as *admin user*. * Create a *partner* for the company. * Set *country* as Turkey. * Configure required *taxes*. * Create a *demo user*. * Grant the demo user *Accounting admin access*. * Log in with the *demo user*. * Navigate to *Contacts* → open the *Turkey company partner*. * Navigate to *Nilvera Status* (via Invoicing/Accounting tab) click on *Verify*. **Observed behavior:** * A *company access error* is raised during partner verification. **Cause:** * The field *l10n_tr_nilvera_api_key* is restricted with `groups='base.group_system'`, requiring full system admin rights. * Users with *module-level admin access* (e.g., Accounting) do not have sufficient rights, causing the access error. **Fix:** * Use `sudo()` on `env.company` to bypass the restrictive group access. * This allows users with appropriate *functional admin rights* to perform verification without granting full system privileges. Ticket [link](https://www.odoo.com/odoo/project.task/6106907) opw-6106907 Forward-Port-Of: odoo/odoo#267286 Forward-Port-Of: odoo/odoo#262668
This update fixes an issue where intercompany sales and purchases with multiple identical products resulted in incorrect stock reservation during receipt picking. Specifically, the system was failing to properly reserve all units of a product when creating receipts for intercompany transactions. This ensures accurate stock tracking and order fulfillment across companies.
Original PR description
…lit for same-product lines When doing an intercompany Sale->Purchase with multiple lines having the same products, the receipt picking would be incorrectly assigned: - Enable Inter-Company…
…lit for same-product lines
When doing an intercompany Sale->Purchase with multiple lines having the same products, the receipt picking would be incorrectly assigned:
- Enable Inter-Company Transactions on both companies (Create and validate)
- Create SO in company A to company B with 2 lines having the same product P, Confirm. => Delivery in company A, Purchase and Receipts in company will be created => The SO/PO/Delivery/Receipt will all have 2 lines
- Validate delivery => On the receipt, the 2 units of P are reserved on the 1st move, and the 2nd move is not reserved.
https://github.com/user-attachments/assets/b4816051-120e-4226-9228-fd552649d5ef
---
### Test result without fix:
```
2026-04-23 13:16:44,577 48027 INFO oes_test_18.0 odoo.addons.sale_purchase_stock_inter_company_rules.tests.test_inter_company_so_to_po: Starting TestInterCompanySaleToPurchaseWithStock.test_02_inter_company_multiple_lines_with_same_product ...
2026-04-23 13:16:44,949 48027 INFO oes_test_18.0 odoo.addons.sale_purchase_stock_inter_company_rules.tests.test_inter_company_so_to_po: ======================================================================
2026-04-23 13:16:44,949 48027 ERROR oes_test_18.0 odoo.addons.sale_purchase_stock_inter_company_rules.tests.test_inter_company_so_to_po: FAIL: TestInterCompanySaleToPurchaseWithStock.test_02_inter_company_multiple_lines_with_same_product
Traceback (most recent call last):
File "/home/odoo/Odoo/src/18.0/enterprise/sale_purchase_stock_inter_company_rules/tests/test_inter_company_so_to_po.py", line 109, in test_02_inter_company_multiple_lines_with_same_product
self.assertRecordValues(purchase_from_a.picking_ids.move_ids, [
File "/home/odoo/Odoo/src/18.0/odoo/odoo/tests/common.py", line 709, in assertRecordValues
self.assertSequenceEqual(expected_reformatted, record_reformatted, seq_type=list)
AssertionError: Lists differ: [{'pr[18 chars]0, 'quantity': 1.0}, {'product_uom_qty': 1.0, 'quantity': 1.0}] != [{'pr[18 chars]0, 'quantity': 2.0}, {'product_uom_qty': 1.0, 'quantity': 0.0}]
First differing element 0:
{'product_uom_qty': 1.0, 'quantity': 1.0}
{'product_uom_qty': 1.0, 'quantity': 2.0}
- [{'product_uom_qty': 1.0, 'quantity': 1.0},
? ^
+ [{'product_uom_qty': 1.0, 'quantity': 2.0},
? ^
- {'product_uom_qty': 1.0, 'quantity': 1.0}]
? ^
+ {'product_uom_qty': 1.0, 'quantity': 0.0}]
? ^
```
OPW-6145683
Forward-Port-Of: odoo/enterprise#118805
Forward-Port-Of: odoo/enterprise#114873This update resolves an issue where custom text attributes on products weren't correctly displayed when settling website orders through the POS system. The fix ensures that customer-entered text from these attributes is accurately reflected in the POS order line, improving the user experience and order accuracy. This was a critical fix impacting order fulfillment.
Original PR description
When a sale order containing a product with a custom (free text) attribute was settled in POS, the order line displayed the placeholder attribute value name (e.g. "Custom") instead of the actual text…
When a sale order containing a product with a custom (free text) attribute was settled in POS, the order line displayed the placeholder attribute value name (e.g. "Custom") instead of the actual text entered by the customer. Steps to reproduce: ------------------- * Create a product with a free text attribute (create_variant='no_variant', is_custom=True) * Go to the website's shop (works best in a new private tab) * Fill the free text attribute and add the product to the cart * Click on checkout * In POS, open Quotation/Order and settle the order > Observation: the order line shows "Custom" instead of the text Why the fix: ------------ `SaleOrderLine._load_pos_data_fields` was not exposing `product_no_variant_attribute_value_ids` nor `product_custom_attribute_value_ids`, so the JS `settleSO` function received no attribute data on the `line` object. As a result, the new POS order line was created with empty `attribute_value_ids` and `custom_attribute_value_ids`, leaving `constructFullProductName` unable to find the custom text. The fix adds both fields to `_load_pos_data_fields` and updates `settleSO` to use them when building the new POS order line. The dynamic fetch path (`_getSaleOrder`) is also updated to explicitly read the `product.attribute.custom.value` records so the data is available for orders loaded at runtime. opw-5958678 Forward-Port-Of: odoo/odoo#266875 Forward-Port-Of: odoo/odoo#251993
This pull request resolves errors in the l10n_mx_hr_payroll module related to calculating salary rules for employees with IMSS disabilities when generating CFDI tax documents. Specifically, it addresses missing XML nodes and incorrect amount calculations, ensuring compliance with Mexican law and accurate payroll reporting.
Original PR description
Several error are logged in the chatter when signing a payslip that includes an IMSS disability time off. Steps to reproduce: * Install l10n_mx_hr_payroll_account modules * Switch to "INNOVACION…
Several error are logged in the chatter when signing a payslip that includes an IMSS disability time off.
Steps to reproduce:
* Install l10n_mx_hr_payroll_account modules
* Switch to "INNOVACION VALOR Y DESARROLLO SA SA" company
* Go to Employees and open Cesar Osbaldo Cruz Solorzano
* Click on "Time Off" smart button and create a new time off with "Disability due to illness (IMSS)" type for "02/01/2026"(Any date).
* Go to Payroll > Payslips > Payslips and create a new pay run
* Select Salary Structure 'Mexico: Regular Pay', Pay Schedule 'Monthly' and the Period '01/01/2026 -> 01/31/2026'
* Click on Continue, select Cesar and click on Select
* Open the payslip, click on "Validate" and "Ok"
* Mark as paid, open the "Journal Entry" from the smart button and click on "Post".
* Back to the payslip, and click on "Generate CFDI" button.
* An error is added to the chatter.
### Missing node to declare disabilities
Original message
```py
An error occurred while signing the CFDI document with the government:
Code : NOM111 Message : Error no clasificado. Extra Info : El nodo
"Incapacidades" se debera informar si se incluye en percepciones la
clave 014 "Subsidios por Incapacidad" o bien en deducciones la clave 006
"Descuento por incapacidad".
```
Translated message
```py
An error occurred while signing the CFDI document with the government:
Code: NOM111 Message: Unclassified error. Extra Info: The "Incapacidades"
(Disabilities) node must be reported if the perception key 014
"Subsidios por Incapacidad" (Disability Subsidies) is included, or if
the deduction key 006 "Descuento por incapacidad" (Disability Deduction)
is included.
```
Legal Context:
According to Mexican law, IMSS disabilities must be declared in a specific XML node.
There are two primary scenarios for reporting these amounts:
* Deduction (Type 006): The employer does not pay for these days, as the IMSS is responsible for the payment to the employee.
This is the most common scenario.
* Perception (Type 014): The employer pays for these days as a superior benefit. For example, by law, the IMSS does not pay for the first 3 days of a disability due to illness, and employers are not obligated to cover them either. However, companies offering superior benefits may choose to pay these days as a "Disability Subsidy."
Solution:
The chosen approach is to configure the Deduction node.
For the `l10n_mx_regular_pay_imss_disabilities` rule, the `l10n_mx_concept` has been set to `l10n_mx_concept_d6` (D06 - Disability Deduction). This ensures the required node is added.
### Missing "ImporteMonetario" attribute
Original message
```
An error occurred while signing the CFDI document with the government:
Code : NOM95 Message : El atributo Deduccion:Importe no es igual a la
suma de los nodos Incapacidad:ImporteMonetario, ya que la clave
expresada en Nomina.Deducciones.Deduccion.TipoDeduccion es "006".
```
Translated message
```
An error occurred while signing the CFDI document with the government:
Code: NOM95 Message: The attribute "Deduccion:Importe" does not match
the sum of the "Incapacidad:ImporteMonetario" nodes, as the key
expressed in "Nomina.Deducciones.Deduccion.TipoDeduccion" is "006".
```
Problem:
The "Incapacidades" node requires the "ImporteMonetario" attribute, which should represent the sum of the monetary value associated with the disabilities.
Solution:
Add the "ImporteMonetario" attribute and calculate its value using `l10n_mx_daily_salary`.
### Invalid "DiasIncapacidad" format
```py
An error occurred while signing the CFDI document with the government:
Code : 301 Message : XML mal formado Extra Info : Element
'{[http://www.sat.gob.mx/nomina12}Incapacidad](http://www.sat.gob.mx/nomina12%7DIncapacidad)', attribute
'DiasIncapacidad': '4.0' is not a valid value of the local atomic type.
```
Problem:
Altough the defaultdict where the values are sum up, `number_of_days` is
a float field, and when we get back the value, it is a float, adding for
example 4.0 instead of 4, which is not a valid value.
Solution:
Cast the value to int.
### Duplicate deduction on disabilities
```py
Wrong python code defined for:
- Employee: Cesar Osbaldo Cruz Solorzano
- Version: False
- Payslip: Salary Slip - Cesar Osbaldo Cruz Solorzano - 05/01/2026 - 05/15/2026
- Salary rule: ISR (Income Tax) (ISR)
- Error: TypeError('cannot unpack non-iterable NoneType object') while
evaluating
"
def find_rates(x, rates):
for low, high, fix, rate in rates:
if low <= x <= high:
return low, high, fix, rate
gross = categories['GROSS']
result = 0
if gross:
isr_table = payslip._rule_parameter('l10n_mx_isr_tables')[version.schedule_pay]
low, high, fix, rate = find_rates(gross, isr_table)
result = -((gross - low) * rate + fix)
period_factor = payslip._rule_parameter('l10n_mx_schedule_table')[version.schedule_pay]
if period_factor >= 15:
period_factor = (period_factor / 30) * (365 / 12)
min_wage = payslip._rule_parameter('l10n_mx_daily_min_wage') * period_factor
if gross <= min_wage:
result_qty = 0.0
"
```
Problem:
The IMSS disability amount is being deducted twice:
1. During "Worked Days" calculation, the IMSS disability is already not considered because the work entries belong to the "Unpaid Work Entry Types" of "Mexico: Regular Pay" structure.
2. During "Salary Computation", the `IMSS_DISABLE` salary rule deducts another time because it is in the `TAXABLE_ALW` category, and this one is deducted in the `NET` rule.
When the disability covers more than half of the period (e.g., 20 days in a monthly schedule), the double deduction causes the NET to become negative. This prevents the ISR rule from finding a correct stage in the tax tables, leading to a traceback.
Example: For a monthly wage of 30,000.0 and 5 disability days:
- The total amount in "Worked Days" is 25,000.0 (disabilities already deducted).
- The IMSS_DISABLE rule calculates -5,000.0, and when the NET rule is calculated, the disabilities are deducted again. Total NET becomes 16,843.84 instead of the expected 20,834.85.
Solution:
Change the rule category to `INTERMEDIARY_COMPUTATION` and avoid the double deduction when the `NET` is calculated, as it is already considered in the "Worked Days".
### Incorrect values in the XML
The signing process completes without errors, but some amounts in the generated XML are incorrect.
Problem:
The introduction of the Deduction 006 (Disability) directly impacts the calculation of the SubTotal and Total attributes in the Comprobante node.
For a monthly payslip with a wage of 30,000.00 (daily salary of 1,000.00) and 5 disability days (work risk), the values are calculated incorrectly as follows:
Attribute | Calculation | Actual Value | Correct Value
---------------------|----------------------------------|--------------|--------------
Comprobante:SubTotal | Sum of Perceptions (P01) | 25000.00 | 30000.00 (1)
Comprobante:Total | SubTotal - Total Deductions (2) | 15803.74 | 20803.74
(1) Must include the 5,000.00 from disabilities to balance the deduction.
(2) Total Deductions = D06 (5,000.00) + ISR (3,451.65) + IMSS (744.61) = 9,196.26.
The Total is currently undercalculated because the 5,000.00 is being
deducted from the SubTotal that already had those 5,000.00 excluded.
Solution:
Since the `SubTotal` is derived from Perceptions, and the "(P01)
Salaries, Wages, Stripes, and Day Labor" amount is driven by the
`GROSS_WITHOUT_HOLIDAY` rule, the disability amount must be added. This
balances the Deduction 006, ensuring `SubTotal` is correct.
### Absenteeism and Disabilities
By law, the calculation of IMSS contributions depends on these two types of unpaid days:
* Disabilities: Refers to medical leave issued by the Institute (IMSS).
* Absenteeism: Refers to unjustified leave; apply for periods of fewer than 8 days.
Source: [Artículo 31](https://www.imss.gob.mx/sites/all/statics/pdf/leyes/LSS.pdf)
Translated text:
Article 31. When wages are not paid due to the employee's absence from work, but the employment relationship persists, the monthly contribution shall be adjusted according to the following rules:
I. If the employee's absences are for periods of fewer than eight consecutive or non-consecutive days, contributions shall be calculated and paid for such periods only for the sickness and maternity insurance...
If the employee's absences are for periods of eight consecutive days or more, the employer shall be released from the payment of employer-employee contributions...
IV. In the case of absences covered by medical disabilities issued by the Institute, it shall not be mandatory to cover the employer-employee contributions, except regarding the retirement branch.
The following table summarizes the contribution requirements based on the type of absence:
Insurance Branch (RAMA) | Section I (Absenteeism) | Section IV (Disability)
--------------------------------|-------------------------|------------------------
Sickness and Maternity | Paid | Not Paid
Disability and Life | Not Paid | Not Paid
Severance and Old Age | Not Paid | Not Paid
Work Risk | Not Paid | Not Paid
Daycare and Social Benefits | Not Paid | Not Paid
INFONAVIT | Not Paid | Paid
Retirement | Not Paid | Paid
The type of unpaid day to be considered depends on the specific insurance branch being calculated within the employer-employee contributions.
### Add test for cfdi with disabilities.
target: 19.0
task-6066160
Forward-Port-Of: odoo/enterprise#116819A recent update unintentionally removed a styling class from dynamic website snippets, causing layout problems. This commit corrects the fix to ensure the correct styling is applied to all dynamic snippets, restoring the intended visual appearance. This ensures consistent and accurate display of dynamic content on the website.
Original PR description
Before [1], the `s_dynamic_snippet_row` class was added to all dynamic snippets using the `website.s_dynamic_snippet.grid` template and defining `columnClasses` values. In [1], a fix was introduced to prevent adding this class on mono-record snippets, as it was breaking the layout when used inside small containers (`o_container_small`). However, the condition introduced by that fix is too broad and currently removes the class from all dynamic snippets. This commit fixes the condition so that `s_dynamic_snippet_row` is only excluded from mono-record snippets, restoring the intended layout for other dynamic snippets. [1]: https://github.com/odoo/odoo/commit/fe2279f760adc6a53ba2242961f3873a5d3215dd Forward-Port-Of: odoo/odoo#265362 Forward-Port-Of: odoo/odoo#264407
This update resolves a technical issue preventing Viva payments in the POS kiosk. The Viva payment system requires a unique identifier for the cash register, which was previously missing. This fix ensures the correct 'cashRegisterId' is sent during payment validation, preventing errors and allowing successful Viva transactions.
Original PR description
When validating a payment in POS Kiosk with Viva payment method we get a Viva.com error Viva’s card-terminal API validates the JSON body with Pydantic and requires a non-empty ``cashRegisterId``. Steps to reproduce: ------------------- * Open POS in kiosk * Make an order and pay with Viva > Observation: Viva returns a validation error: ``cashRegisterId`` is missing or required in the request body (Pydantic ``missing`` on ``body.cashRegisterId``). Why the fix: ------------ Compute ``cashRegisterId`` in the POS client as cashier name, then ``pos.config.name`` so the value is always a non-empty string sent to ``viva_wallet_send_payment_request``. opw-6091223 Forward-Port-Of: odoo/odoo#267280 Forward-Port-Of: odoo/odoo#258605
This update resolves an issue where purchase journals created with only the Invoicing app couldn't import Peppol XML invoices due to a missing default account. The fix automatically assigns a default expense or income account, mirroring how it works for bank/cash journals, ensuring successful invoice imports.
Original PR description
When a user installs only the Invoicing app and creates a new purchase journal, no default_account_id is set on the journal. The Invoicing app does not expose account configuration, so the user…
When a user installs only the Invoicing app and creates a new purchase journal, no default_account_id is set on the journal. The Invoicing app does not expose account configuration, so the user cannot fix this manually. As a result, importing a Peppol XML invoice through that journal fails with a database constraint error because the generated account.move.line has a null account_id. https://github.com/odoo/odoo/blob/16245530f0e3e9be21c8b96baaecd3a679420cac/addons/account/models/account_journal.py#L776-L805 This already auto-creates accounts for bank/cash journals, but does nothing for sale/purchase journals. Steps to reproduce: - Install the Invoicing app (no full Accounting) - Create a new purchase journal with type 'purchase' - Go to Vendors -> Bills and Upload a Peppol XML file - Error importing attachment as invoice (decoder=_import_invoice_ubl_cii) Ticket [link](https://www.odoo.com/odoo/action-4043/6014363) opw-6014363 Forward-Port-Of: odoo/odoo#267552 Forward-Port-Of: odoo/odoo#252859
This update fixes a critical issue where users could accidentally add snoozed products to their orders, particularly in self-ordering scenarios. The changes now include warnings before adding snoozed items and improved detection across product types, ensuring a smoother and more reliable ordering experience. This prevents potential order errors and improves user confidence.
Original PR description
### Before this commit: - Snoozed products could still be selected from the product screen and combo configurator without any warning. - Users could add snoozed products to the order by mistake. - In self-order, snoozed products were still selectable in combo items. - Snooze checking was only based on product template id. For product variants (`product.product`), the `product_tmpl_id` was not checked. ### After this commit: - Add a `canAddProductToCurrentOrder` method to show a warning before adding a snoozed product. - Apply this check in the product screen and combo configurator. - Improve snooze detection by supporting both `product.template` and `product.product` (via `product_tmpl_id`). - In self-order, If a product is snoozed, show it as 'Out of stock'. - If a product is not available in self-order, do not show it in the list. - Fix the radio input attribute in the snooze dialog. Task:6012412 Forward-Port-Of: odoo/odoo#267304 Forward-Port-Of: odoo/odoo#253269
This update enhances the security of Odoo.com by adding a check to ensure that test databases, which are often duplicated SAAS environments, are properly verified for subscription status. Previously, test databases could bypass subscription checks, creating a potential vulnerability. This fix strengthens Odoo.com's security posture.
Original PR description
Odoo.com does not create a distinction between a production and duplicated SAAS database. This allows test databases to pass the check for subscription. This commit adds an additional check for duplicated SAAS databases with a neutralised status. task-6249752 Forward-Port-Of: odoo/enterprise#118281
This update ensures that duplicated website pages accurately reflect the latest content, regardless of the user's language setting. Previously, duplication didn't account for updated translations, leading to inconsistencies. The fix now correctly uses the most recent translation data during duplication, maintaining accurate content across all languages.
Original PR description
**Steps to Reproduce:** 1. Configure the website default language different from the user’s current language. 2. Go to Website → Site → Pages. 3. Duplicate an existing page. 4. Edit the duplicated…
**Steps to Reproduce:**
1. Configure the website default language different from the user’s current language.
2. Go to Website → Site → Pages.
3. Duplicate an existing page.
4. Edit the duplicated page and save the changes.
5. Duplicate the edited page again.
6. Observe that the newly duplicated page is generated from the original page content instead of the updated duplicated page.
**Issue:**
When duplicating a website page, the website default language was not passed in the context. As a result, the duplication was performed using the active user language.
The root cause is that `copy()` does not simply duplicate the existing `arch_db` translation dictionary. Instead, it copies the field value in the current language and then rebuilds all translations through `copy_translations()`.
This behavior becomes problematic when `delayed translations` are involved. After a page is modified in a non-default language, the latest changes may be stored in a delayed translation entry (`_{lang}`) while the regular translation value remains unchanged. During the copy process, these delayed translation entries are intentionally discarded because they are considered temporary data and are not treated as valid language translations.
As a result, when the page is duplicated from a non-default language, `copy()` rebuilds the translations using an outdated translation value instead of the most recent content stored in the delayed translation. The newly
duplicated page therefore does not accurately reflect the current state of the source page.
**Solution:**
The fix enables `check_translation=True` during page duplication so that the copy operation uses the latest translation state, including delayed translations when available. This ensures duplicated pages are generated
from the most recent version of the source page and keeps translated content consistent across languages.
**opw-5914281**
Forward-Port-Of: odoo/odoo#264656This update resolves an issue where large ecommerce orders (over 150kg) using the Sendcloud delivery method would generate errors. The fix ensures the order weight is correctly retrieved, preventing a crash when the delivery requires multiple packages due to weight limitations. This improves the reliability of order processing for heavy items.
Original PR description
Issue ----- Traceback when trying to get a rate through the e-commerce if the order has to be split into multiple packages due to weight being too high. Steps to reproduce ----- - Setup Sendcloud…
Issue ----- Traceback when trying to get a rate through the e-commerce if the order has to be split into multiple packages due to weight being too high. Steps to reproduce ----- - Setup Sendcloud delivery method - make it available in e-commerce - Create a 150kg product and publish it - Go to e-commerce - Add the product to cart - Checkout the cart > Traceback Cause ----- We retrieve the order's weight through the context. https://github.com/odoo/enterprise/blob/d9a9339e1f30f1e5cc37ebb88949451a6652f83b/delivery_sendcloud/models/delivery_carrier.py#L108 If the call to `_get_shipping_rate` returns that the delivery requires multiple packages, we go into https://github.com/odoo/enterprise/blob/d9a9339e1f30f1e5cc37ebb88949451a6652f83b/delivery_sendcloud/models/delivery_carrier.py#L126-L128 If `order_weight` was not present in the context, this will cause an error in `sendcloud_convert_weight` since it expects a numerical value but receives the `None` fallback. This context key is only present when going through `choose.delivery.carrier` (so not in the e-commerce flow). https://github.com/odoo/odoo/blob/058e640e6687ed3f709dc846f0fa7a1f45226849/addons/delivery/wizard/choose_delivery_carrier.py#L69 ----- Ticket: opw-6210398 Forward-Port-Of: odoo/enterprise#119048 Forward-Port-Of: odoo/enterprise#117028
This update corrects an issue where the FIFO valuation in stock reports was fluctuating due to how it recalculated prices based on current stock levels. The fix ensures that historical valuations remain consistent, providing more accurate reporting of inventory value at specific dates. This improves the reliability of financial reporting.
Original PR description
When the stock valuation closing report computes FIFO valuation at a historical date, it recomputes each move's value via `move._get_value(at_date)`. For moves without a purchase link (inventory…
When the stock valuation closing report computes FIFO valuation at a historical date, it recomputes each move's value via `move._get_value(at_date)`. For moves without a purchase link (inventory adjustments, initial inventory), the value falls through to `_get_value_from_std_price()` which uses the current `standard_price`. For FIFO products, `standard_price` is recalculated on every stock operation (`total_value / qty_available`), so the historical valuation drifts as new operations are processed. This is the same root cause as https://github.com/odoo/odoo/commit/d2934b59e49ef943d957a80e53cca835a59fabef which fixed it for AVCO's `_run_average_batch` by passing `forced_std_price`. This commit fixes the FIFO path by using `move.value / move._get_valued_qty()` (the unit price stored at validation time) as the fallback in `_get_value_from_std_price` when `at_date` is set and no std_price was explicitly forced. Steps to reproduce (in `odoo-bin shell`, using freezegun's `freeze_time` to backdate two operations to different dates, e.g. date1 = two days ago and date2 = yesterday): 1. Create a FIFO periodic product with `standard_price = 10` 2. With `freeze_time(date1)`: apply an inventory adjustment of 10 units 3. With `freeze_time(date2)`: receive 10 units at unit cost 20 > standard_price shifts to 15 4. Check `product.with_context(to_date=date1).total_value` > Before fix: 150 (drifted with current standard_price) > After fix: 100 (stable, uses stored move value) closes opw-6081736 Forward-Port-Of: odoo/odoo#267409 Forward-Port-Of: odoo/odoo#262127
This update fixes an issue where flexible employee time off wasn't displayed accurately in the calendar view. Now, time off durations are correctly grayed out from midnight to 11 PM, aligning with expected behavior and ensuring accurate representation of available time. This improves the usability of the attendance app for flexible workers.
Original PR description
__ ## Short functional explanation of the error When setting a time off for an employee who has a flexible schedule, in the attendance app, on the calendar with the 'days' view. We can see that the…
__ ## Short functional explanation of the error When setting a time off for an employee who has a flexible schedule, in the attendance app, on the calendar with the 'days' view. We can see that the hours are grayed out from 8 hours to 16 hours. However, according to this message: https://www.odoo.com/mail/message/1027495005 "[...] the entire day of absence might not be represented as such, which is an issue (for example if a flexible employee with 8h/day takes a day off, the duration of the leave should be 1 day/8 hours but on the gantt view everything should be gray from midnight to midnight)". Moreover, when we select the Week or Month view on the calendar, the day off isn't grayed out. This comes from the fact that, for a flexible schedule, we consider that any time of the day can be a working hour; and we only grey out days in the calendar where no hour has been worked at all. Hence, the hours considered during a flexible day off should be from midnight to 23:59:59. ## Reproduction Steps 1. Go to an employee's profile and set their schedule to flexible. 2. Create a time off of a one-day duration for this employee. 3. Go to the attendance app and see the calendar. ### Expected behavior When clicking on the Day view, all hours from midnight to 11pm should be grayed out. When clicking on the Week/month view, the day of the time off should be grayed out. ### Unexpected behavior When clicking on the Day view, hours from 8am to 4pm are grayed out. When clicking on the Week/month view, the day of the time off isn't grayed out. ## Origin of the issue First, we only consider the leave if the resource is fully flexible, i.e if the employee has no working calendar set: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L546 However, if the schedule of the employee is flexible, the leave resource isn't considered as fully flexible, thus leading us to a leave from 8 am to 4 pm. Moreover, when processing flexible leaves, we return the unavailable intervals with the timezone of the employee: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L589-L592 Whereas when we process fixed leaves, we return the unavailable intervals under utc: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L597-L601 This leads us to display problems: when the user is under European/ Brussels time in summer, the leave starts at 2 am and ends at 11pm, instead of starting at midnight. Note: after discussion with AJU, it has been agreed that the behavior should be the same on the Planning app. __ opw-6030212 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267234 Forward-Port-Of: odoo/odoo#256636
This update fixes an issue where flexible employee time off wasn't accurately displayed in the attendance calendar. Previously, hours were grayed out from 8am-4pm instead of the full 24-hour period. Now, the calendar correctly shows all hours from midnight to 11pm for flexible time off, ensuring accurate tracking of employee availability.
Original PR description
__ ## Short functional explanation of the error When setting a time off for an employee who has a flexible schedule, in the attendance app, on the calendar with the 'days' view. We can see that the…
__ ## Short functional explanation of the error When setting a time off for an employee who has a flexible schedule, in the attendance app, on the calendar with the 'days' view. We can see that the hours are grayed out from 8 hours to 16 hours. However, according to this message: https://www.odoo.com/mail/message/1027495005 "[...] the entire day of absence might not be represented as such, which is an issue (for example if a flexible employee with 8h/day takes a day off, the duration of the leave should be 1 day/8 hours but on the gantt view everything should be gray from midnight to midnight)". Moreover, when we select the Week or Month view on the calendar, the day off isn't grayed out. This comes from the fact that, for a flexible schedule, we consider that any time of the day can be a working hour; and we only grey out days in the calendar where no hour has been worked at all. Hence, the hours considered during a flexible day off should be from midnight to 23:59:59. ## Reproduction Steps 1. Go to an employee's profile and set their schedule to flexible. 2. Create a time off of a one-day duration for this employee. 3. Go to the attendance app and see the calendar. ### Expected behavior When clicking on the Day view, all hours from midnight to 11pm should be grayed out. When clicking on the Week/month view, the day of the time off should be grayed out. ### Unexpected behavior When clicking on the Day view, hours from 8am to 4pm are grayed out. When clicking on the Week/month view, the day of the time off isn't grayed out. ## Origin of the issue First, we only consider the leave if the resource is fully flexible, i.e if the employee has no working calendar set: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L546 However, if the schedule of the employee is flexible, the leave resource isn't considered as fully flexible, thus leading us to a leave from 8 am to 4 pm. Moreover, when processing flexible leaves, we return the unavailable intervals with the timezone of the employee: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L589-L592 Whereas when we process fixed leaves, we return the unavailable intervals under utc: https://github.com/odoo/odoo/blob/394a30f8a2814d460cfe220b5017e7ac95d8cddc/addons/resource/models/resource_calendar.py#L597-L601 This leads us to display problems: when the user is under European/ Brussels time in summer, the leave starts at 2 am and ends at 11pm, instead of starting at midnight. Note: after discussion with AJU, it has been agreed that the behavior should be the same on the Planning app. __ opw-6030212 Forward-Port-Of: odoo/enterprise#118832 Forward-Port-Of: odoo/enterprise#112482
This update resolves an issue where changes made within nested editable areas of the description field weren't consistently saved. The fix replaces a specific event listener to ensure that blur events bubble up correctly, triggering the save functionality. This ensures that all description updates, even within complex nested structures, are reliably saved.
Original PR description
Problem: When the selection is inside a `contenteditable="true"` element that is not the root editable, focusing away does not trigger a save. Cause: When the DOM contains a nested contenteditable…
Problem:
When the selection is inside a `contenteditable="true"` element that is not the root editable, focusing away does not trigger a save.
Cause:
When the DOM contains a nested contenteditable structure like:
```html
<div class="odoo-editor-editable" contenteditable="true">
abc
<div contenteditable="false">
ac
<div contenteditable="true">a</div>
</div>
</div>
```
With the selection inside the inner `contenteditable="true"`, the `blur` event is not triggered on `.odoo-editor-editable` when focusing away, because the active element is the inner div and `blur` does not bubble.
Solution:
Replace the `blur` listener with `focusout`, which bubbles from the inner `contenteditable="true"` up to `.odoo-editor-editable`, allowing `onBlur` to be called correctly.
Steps to reproduce:
- Open Project > Task.
- Add a `/column` block in the description.
- Focus inside any column and write some text.
- Click away to change tab.
- Reopen the description tab.
- The latest changes inside the columns were not saved.
opw-6227922
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#266367This update fixes a bug where restaurant takeout and delivery tickets didn't display the customer's name. The change restores the display of the customer's name when ordering directly without a table selection, ensuring accurate order information for customers. This improves the customer experience and order clarity.
Original PR description
Steps to reproduce ------------------ 1. Configure a kitchen printer for a restaurant 2. Open PoS, don't select a table, but select a customer 3. Select Take Out or Delivery 4. Add products in the kitchen printer's category 5. Send to the kitchen Observation -> the ticket shows the order reference, not the customer name! Why the issue: -------------- Before the receipt printer refactor https://github.com/odoo/odoo/commit/b1f17b6e61191cad6a932f923e76a0c406f91f13, the template was showing `order.getName()`, which returns `floating_order_name` (set to the partner name by `setPartner`). After the refactor, the template shows the raw `pos_reference` directly, hence the partner name is missing. Fix: ---- Show the table label when a table is set, otherwise `floating_order_name` which includes the customer name if it exists. opw-6208965 Forward-Port-Of: odoo/odoo#264985
This update resolves an issue where the ‘NABN’ document type was unavailable when creating credit notes for GT companies. The fix ensures users can now correctly select ‘NABN’ for vendor credit notes, aligning with GT-specific requirements for electronic payment processing. This improves the accuracy of financial reporting for GT businesses.
Original PR description
**Steps to reproduce:** - Install the `l10n_gt_edi` module and switch to a `GT Company`. - Navigate to Invoicing > Vendors > Bills. - Create a new vendor bill. - Observe that `GT Document Type`…
**Steps to reproduce:** - Install the `l10n_gt_edi` module and switch to a `GT Company`. - Navigate to Invoicing > Vendors > Bills. - Create a new vendor bill. - Observe that `GT Document Type` includes `NABN - Nota de Pago Electrónica` option. - `Confirm` the bill. - Click `Credit Note`, add a reason, and click `Reverse`. - Observe the available options in the `GT Document Type` field. **Observation:** The `NABN - Nota de Pago Electrónica` option is not available for vendor credit notes (`in_refund`), even though `NABN` is a GT-specific credit note document type. **Root Cause:** At [1], `NABN` is added for vendor bills (`in_invoice`, `in_receipt`) instead of vendor credit notes (`in_refund`). **Fix:** This commit ensures users can correctly select `NABN - Nota de Pago Electrónica` on GT vendor credit notes. [1]: https://github.com/odoo/enterprise/blob/c7fa8c9c6f6830f5702fab4f6efaf3ac33f7fe72/l10n_gt_edi/models/account_move.py#L159-L160 opw-6252256 Forward-Port-Of: odoo/enterprise#119215 Forward-Port-Of: odoo/enterprise#118711
This update resolves an issue where time off requests with the 'Both' approval type didn't send notifications to the designated responsible parties (e.g., Time Off Officer). The fix ensures that notifications are correctly triggered, improving the accuracy and efficiency of the time off request process. This prevents delays and ensures timely approvals.
Original PR description
…cer') no fallback for responsible_ids
Issue:
When ('both','By Employee's Approver and Time Off Officer') is selected on a new HR Leave Type it does not fall back to the responsible_ids or “Notify HR”.
Steps:
1) Setup a neutralized outgoing mail server
2) install hr_holidays
3) make a new hr.leave.Type (Approval) with ('both','By Employee's Approver and Time Off Officer') and select a 'Notified Time Off Officer'(responsible_ids) 4) select an emplyee with a reelated user and remove the coach, manager, and responsible 'Time Off'. 5) save
6) Sign in as the employee, make a time off request under the new Type 7) No email
Fix:
Add a conditional with the lowest priority to fall back to responsible_ids
opw-6101637
Forward-Port-Of: odoo/odoo#265760
Forward-Port-Of: odoo/odoo#261853This update fixes an issue where EDI invoices were incorrectly assigned to individual contacts due to shared VAT numbers. The change prioritizes main companies and active vendors during matching, ensuring invoices are routed to the correct business entity. This improves data accuracy and reduces manual intervention in invoice processing.
Original PR description
### Description of the issue/feature this PR addresses: **Issue:** When processing incoming EDI/Peppol invoices, multiple contacts (parent companies, joint ventures, child contacts) frequently share…
### Description of the issue/feature this PR addresses: **Issue:** When processing incoming EDI/Peppol invoices, multiple contacts (parent companies, joint ventures, child contacts) frequently share the same VAT number. Because the parser's tie-breaker relies primarily on VAT, invoices are often incorrectly assigned to individual child contacts or newly created joint ventures rather than the correct vendor. **Solution:** Prepend is_company DESC, supplier_rank DESC to the SQL search order in the _import_retrieve_customer fallback domain. This ensures that the matching logic explicitly prioritizes business entities over individual contacts, and active vendors over other records. ### Current behavior before PR: When searching by VAT with limit=1, the parser uses order='company_id, parent_id DESC, id DESC'. If a parent company and a child contact share a VAT, the query frequently returns the child contact or a newer joint venture due to the id DESC fallback. ### Desired behavior after PR is merged: The parser will correctly prioritize main companies over individual contacts when VAT numbers are shared. If multiple companies share the same VAT, the most active vendor will be selected. opw-6174628 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#266224