Skip to content

Merge from upstream 18 20260615 01#1228

Open
royle-vietnam wants to merge 309 commits into
Viindoo:18.0from
royle-vietnam:merge_from_upstream_18_20260615_01
Open

Merge from upstream 18 20260615 01#1228
royle-vietnam wants to merge 309 commits into
Viindoo:18.0from
royle-vietnam:merge_from_upstream_18_20260615_01

Conversation

@royle-vietnam

Copy link
Copy Markdown
Collaborator

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

khah-odoo and others added 30 commits May 19, 2026 12:35
In the test `test_create_work_entry_for_flexible_employee_leave`,
the `jules_emp` will not have a timezone if the test is ran without
demo data. This commit adds a timezone to ensure consistent behavior.

error-230949

closes odoo#225544

Signed-off-by: Bertrand Dossogne (bedo) <bedo@odoo.com>
Before this commit:

- URL_REGEX was constructed with the "i" flag, but passing a RegExp object
to new RegExp(regex, "g") silently drops the original flags, leaving only
"g". This caused uppercase (ODOO.COM) and mixed-case (Odoo.Com) URLs to
not be converted to links when pressing space.

After this commit:

- URL_REGEX.source with explicit "gi" flags to preserve case-insensitive
matching in `prepareConvertToLink`.

task-6199269

Part-of: odoo#263255
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
Purpose of this commit:

- Allow automatic URL detection for single-character domains such
as `x.com`, `t.co`, and `a.io` by relaxing the minimum domain
label length in the URL regex from 2 to 1 characters.

task-6199269

closes odoo#263255

Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
**STEP TO REPRODUCE**
1. Create a partner with a bank account and setup its currency.
2. Create an invoice using a different currency.
3. Send the invoice to Ksef.
4. Notice the generated xml contains the invoice currency in the field OpisRachunku,
but it should be the bank account currency instead.

opw-6150563

closes odoo#263842

Signed-off-by: Antoine Boonen (aboo) <aboo@odoo.com>
…task

closes odoo#265069

Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
**Problem:**
In PR odoo#263425, the test uses the user group `group_account_manager` to grant
write access to `bank.statement.line`. However, this write access is available
through `group_account_user` which is only inherited in `account_accountant`.

**Solution:**
Instead, use the user group `group_account_user` directly so the test works
with only `account` installed.

closes odoo#265049

X-original-commit: 487ae08
Signed-off-by: William André (wan) <wan@odoo.com>
Signed-off-by: Dirk Douglas (dido) <dido@odoo.com>
…computing lead_days

When computing `qty_to_order` 1-3 extra queries are made by `get_lead_days()`, which can cause performance
issues when computing `qty_to_order` for a large number of orderpoints.

This commit aims to prevent these extra queries by returning early if the current product is not associated with
a bom. The amount this commit speeds up the compute depends on how many of products passed into `_get_lead_days()`
are associated with a bom.

`qty_to_order` is no longer a stored field after this commit: odoo#159432
This benchmark was done in 18.0 on /stock.warehouse.orderpoint/search_panel_select_range. This call does not
trigger the compute on all orderpoints in 17.0 as the field is stored but calling the compute directly on all
orderpoints results in the same speed up as seen in 18.0.

| Orderpoints | % of products linked to a bom | Time before | Queries before | Time after | Queries After |
|-------------|-------------------------------|-------------|----------------|------------|---------------|
|         800 | 50%                           | 2.8s        |           1570 | 2.3s       |           818 |
|       8,000 | 0%                            | 28.2s       |         16,698 | 15.3s      |           242 |
|       8,000 | 25%                           | 29.6s       |         16,833 | 19.2s      |          4497 |
|       8,000 | 50%                           | 29.8s       |         16,925 | 23.2s      |          8693 |
|       8,000 | 75%                           | 31.6s       |         16,949 | 27.6s      |         12827 |

rebase later

closes odoo#264689

X-original-commit: 9ba3c2e
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Mackenzie Smith (macs) <macs@odoo.com>
**PROBLEMS**
1. On a company with taxes with price_include = True, we fail to retrieve a tax when importing a ubl.
2. The price_unit adjustement for when importing price-included taxes doesn't account for quantity.

**STEP TO REPRODUCE**
1. Have a setup where the tax are only price_include.
2. Import a ubl, the taxes will not be retrieved.
3. Run odoo only with the fix for problem 1, and import the same ubl with some invoice quantity != 1
4. Notice the price unit are messed up for lines with quantity != 1
(odoo tries to correct the untaxed amount with a line, but this doesn't fixes the tax).

opw-6159394

closes odoo#262686

Signed-off-by: Léo Gizard (legi) <legi@odoo.com>
**STEP TO REPRODUCE**
1. Create an invoice and fill the customer reference field (other info tab).
2. send the invoice to ksef.
3. Open the generated fa3 file, and notice there is no mention of the customer reference.

opw-6150812

closes odoo#263797

Signed-off-by: Paolo Gatti (pgi) <pgi@odoo.com>
Steps to Reproduce:
1. Install the stock module.
2. Activate "Multi-Step Routes" from settings.
3. Activate the "My Company (Chicago)" company.
4. Create a route for the Chicago company.
5. Create a new product and enable the created route on it.
6. Click on the "Replenish" button.

Error:
IndexError - tuple index out of range

Cause:
At [1], when none of the product routes belong to the current company or are
shared routes, the filtering returns an empty recordset. As a result, trying to
access the first route from the empty result raises an index error.

Fix:
This commit only assigns route_id when a route matches the given condition.
Otherwise, it keeps the value as False.

[1]: https://github.com/odoo/odoo/blob/13c0e082c260381a332fe1425fe2ba83a1c0c579/addons/stock/wizard/product_replenish.py#L78

sentry-7488075413

closes odoo#265179

Signed-off-by: William Henrotin (whe) <whe@odoo.com>
For SEZ transactions, the e-waybill API requires `toStateCode` to be set to 99.
Previously, this value was not enforced, leading to API errors.

This fix updates the logic to derive `toStateCode` based on the partner's
GST treatment. When the transaction is identified as SEZ, `toStateCode` is
correctly set to 99, ensuring compliance with e-waybill requirements and
preventing API failures.

task-6117694

closes odoo#265218

X-original-commit: ef9772b
Signed-off-by: Josse Colpaert (jco) <jco@odoo.com>
Signed-off-by: Harsh Shah (hash) <hash@odoo.com>
Source: gob.mx/cms/uploads/attachment/file/151586/codigo_agrupador.pdf
opw-6174385

closes odoo#264516

X-original-commit: a4b702e
Signed-off-by: Claire Bretton (clbr) <clbr@odoo.com>
Signed-off-by: Léo Gizard (legi) <legi@odoo.com>
Currently, the pos_restaurant module only calls capture payment within
validateTip if there is an amount to tip. Since Stripe requires a
capture for all payments and we defer that to later if it can be
adjusted, this means that if customers have tip after payment enabled
and enter a tip of 0%, it will never be captured, stay pending, and then
be automatically cancelled later down the line.

This commit catches that by hooking into the validateTip method and
calling capture anyway on 0 charge tips.

opw-6082596

closes odoo#261129

Signed-off-by: Adrien Guilliams (adgu) <adgu@odoo.com>
1. The following flags accept only "1" as a valid value.

See their type being etd:TWybor1:
http://crd.gov.pl/wzor/2025/06/25/13775/schemat.xsd
http://crd.gov.pl/xml/schematy/dziedzinowe/mf/2020/07/06/eD/DefinicjeTypy/ElementarneTypyDanych_v7-0E.xsd

```
<xsd:simpleType name="TWybor1">
        <xsd:annotation>
                <xsd:documentation>Pojedyncze pole wyboru</xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:byte">
                <xsd:enumeration value="1"/>
        </xsd:restriction>
</xsd:simpleType>
```

2. KursWaluty is optional and doesn't need to be included if it's the same
as PLN.

closes odoo#262462

Signed-off-by: Paolo Gatti (pgi) <pgi@odoo.com>
Issue:
- Create an invoice with a line having a price of `528,000,000.00`
- Print the invoice
-> pdf displays `528,000,000.000001`

Cause:
In `value_to_html` from `ir.qweb.field.float`, we compute the maximum
precision that we can get from the value, to avoid parasite digits.
The maximum is 15, so if a number has 11 digits, we won't ask for
a precision higher than 4.
But in `float_round`, they multiple the value with its precision, then
add `epsilon` (a small value).
So we're now working with a 16 digits float, which is what we want to
avoid.

Solution:
Reduce the maximum precision from one digit before calling
`float_round`.

opw-6012129

closes odoo#260955

Signed-off-by: William André (wan) <wan@odoo.com>
Commit e61403f accidentally removed test_aggregates while resolving
a forward-port conflict. Restoring the test to maintain coverage.

no task-id

closes odoo#263854

Signed-off-by: Julien Alardot (jual) <jual@odoo.com>
…cates

Description of the issue/feature this PR addresses:
Issue: During KSeF authentication, some foreign qualified certificates
causes a crash with error "Failed to authenticate with XAdES: 400
Client Error: Bad Request for url:
https://api.ksef.mf.gov.pl/v2/auth/xades-signature" This is due to Odoo
not handling different SubjectIdentifierType

Solution: Implement a try/except block to safely check for the NIP in
the certificate's subject string, defaulting the identifier type to
certificateFingerprint when the NIP is missing or a ValueError is
caught.

Current behavior before PR:
The SubjectIdentifierType is hardcoded as certificateSubject, and does
not handle certificateFingerprint at all. This causes there to be
an error when trying to authenticate with the KSeF server using XAdES
signature.

Desired behavior after PR is merged:

The sign_authentication_challenge method will now safely evaluate the
subject string. It assigns certificateSubject only if the NIP is
verified to be in the subject string. If the NIP is absent or a
ValueError occurs during parsing, the system safely falls back to
using certificateFingerprint. This prevents tracebacks and ensures the
correct XML payload is sent to the KSeF server.

opw-6125243

closes odoo#265390

Signed-off-by: Paolo Gatti (pgi) <pgi@odoo.com>
`_import_ubl_invoice_add_base_lines` filters out every imported line
whose `total_included_currency` is zero, on the assumption that a
zero-amount line carries no useful information. This is correct for
truly empty rows, but wrong for 100%-discounted lines,
an ecotax or excise row, or a returnable-packaging
entry nets to zero precisely because the supplier discounted it
entirely, and the line still carries data the customer needs to
reconcile the bill against the original document

The fix is to loosen the filter so that a line is kept either when its total is
non-zero or when it has a non-zero discount.

opw-6176349

closes odoo#265284

Signed-off-by: John Laterre (jol) <jol@odoo.com>
Before this commit:
1. Create a POS order with no discount and a quantity that does not
   divide evenly into the unit price (e.g. price=10.0, qty=3)
2. Send the order to Jofotara

Jofotara rejects the invoice because the AllowanceCharge/Amount on the
invoice line is a small negative value like -0.000000001 with the error `"EINV_MESSAGE":"discount cannot be negative"`

This happens because _add_document_line_gross_subtotal_and_discount_vals
computes the discount as: gross_subtotal - total_excluded_currency

where gross_subtotal goes through two independent rounding steps
(round unit price, then round unit_price * qty). When the quantity
is indivisible, the reconstituted gross_subtotal can land just below
total_excluded_currency by a floating-point epsilon, producing a tiny
negative discount. The same subtraction also produces a legitimate
negative value for refund lines (negative quantity), which was already
handled by abs() in _add_pos_order_discount_vals for the document-level
total but was left unguarded at the per-line level.

After this commit:

Apply abs() to vals[f'discount_amount{currency_suffix}'] in _add_pos_order_line_allowance_charge_nodes
so that discount_amount_currency is always non-negative.

opw-6183423

closes odoo#265159

Signed-off-by: de Wouters de Bouchout Jean-Benoît (jbw) <jbw@odoo.com>
Before this commit, when high number of partners were loaded in the POS,
searching for a partner was slow. The main issue was that all of the
filtered partners based on the search query were being rendered, while
in reality, if a query returns lots of results, the search query is not
refined enough and the user is likely to type more characters to narrow
down the search. So in this commit, we limit the number of rendered
partners to 200, which is a reasonable number of results to display and
does not cause performance issues.

Moreover, the debounce time of the search input has been increased from
100ms to 500ms to further reduce the number of times the search function
is called while the user is typing.

opw-6215958

closes odoo#264300

Signed-off-by: Adrien Guilliams (adgu) <adgu@odoo.com>
Steps to reproduce
1. Install hr_holidays and hr_contract.
2. Take a demo employee currently on a validated time off covering now.
3. Set the employee's running contract Working Schedule to empty
   (fully flexible).
4. Run the "HR Contract: update state" cron (or otherwise leave the
   contract in state open / close, or draft + kanban Ready).
5. Open the Employees app or any view rendering that employee's
   chatter / follower panel.

Issue
A traceback "Expected singleton: resource.calendar()" is raised. If
the employee's chatter is open, the failing compute is re-fired on
every store refresh and the UI becomes unusable.

For fully flexible contracts, hr.contract has no resource_calendar_id,
so _get_calendar_periods appends period tuples with an empty calendar
recordset (https://github.com/odoo/odoo/blob/c6d9fa5873eb759846e9be5b66eedb8b00c5ac11/addons/hr_contract/models/hr_employee.py#L184).
_get_first_working_interval then forwarded that empty recordset to
_work_intervals_batch, which calls ensure_one() and crashes.

This regressed when _compute_leave_status was switched from a single
calendar lookup to per-period iteration in
odoo@bf73b63
which did not consider that a period in the chain can come from a contract with
no calendar.

Solution
Fall back to the company's calendar when the period's calendar is
empty, matching the guard already present from saas-18.4 onwards in
odoo@21f18b1.

opw-6168264

closes odoo#261995

Signed-off-by: Amr Ahmed (amahm) <amahm@odoo.com>
**Problem:**
When a combo product has a price but all of its combo components have a
zero list price, the quotation shows the combo's price twice: once on
the combo line itself and once on the last combo item line.

**Steps to reproduce:**
1. Create a combo product with a non-zero price and two or more combo
   groups whose component products have a zero list price.
2. Create a sale order, add the combo, pick one item per group.
3. Look at the quotation/order: the combo line total and the last
   combo-item line both show the full combo price.

**Current behavior:**
The full combo price ends up on the last combo item line; the other
combo items show 0. The combo line then displays the same total via
`_get_combo_totals`, so the same amount appears twice.

**Expected behavior:**
The combo's price is spread across its combo items so no single line
duplicates the combo total.

**Cause of the issue:**
`_get_combo_item_display_price` prorates the combo price by each
combo's base price. When every base price is 0, every prorated price
is 0, so `combo_price_delta` equals the full combo price and is added
to the last combo as a rounding correction — concentrating the whole
price there instead of spreading it.

**Fix:**
Treat an all-zero base case as "no proration signal" and split the
combo price evenly across combos before the delta adjustment runs.
The delta correction then only handles rounding, as intended.

opw-6217945

closes odoo#265010

Signed-off-by: Andrea Grazioso (agr) <agr@odoo.com>
Issue is really similar commit 21877c0
except here the compensations amls are created in
_stock_account_prepare_anglo_saxon_in_lines_vals
because of a difference between bill price and product cost

**Steps to reproduce:**
- create storable product with category standard auto
- on the category, set an account in the field
'price difference account'
- set a cost of 10
- set a purchase tax
- confirm a PO for 1 @ 15 and validate receipt
- create bill, set a date, save
- on the Bill set the total tax at 100 (it's bellow
'untaxed amount' on the bottom right of the bill and
should be 2.25 before you change it, if the tax is 15%)
- confirm the bill

**Current behavior:**
tax was reset to 2.25

**Expected behavior:**
It should stay 100 as it was manually set

**Cause of the issue:**
The total tax amount is computed based on the tax lines
in Journal Items
https://github.com/odoo/odoo/blob/2744396733bb3ad60813e9e093d67192c0d38b36/addons/account/models/account_move.py#L1171
So the problem is actually that a recomputation of the
balance of the tax account.move.line (the one with the
account "tax paid" in journal items) is triggered when
we confirm the Bill.

That's because:
 When we confirm the bill,
_stock_account_prepare_anglo_saxon_in_lines_vals() creates
two amls :
- one debiting 5 on the account set in the field 'price
difference account'
- one crediting 5 in the stock interim received account
(This makes sense and is there to realign with the fact
that, on the account move linked to the svl, the amount
credited from stock interim received is rightfully 10
because that's cost of the product and it's a standard
price product)

When we create those amls from,  the create method
from account.move.lines calls super() inside a context
manager calling _sync_dynamic_lines().
https://github.com/odoo/odoo/blob/5583cbcebae00d8122dce5ce929b650929966a90/addons/account/models/account_move_line.py#L1628-L1635
the yield of sync_dynamic_lines() is inside a context
manager calling _sync_tax_lines.
https://github.com/odoo/odoo/blob/5583cbcebae00d8122dce5ce929b650929966a90/addons/account/models/account_move.py#L3250
Therefore, the first half of sync_tax_lines() (untill
the yield) is ran before the call to super and the rest
(from the yield) is ran after the call to super.
Because we added two lines in the account.move,
get_changed_lines will return those 2 new line and
because there is a tax_ids on the new lines round_from_tax
will be False.
https://github.com/odoo/odoo/blob/5583cbcebae00d8122dce5ce929b650929966a90/addons/account/models/account_move.py#L3034-L3041
Therefore we won't reach continue.
https://github.com/odoo/odoo/blob/73d73c5c6606e0b34c754bfc4de035840951dd3b/addons/account/models/account_move.py#L3055-L3059
And the tax line will be recomputed using
_prepare_tax_line()
https://github.com/odoo/odoo/blob/73d73c5c6606e0b34c754bfc4de035840951dd3b/addons/account/models/account_move.py#L3065

Here is why there is a tax_ids on the new lines :
The field is precompute so if we don't set a value for
it, _compute_tax_ids will be ran to compute it.
As the account move on which the lines are added
is a bill, the tax_ids will the supplier_tax_id of the product.
https://github.com/odoo/odoo/blob/73d73c5c6606e0b34c754bfc4de035840951dd3b/addons/account/models/account_move_line.py#L898-L901

**fix**
There is no need for a tax_ids on these lines as they
are not meant to (and should'nt) impact the taxes.

opw-6014710

closes odoo#263306

Signed-off-by: Adrien Widart (awt) <awt@odoo.com>
This [related PR] introduced a compression pass after calls to
mergePage(). However in newer versions of pypdf (>=3.5.2),
compress_content_streams() can only be called on pages of PdfWriter. An
error would be raised when called on pages of a PdfReader.

Steps to reproduce
-----
1. Run Odoo with pypdf>=3.5.2
2. Sign and download a document in the Sign app
3. Traceback occurs

Fix
----
This commit moves the compression to the writer object, after the merged
page has been added.

Related pr: odoo#261879

runbot-937761

closes odoo#265604

X-original-commit: 4036a8e
Related: odoo/enterprise#117931
Signed-off-by: Lou Habert (loha) <loha@odoo.com>
Signed-off-by: Junqi Wu (juwu) <juwu@odoo.com>
Issue:
-----------------------------------
At certain times of the day (e.g., around midnight UTC), the test would fail
deterministically
```
test_allocation_stats_with_duplicate_leave_type_names
    self.assertEqual(leave_type_no_comp.with_context(employee_id=employee_id).max_leaves, 10)
AssertionError: 0.0 != 10
```

Cause:
-----------------------------------
This occurred due to a timezone mismatch during the test execution. When
creating the `hr.leave.allocation`, `date_from` implicitly defaults to
`fields.Date.context_today(self)` (which evaluates the date based on the test
user's timezone, e.g., Europe/Brussels). However, the `max_leaves` computation
in `hr.leave.type` evaluates valid allocations using `fields.Date.today()` as
the target date (which strictly evaluates to the UTC date)

At certain times of day, this caused the allocation's `date_from` to evaluate to
'tomorrow' relative to the UTC `target_date`. Because the allocation was
technically in the future relative to UTC, it was skipped during the computation
causing `max_leaves` to return 0.0 instead of 10.

Solution:
-----------------------------------
Explicitly define `'date_from': date.today()` when creating the allocation in
the test case. This perfectly aligns the allocation's starting date with the
strict UTC evaluation used by the `max_leaves` computation under the hood.

Runbot Error: [937759](https://runbot.odoo.com/odoo/runbot.build.error/937759)
Related PR: odoo#261680

closes odoo#265703

Signed-off-by: Mélanie Peyrat (mepe) <mepe@odoo.com>
…m view

* Go to Timesheets > My Timesheets > switch to Grid view.
* Hover over a cell with a timesheet entry and click the magnifier (search) icon.
* The list opens; click a record to open its form view.
* Observe the URL: `/odoo/timesheets/account.analytic.line/<id>`.
* Refresh the page (F5).

Before this commit, the generic form view was shown instead of the
timesheet-specific form view. This occurred because, when reloading a
page with a dynamic action and a resId, a generic view layout [false, "form"]
was requested instead of the action-defined view.

Now, the dynamic action is properly restored on refresh, ensuring the
correct specific view is loaded for the form.

opw-6133602

closes odoo#265552

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
When applying a global discount, the amount of fixed taxe was reduced
by that disocunt. This is not correct for fixed amount taxes.

Steps to reproduce:
-------------------
* Enable global discount
* Configure a fixed-amount tax and a regular percentage tax (e.g. VAT) on the same product.
* In POS, add this product to an order.
* Apply a global discount

> Observation:
Total is not correct.

Why the fix:
------------
Only taxes that accountTaxHelpers.can_be_discounted allows (mirroring account.tax._can_be_discounted,
i.e. not fixed or code) are linked on the global discount line.
The discount product line is excluded from lines considered for the global discount base.

opw-6138507

closes odoo#261546

Signed-off-by: Stéphane Vanmeerhaeghe (stva) <stva@odoo.com>
… is missing

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

closes odoo#258605

Signed-off-by: Stéphane Vanmeerhaeghe (stva) <stva@odoo.com>
When exporting a number N of records as XLSX or CSV file, we
call the export_data() method for the N records at the same time.
This method prefetches the selected fields for all the records which
can lead to memory limit errors when N is too large.

We propose to batch this call and invalidate the recordsets
between batches.

Benchmarks
-----------

Execution time:

| No records | Before PR | After PR |
|------------|-----------|----------|
|     70 260 |    3.82 s |   3.94 s |
|    228 116 |   18.71 s |  19.36 s |
|    394 381 |   31.02 s |  32.67 s |

Memory usage:

| No records | Before PR | After PR |
|------------|-----------|----------|
|     70 260 |  316.0 MB | 273.5 MB |
|    228 116 |  796.9 MB | 620.8 MB |
|    394 381 |    1.3 GB | 947.7 MB |

opw-5881026

closes odoo#257333

Signed-off-by: Piryns Victor (pivi) <pivi@odoo.com>
Backport of:
- odoo@1dac720
- odoo@7e21cb9

Steps to reproduce:
- Create a product P1 with the Follwing BoM:
    - 1 component C1 (10 units)
    - 1 by-product BP1 (10 units)

- Create a Manufacturing Order for 1 unit of P1 and confirm it
- Set quantity to 5 units for C1 and 5 units for BP1
- Set Consumed = True for C1 and Produced = True for BP1
- Validate the MO

Problem:
C1 is correctly validated with 5 units, but BP1 is reset to 10 units

_onchange_quantity only checks raw_material_production_id to set
`manual_consumption = True`, so by-products (which use production_id)
never get `manual_consumption` set. As a result, `_set_qty_producing`
does not skip the by-product move and resets its quantity to the
product_uom_qty on validation.

opw-6221482

closes odoo#265276

Signed-off-by: William Henrotin (whe) <whe@odoo.com>
YoussefM890 and others added 24 commits June 11, 2026 13:35
**Problem:**
On the /shop page, the "Fill" option in the web editor (cover/contain
toggle on product card images) appears clickable but has no visible
effect on the product thumbnails.

**Steps to reproduce:**
1. Install website_sale and open /shop.
2. Open the web editor and select the shop page.
3. Locate the "Fill" button group in the right panel (with the two
   svg icons).
4. Click the alternate option to switch between cover and contain.
5. Observe that the product card thumbnails do not change appearance.

**Current behavior:**
The toggle flips the `o_wsale_context_thumb_cover` class on the products
table (and the activation of the `products_thumb_cover` view), but the
product images keep rendering with `object-fit: contain` regardless of
the toggle state.

**Expected behavior:**
The image fill mode follows the toggle:
- "cover" option active   -> product image uses `object-fit: cover`
- "cover" option inactive -> product image uses `object-fit: contain`

**Cause of the issue:**
The product image template renders the img with the `object-fit-contain`
utility class. Starting with Bootstrap 5.3, that class is shipped by
Bootstrap itself as
`.object-fit-contain { object-fit: contain !important; }`. The CSS
variable `--o-wsale-card-thumb-fill-mode` (set to `cover` by
`.o_wsale_context_thumb_cover`) does cascade down to the img, but the
variable-driven rule
`img { object-fit: var(--o-wsale-card-thumb-fill-mode, contain); }` does
not carry `!important`, so Bootstrap's utility class always wins and the
toggle becomes inert.

**Fix:**
Marking the variable-driven `object-fit` declaration as `!important`
restores the priority contest with Bootstrap's utility class to a pure
specificity comparison. The nested selector
`.oe_product_cart .oe_product_image .oe_product_image_link
.oe_product_image_img_wrapper img` has higher specificity than the
single-class `.object-fit-contain`, so the variable-driven rule wins and
the toggle takes effect again. The change is confined to a single SCSS
line; no templates touched, no class semantics altered.

opw-6231432

closes odoo#268302

Signed-off-by: Youssef Maaouia (yoma) <yoma@odoo.com>
When refunding an order that has already been partially refunded, the
line amount and total amount where incorrect. They would be the total
amount of the original order.

Steps to reproduce:
-------------------
* Open PoS and make an order with 3 quantity of a product.
* Close the session
* In the backend, refund 1 quantity of the order and validate the refund
* Refund again the same order with the 2 remaining quantities
> Observation: The total amount and line amount are not correct

opw-6215019

closes odoo#269124

X-original-commit: ffebce1
Signed-off-by: Adrien Guilliams (adgu) <adgu@odoo.com>
Signed-off-by: Robin Engels (roen) <roen@odoo.com>
The configuration option `registry_lru_size` does not exist and does not
work at all in recent versions.

Defining odoo-specific environment variables to handle:
- ODOO_REGISTRY_LRU_SIZE: the default registries size
- ODOO_REGISTRY_LRU_SIZE_CRON: overwrite for cron workers

Cron workers have often a different workload than HTTP workers and we
may set a different limit there. If the limit is lower than the number
of databases, a cron job will not reuse registries because it cycles
through all known ones - in such cases, we can set a lower limit to
keep the memory lower.

closes odoo#269374

X-original-commit: 7ec7048
Signed-off-by: Raphael Collet <rco@odoo.com>
Signed-off-by: Krzysztof Magusiak (krma) <krma@odoo.com>
The media list snippet forces the image to fill the height of its row. The
image column carries align-self-stretch and the image carries h-100, so when
the text next to the image is longer than the image is tall, the row grows to
fit the text and the image is stretched to that height (and cropped through
object-fit: cover). The longer the text, the more the image is distorted.

Drop h-100 from the image and align-self-stretch from its column in the
s_media_list snippet and in the mass_mailing_themes templates that reuse it.
With no forced height the image keeps its natural aspect ratio and the row
height follows its content, so the image is laid out next to the text instead
of being stretched to match it.

Steps to reproduce:
1. Open Email Marketing and create a new mailing.
2. Select the Blogging template for the mail body.
3. In a media item, replace the text next to an image with a very long paragraph.

=> The image is stretched and cropped to match the height of the text.

Ticket [link](https://www.odoo.com/odoo/project.task/5117571)
opw-5117571

closes odoo#268533

X-original-commit: dd6b007
Signed-off-by: Damien Abeloos (abd) <abd@odoo.com>
In the combo configurator dialog, a combo item's `extra_price` and the
`price_extra` of `no_variant` attributes are stored in the company
currency but were serialized to the front-end without conversion. With a
foreign-currency pricelist, the popup added them 1-to-1 to the
already-converted base price, so the displayed total didn't match the
price computed on the sale order line.

Convert both extras via `currency._convert()` before sending them to the
front-end, so the popup matches the order line.

Closes odoo#119701

closes odoo#269386

Signed-off-by: Louis Tinel (loti) <loti@odoo.com>
We iniatially though the 2FA was needed by the administration.
But in fact, it was not. So we will remove it.

Commit of the 2FA:
odoo@7535ce7

Commit of the reregister also changed a bit that
odoo@22ba629

no task id

closes odoo#269597

Signed-off-by: Sven Führ (svfu) <svfu@odoo.com>
This commit is a backport of [1] and [2] which fix non deterministic
one2many tests involving a many2one.

[1] odoo#266344
[2] odoo#256582

runbot error~243512

closes odoo#269739

Signed-off-by: Jorge Pinna Puissant (jpp) <jpp@odoo.com>
Before this commit, user needed to manually refresh de kyc status,
with this commit, the status will be changed when receiving the
notification from IAP

task-6271596

closes odoo#268528

Signed-off-by: Florian Gilbert (flg) <flg@odoo.com>
Version:
----------
-18.0+

Steps to reproduce:
-------------------
- Install `stock` module
- Enable `Storage Locations` from Inventory settings
- Create a tracked storable product with on-hand 8 units in `Shelf 1`
- Create Delivery 1 for 5 units and click `Mark as To Do`
- Create Delivery 2 for 5 units and click `Mark as To Do`
- Verify reservations:
  - Delivery 1 reserves 5 units
  - Delivery 2 reserves remaining 3 units
- Relocate all 8 units from `Shelf 1` to `Shelf 2` using the `Relocate` action
- Reopen both deliveries

Issue:
------
After relocating stock between internal locations, reservations are reassigned
in the wrong order:
- Delivery 2 becomes fully reserved with 5 units
- Delivery 1 is reduced to 3 reserved units

This incorrectly swaps the original reservation priority between deliveries.

Cause:
------
The relocation wizard starts from:
`stock.quant.relocate.action_relocate_quants()`
which calls `move_quants()`:
https://github.com/odoo/odoo/blob/d3eebbd1c27e8a039bb55cdf2a82d464e06ffa8c/addons/stock/wizard/stock_quant_relocate.py#L70

`move_quants()` validates an internal stock move through `_action_done()`:
https://github.com/odoo/odoo/blob/d3eebbd1c27e8a039bb55cdf2a82d464e06ffa8c/addons/stock/models/stock_quant.py#L1572

During validation, `_synchronize_quant()` moves the stock quantity from
`Shelf 1` to `Shelf 2`.

However, the already reserved delivery move lines still reference `Shelf 1`.
This temporarily makes the source quant negative (`available_qty < 0`),
triggering `_free_reservation()`:
https://github.com/odoo/odoo/blob/d3eebbd1c27e8a039bb55cdf2a82d464e06ffa8c/addons/stock/models/stock_move_line.py#L697-L700

Inside `_free_reservation()`, move lines are ordered using
`current_picking_first`:
https://github.com/odoo/odoo/blob/d3eebbd1c27e8a039bb55cdf2a82d464e06ffa8c/addons/stock/models/stock_move_line.py#L816-L821

Since both deliveries share the same scheduled date, the fallback ordering
uses `-cand.id`, causing Delivery 2 (higher id) to be processed before
Delivery 1 (lower id).

The reservation cleanup therefore happens in this order:
- Remove Delivery 2 reservation (3 qty)
- Remove Delivery 1 reservation (5 qty)

The corresponding moves are then added to `move_to_reassign`
in the same order:
`[Delivery 2, Delivery 1]`
https://github.com/odoo/odoo/blob/d3eebbd1c27e8a039bb55cdf2a82d464e06ffa8c/addons/stock/models/stock_move_line.py#L849

Later, `move_to_reassign._action_assign()` processes the moves in recordset
order:
- Delivery 2 reserves 5 units first
- Delivery 1 only gets the remaining 3 units

As a result, reservation priority is unintentionally reversed after relocation.

Fix:
----
Before calling `_action_assign()`, reverse `move_to_reassign`.

This ensures reassignment preserves the original reservation order:
- Delivery 1 is reassigned first and recovers 5 units
- Delivery 2 receives the remaining 3 units

The reservation state therefore remains consistent before and after
internal stock relocation.

opw-6218256

closes odoo#265169

Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com>
This commit ensures product images follow their visual order in the
product image viewer when using the grid layout.

Steps to reproduce:
- Open a product page with multiple images (or add Extra Media to the
  product)
- Change layout mode to "Grid" and click save
- Click any image to open the product image viewer
- Navigate between images

Images do not follow the visual left-to-right order. This regression was
introduced by commit odoo@9a3628b, which replaced the row-based grid with a
column-first layout. As a result, `querySelectorAll` returns images in
DOM order, which no longer matches the visual order.

To fix this, images are now reordered based on their visual placement in
the grid so navigation matches the order seen by the user. Images are
traversed in visual left-to-right order while also accounting for
varying image heights and multi-column alignment.

task-4364143

closes odoo#254077

Signed-off-by: Francois Georis (fge) <fge@odoo.com>
Steps to reproduce:
- Go to the Link Tracker page
- Generate a first tracked link
- Click on the button to start editing the code
- Click on "create another tracker"
- Generate a second tracked link
=> When you access the screen to see/edit the tracked link url, the
buttons "ok" and "cancel" are already present. Clicking on "ok" display
a traceback.

To fix this issue, this commit also cancels edition when clicking on
"create another tracker".

task-4531974

closes odoo#268573

Signed-off-by: Francois Georis (fge) <fge@odoo.com>
This fix removes the condition that moves must be sent to be part of a flow 10 and correct copy-pasted zip by country_id in address check.

closes odoo#268022

Signed-off-by: de Wouters de Bouchout Jean-Benoît (jbw) <jbw@odoo.com>
When we download the ETA invoice PDF, a JSONDecoderError
can happen when calling the json() method on the request.
This error is properly caught by Odoo :

https://github.com/odoo/odoo/blame/7a9a340e0dbac470c4bea3f8ce8a32e55f3e82e6/addons/l10n_eg_edi_eta/models/account_edi_format.py#L58-L60

However, the following commit introduced a monkeypatch
to handle errors when the simplejson library is
installed : 2435fe7

If we meet the conditions, the original error is replaced
by a json.JSONDecodeError which is not caught during the
previous process.

We propose to add this error to the catch block.

This modification was inspired by the commit
d483dac.

opw-6266862

closes odoo#269296

X-original-commit: 28ee5d3
Signed-off-by: Maximilien La Barre (malb) <malb@odoo.com>
**Problem:**
Valuation account for luxembourg is currently 60761 Merchandise
which is incorrect because it's an expense account.
We should rather use a current asset account like
301 Inventories of raw materials

**Steps to reproduce on a fresh db:**
- create a new db with modules stock_account and accountant
(without demo data)
- On the 'fiscal localization setting' set the package
as 'Luxembourg' and save
- ativate the automatic accounting setting

**Current Behavior:**
The 'stock valuation account' appearing below
the automatic accounting setting is :
60761 Merchandise

**Expected behaviour:**
It should be 301 Inventories of raw materials

closes odoo#269469

Signed-off-by: Yolann Sabaux (yosa) <yosa@odoo.com>
…emo mode

**Steps to reproduce:**
* Install `l10n_fr_pdp` module.
* Activate French eInvoicing in demo mode and enable
  "Participate in the pilot phase".
* Create an invoice for a French client and send it via the French E-Invoicing.

**Observed behavior:**
* A traceback is raised with `KeyError: 'messages'` in
  `_send_peppol_documents`.

**Cause:**
* `DEMO_ENDPOINTS['send_document']` in `l10n_fr_pdp` was returning
  `{'ppf_messages': [...]}`, missing the `messages` key that
  `_send_peppol_documents` in `account_peppol` unconditionally reads for flow 2.
* Additionally, the demo mock was returning `uid` instead of `uuid` inside
  `ppf_messages`, which does not match the real IAP response structure.
* Finally, in `l10n_fr_pdp/models/pdp_flow.py`, after successfully sending a
  flow 10 batch, the system attempted to log `response['uid']` despite the
  `_send_to_proxy()` method returning `uuid`. This caused a crash during the
  chatter logging step.

**Fix:**
* Fix `DEMO_ENDPOINTS['send_document']` to return
  `{'messages': [...]}` with `message_uuid` entries, matching the
  real IAP response structure for flow 2.
* Update `ppf_messages` in the demo mock to return `uuid` instead of `uid`.
* Fix `pdp_flow.py` to correctly access `response['uuid']` instead of
  `response['uid']` when posting the success message.

opw-6289723

closes odoo#269515

Signed-off-by: Wala Gauthier (gawa) <gawa@odoo.com>
**Description of the issue/feature this PR addresses:**
The method _get_ab_testing_siblings_mailings currently scans all mailings in a campaign to apply a simple filter, which becomes expensive on databases with many large mailings.

**Steps to reproduce bug:**
1) Run this script to get [enough sufficiently large mailings](https://gist.github.com/brcut-odoo/bb0d6d334bfe110afe16021d17d1b443)
2) Open one of the mailings and recieve a crash from the _get_ab_testing_siblings_mailings

**Current behavior before PR**
https://drive.google.com/file/d/19xftvzsGSQ9DxB67LNiLkKApzsD192ax/view?usp=drive_link

**Current behavior after PR**
https://drive.google.com/file/d/1apTJ0rWTKaATYa67ZmmN-7bKhrw4KuTx/view?usp=drive_link

opw-6245908

closes odoo#269738

X-original-commit: 4a52702
Signed-off-by: Damien Abeloos (abd) <abd@odoo.com>
Signed-off-by: Brett Cutler (brcut) <brcut@odoo.com>
…ring rule

Version:
----------
- 18.0+

Steps to reproduce:
-----------------------
1- Install the `purchase` and `stock` modules.
2- Create a storable product with tracking enabled.
Set the Cost (standard price) to 50.
3- Open the product form and go to the Purchase tab.
   * Add a vendor with:
   * Quantity: 2
   * Price: 10
4- Create a Reordering Rule for this product:
   * Route: Buy
   * Trigger: Manual
   * To Order Quantity: 2
5- Click on the Order button to generate a purchase order.
6- Open the generated Purchase Order and verify the Unit Price
on the purchase order line.
7- Open the same product and go to the Purchase tab.
In the existing vendor line, add an End Date lower than today so
the vendor pricelist becomes expired.
8- Reopen the same reordering rule.
Change To Order Quantity to 1.
9- Click on the Order button again.

Issue:
-----
The generated purchase order line gets a Unit Price of 0
instead of keeping the product cost or a valid fallback price.

Root Cause:
--------------
- When clicking on `Order`, it triggers `action_replenish`, which calls the
procurement flow:
`_procure_orderpoint_confirm` → `run` → `run` → `_run_buy`.

- Inside `_run_buy`, the system checks whether a `purchase.order.line`
already exists. In this case, the PO line exists, so it calls
 `_update_purchase_order_line`.

https://github.com/odoo/odoo/blob/47ef8b75d0c90001b9989a95f09b962c5b286c53/addons/purchase_stock/models/stock_rule.py#L137

- In `_update_purchase_order_line`, the system tries to fetch a seller using
 `_select_seller`,
- which internally calls `_get_filtered_sellers`.
https://github.com/odoo/odoo/blob/47ef8b75d0c90001b9989a95f09b962c5b286c53/addons/product/models/product_product.py#L759

- However, if the seller's `end_date` is less than `today`,
 `_get_filtered_sellers` skips that seller and returns no valid seller.

https://github.com/odoo/odoo/blob/47ef8b75d0c90001b9989a95f09b962c5b286c53/addons/product/models/product_product.py#L731-L733

- As a result, `_update_purchase_order_line` does not find any seller and falls
back to setting `price_unit` to `0`, causing the purchase order line price to
be updated incorrectly.

https://github.com/odoo/odoo/blob/47ef8b75d0c90001b9989a95f09b962c5b286c53/addons/purchase_stock/models/stock_rule.py#L259

opw-6117461

closes odoo#262396

Signed-off-by: William Henrotin (whe) <whe@odoo.com>
**Problem:**
When creating a new employee, the future timesheets due to public holidays are
computed. If the number of public holidays is large (i.e. if the user creates
them for each year, several years in the future), then it takes excessively
long and the action may not complete.

**Cause:**
The pytz method `localize` and comparing times with non-static timezones is
done repeatedly and unnecessarily which becomes costly with more records.

**Solution:**
Only localize the time when absolutely necessary (determining the date of the
leave in the calendar timezone).

**Performance Stats:***
|Record count|Time before|Queries before|Time after|Queries after|
|------------|-----------|--------------|----------|-------------|
|100         |3.1s       |393           |0.8s      |117          |
|1,000       |22.3s      |2,090         |1.5s      |183          |
|10,000      |Timeout    |N/A           |6.7s      |541          |

opw-6087422

closes odoo#269844

X-original-commit: 5eeb67a
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
Signed-off-by: Dirk Douglas (dido) <dido@odoo.com>
The new collected_values UBL import flow sets product_uom_id from the
XML unitCode without checking that the resolved UoM category matches the
matched product's UoM category. When they diverge, writing the line
triggers the incompatible error.

Steps to reproduce:
- Create a product "XYZ" with UoM "Units" (category "Unit").
- Import a Peppol UBL bill whose line has Item/Name "XYZ" and
unitCode="MTK" (uom_square_meter, "Surface").
- Import fails with: "The Unit of Measure (UoM) 'm²' you have selected
for product 'XYZ', is incompatible with its category : Unit."

This fix will avoid setting the product_uom_id when the UoM category
doesn't match the product's UoM category, allowing the line to be
imported without error. The user can then manually set the correct UoM
after import.

opw-6121714

closes odoo#269714

Signed-off-by: Claire Bretton (clbr) <clbr@odoo.com>
Accounting users can access POS closing journal entries even when they
do not have Point of Sale access rights.

The PDP POS helper checked POS session/order links directly while
computing e-reporting fields on account moves. This could raise an
access error on `pos.session` for accounting users without POS rights.

closes odoo#269815

Signed-off-by: de Wouters de Bouchout Jean-Benoît (jbw) <jbw@odoo.com>
@viinbot

viinbot commented Jun 15, 2026

Copy link
Copy Markdown

@royle-vietnam Viindoo Test Suite has failed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.