Tuesday, September 1, 2026
29 changes · master
Resolved issues and error corrections
The Guatemala electronic invoicing localization now uses the updated tax calculation for regular gasoline that contains 10% alcohol. This matters because, from August 22, only 90% of the gallons are taxable under the new government rule for new customer chart of accounts setup.
Original PR description
Starting August 22nd Regular Gasoline is changing to a version that contains 10% alcohol. Because of this, the government is only taxing 90% of the gallons since the 10% alcohol portion is exempt. This will update COA for new customers, any current dbs who need to use the new formula can manually update theirs to include the * 0.9 portion. task-6483303 Forward-Port-Of: odoo/enterprise#129483 Forward-Port-Of: odoo/enterprise#129412
Fixed an issue where timesheets could lose their connection to a replacement invoice after an invoice was reversed and recreated through a credit note. This helps ensure corrected invoices still show the right timesheet details for the related sales order, reducing billing confusion and manual cleanup.
Original PR description
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior…
### Description of the issue/feature this PR addresses: Fixes an issue where timesheets lose their invoice reference when reversing and re-creating an invoice via a credit note. ### Current behavior before PR: When reversing an invoice tied to timesheets and creating a replacement via a credit note, the timesheets linked to the original invoice have their timesheet_invoice_id cleared. Because the modify_moves function builds the replacement invoice directly via copy_data()/create(), it bypasses the normal sale order invoicing flow. As a result, the unbilled timesheets are left permanently unlinked from the newly created invoice, leaving the new invoice with no reference to the timesheets linked to the original sale order. ### Desired behavior after PR is merged: When a replacement invoice is created, each timesheet is properly relinked to the corresponding line on the new invoice. This linkage matches on the sale order line (so_line) rather than line position, ensuring accuracy since line order and count are not guaranteed to be preserved between the original and modified invoices. opw-6449995 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285232 Forward-Port-Of: odoo/odoo#283104
This fixes how a stock-related field is defined so Odoo can create its supporting database table consistently. It helps prevent upgrade failures for customers whose databases already contain a table with the conflicting name.
Original PR description
The `move_line_ids` field has been a computed Many2many field since its introduction. Starting from V19.0, it was changed to a regular Many2many field…
The `move_line_ids` field has been a computed Many2many field since its introduction. Starting from V19.0, it was changed to a regular Many2many field [Here](https://github.com/odoo/odoo/pull/171743).
The current field definition incorrectly passes the second positional argument as the [relation](https://github.com/odoo/odoo/blob/19.0/odoo/orm/fields_relational.py#L1255) table name. Since the relation name starts with an uppercase character, as the table store as "Products" In addition, the relation table is mistkanley defined (not sure) instead of letting the ORM generate the relation table.
Update the field definition to use the correct field parameters and let the ORM handle the relation table creation.
Issue:
- The standard convention for automatically generated Many2many relation tables is to use a `_rel` suffix.
- Some databases already have a custom relation table with the same table name Products as the one explicitly defined by this field. When upgrading such databases, the ORM attempts to create the conflicting relation table, which can cause the migration to fail.
This change avoids the conflict and makes the field definition consistent with the expected ORM behavior.
current behaviour
```
labo_19_2=# \d "Products"
Table "public.Products"
Column | Type | Collation | Nullable | Default
------------------------------+---------+-----------+----------+---------
stock_package_destination_id | integer | | not null |
stock_move_line_id | integer | | not null |
Indexes:
"Products_pkey" PRIMARY KEY, btree (stock_package_destination_id, stock_move_line_id)
"Products_stock_move_line_id_stock_package_destination_id_idx" btree (stock_move_line_id, stock_package_destination_id)
Foreign-key constraints:
"Products_stock_move_line_id_fkey" FOREIGN KEY (stock_move_line_id) REFERENCES stock_move_line(id) ON DELETE CASCADE
"Products_stock_package_destination_id_fkey" FOREIGN KEY (stock_package_destination_id) REFERENCES stock_package_destination(id) ON DELETE CASCADE
```
After fix
```
labo_19_2=# \d stock_move_line_stock_package_destination_rel
Table "public.stock_move_line_stock_package_destination_rel"
Column | Type | Collation | Nullable | Default
------------------------------+---------+-----------+----------+---------
stock_package_destination_id | integer | | not null |
stock_move_line_id | integer | | not null |
Indexes:
"stock_move_line_stock_package_destination_rel_pkey" PRIMARY KEY, btree (stock_package_destination_id, stock_move_line_id)
"stock_move_line_stock_package_stock_move_line_id_stock_pack_idx" btree (stock_move_line_id, stock_package_destination_id)
Foreign-key constraints:
"stock_move_line_stock_package_destinati_stock_move_line_id_fkey" FOREIGN KEY (stock_move_line_id) REFERENCES stock_move_line(id) ON DELETE CASCADE
"stock_move_line_stock_package_stock_package_destination_id_fkey" FOREIGN KEY (stock_package_destination_id) REFERENCES stock_package_destination(id) ON DELETE CASCADE
```
Ticket: 6485470
Upgrade pr :-https://github.com/odoo/upgrade/pull/11189
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-prOdoo now treats common placeholder VAT values such as '/', 'NA', and 'na' as missing VAT numbers instead of valid entries. This improves accuracy in accounting, e-invoicing, and localization workflows, while also optimizing VAT lookup performance for large databases.
Odoo now treats common placeholder VAT entries such as '/', 'NA', and 'na' as missing VAT values, rather than accepting them as valid. This helps reports, tax filings, payroll, and electronic document flows make more accurate decisions when customer or company VAT information is incomplete.
Users can now generate lot or serial numbers from the Barcode app on receipts without the screen failing. This restores the expected workflow for receiving tracked products and prevents an error that blocked warehouse operations.
Original PR description
Issue: ------------------------------------------ Clicking the `+` button on a receipt in Barcode for a lot/serial tracked product gives traceback instead of opening the lot/serial generation dialog.…
Issue: ------------------------------------------ Clicking the `+` button on a receipt in Barcode for a lot/serial tracked product gives traceback instead of opening the lot/serial generation dialog. Steps to reproduce: ------------------------------------------ 1. Install `stock_barcode`. 2. Create product P1 with serial tracking. 3. Create receipt for P1. 4. Go to barcode and click on `+` button to generate serial number. 5. Traceback: `Cannot read properties of undefined (reading 'getRecord')` Cause of the issue: ------------------------------------------ `LineComponent.openDialog()` was trying to access `this.cache.getRecord(...)`, but `LineComponent` does not define a cache property. This issue was introduced by PR https://github.com/odoo/enterprise/pull/129521, which fixed a similar traceback when generating serial/lot numbers from Barcode for manufacturing orders. Solution: ------------------------------------------ Use `this.env.model.cache.getRecord(...)` when resolving the move before opening the dialog. This avoids the undefined access and allows the serial/lot generation dialog to open normally.
Fixed an accounting issue where bank reconciliation could hide a vendor bill reference when exchange rate differences were involved. This helps users verify reconciled foreign-currency payments more clearly and reduces confusion during review.
Original PR description
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have…
In the bank reconciliation widget, a reconciled transaction matched with a bill and a loss exchange entry no longer displays the bill reference next to the reconciled line. Steps to reproduce: - Have company currency USD and foreign currency EUR - Create a vendor bill in foreign currency (100 EUR, rate 1 USD = 1 EUR) - Create a bank transaction in the foreign currency for less than the bill total, at a rate making the amount higher in company currency (90 EUR, 108 USD at 1 EUR = 1.2 USD) - Open the bank reconciliation widget, reconcile transaction and bill - Unfold the reconciled transaction Issue: A bill reference is missing Analysis: The exchange difference line is reconciled with the transaction line. Being reconciled with two lines (bill and exch), the widget relies on `*_reconciled_lines_excluding_exchange_diff` to show the fields. However, `count_reconciled_lines_excluding_exchange_diff` has been defined as boolean instead of int, faulting the comparison. opw-6479015 Forward-Port-Of: odoo/odoo#283855
The Point of Sale variant popup now shows the truly available free quantity, excluding stock already reserved by confirmed sales orders. This prevents staff from seeing misleading stock levels when choosing product variants, reducing overselling risk and inventory confusion.
Original PR description
## Steps to reproduce: - Create another warehouse - Create a product with a variant, like Color, values black and white - Track the product, add a qty on hand of 50 on the black product - Go to the…
## Steps to reproduce: - Create another warehouse - Create a product with a variant, like Color, values black and white - Track the product, add a qty on hand of 50 on the black product - Go to the sales app, make a quotation of 50 for the black product - Confirm the quotation - Go to the PoS, click on the product, check the available qty in the popup - It is still 50, even though the forecasted is correct at 0 ## Why the fix: Having the actual free qty was added in this commit 682bc82 to be able to check the qty that was really free instead of the available qty. This means that we subtract the reserved_qty from the qty_available to get the free_qty. The variant popup was forgotten in this commit, so it was still displaying the qty_available. This is why there was a difference in the qty if we press the product normally or if we long press it, because the variant popup was forgotten in said commit. opw-6382845 Forward-Port-Of: odoo/odoo#284321 Forward-Port-Of: odoo/odoo#280330
Fixed monthly salaries in Belgian payroll are now prorated using the employee's expected hours for the full pay period, rather than a single week's hours. This prevents incorrect deductions and ensures employees are paid accurately when they work more or less than half of the period.
Original PR description
### Problem - Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against `hours_per_week` (single week) instead of 50% of the theoretical hours for the…
### Problem
- Fixed salary payslips were not being prorated correctly. The 50% rule threshold was compared against
`hours_per_week` (single week) instead of 50% of the theoretical hours for the payslip period.
This led to incorrect salary deductions in all cases and the wrong computation path being taken when less than
50% of the month was worked.
### Solution
- Fix `_l10n_be_has_enough_paid_hours` to compare paid hours against
50% of theoretical hours instead of `hours_per_week` (which are calculated for the employee's `working schedule`)
### How it works
- For fixed salary (`wage_type = 'monthly'`), the quarterly hourly rule
is applied as follows:
- **Hourly rate** = `fixed_salary × 3 / 13 / theoretical_hours`
- **50% rule**:
- If more than 50% of theoretical hours were worked → deduct absences
from fixed wage
- If less than 50% of theoretical hours were worked → pay only the
hours worked
For variable salary (`wage_type = 'hourly'`), the employee is simply
paid for the number of hours worked during the period.
Task-6260378
Forward-Port-Of: odoo/enterprise#126600The HTML editor now correctly removes formatting, such as underline, from headings inside list items. It also prevents formatting applied to a parent list item from unintentionally affecting nested list items, making document editing more predictable for users.
Original PR description
**Current behavior before PR:**
1. Create a list with h1, have some text inside.
2. Select whole heading and apply underline style.
3. Click on remove format button.
You will notice that underline style is not removed from list.
**Desired behavior after PR is merged:**
Now, format is removed correctly from the list.
task-6348753
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#273678Rental product pages now show the original price before a discount more reliably. This helps shoppers better understand savings when rental pricing uses multiple rules across the rental period.
Original PR description
As multiple rules can be applied for rental products, the default computaion of the product price before discount is not computed correctly. This commit overrides `_get_price_before_discount` for rental products to fetch the product's price without pricelist. task-6279525 See also: - https://github.com/odoo/odoo/pull/276566
This fix prevents an error when users close the email composer after selecting more than 500 CRM leads. The composer now uses the selected records from the context when the usual stored list is intentionally unavailable, avoiding a disruptive crash during bulk email workflows.
Original PR description
Steps to reproduce: 1. Install `crm` 2. Create leads more than 500. 3. Select all and try to send email 4. Not close the wizard by "X" Issue: - Traceback occurs: `Uncaught Promise > Unexpected end of JSON input` Cause: - res_ids is not set on the composer when more than 500 records are selected. This is expected, as the compute method `_compute_res_ids()` does not write `res_ids` when the number of `active_ids` exceeds 500 (to avoid storing large payloads on the field). Because of this, the code trying to JSON.parse(res_ids) fails while dismissing the wizard at `onCloseWizardModal` Solution: - Fallback to context.active_ids when res_ids is not available opw-5891862 Forward-Port-Of: odoo/odoo#252670 Forward-Port-Of: odoo/odoo#248406
Audio messages in VoIP forms now open without crashing after a recent platform change to how files are stored. This keeps users able to access and update recorded audio content normally.
Original PR description
Since odoo/odoo#266082, binary fields use an object containing their content, filename and size. The `isBinarySize` helper was also removed.
The VoIP audio binary field still imported this helper and treated binary values as raw base64 strings. Opening an audio message form therefore crashed with:
TypeError: isBinarySize is not a function
Use the new binary value structure when reading and updating audio content.This fixes an issue where search filters and groupings could stop appearing correctly in the search menu after users switched views, even though the choices were still applied. The search interface now keeps its state properly updated, making filtering behavior clearer and more reliable.
Original PR description
Importing a state, as done when switching view, replaced the search model's query and searchItems by the raw values read from that state, dropping the reactivity of both containers. The search bar menu, whose content is rendered in a popover that the parent's render does not reach, then stopped reflecting the filters and group bys toggled in it, even though they were still applied. The fix populates the existing containers in place to preserve reactivity. task-6524460
This fix stops Point of Sale restaurant orders from accidentally deleting order lines when the same order is handled across multiple devices. It helps ensure paid orders keep the correct items, totals, and audit trail after device synchronization.
Original PR description
Steps to reproduce: - Restaurant config with two POS devices on the same session - Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids) -…
Steps to reproduce:
- Restaurant config with two POS devices on the same session
- Device A: open a table, add two products, press Order (the order is synced as a draft, its lines get server ids)
- Device A: remove both lines, without syncing
- Device B: touch the same order, so device A re-reads it from the server through the synchronisation websocket
- Device A: pay and validate the order
Issue:
The order is saved as paid, with its total and its payment, but without any orderline. The lines are deleted on the server: odoo.models.unlink: deleted pos.order.line records with IDs: [...]
Cause:
Removing an orderline queues an unlink command in
models.commands['pos.order'].unlink['lines_<order id>'] (delete_ in related_models.js). That command is only discarded by clearCommands(), which syncAllOrders() calls after a successful sync, so it stays pending in between.
Any read of the order in that window puts the line back: a deleted record counts as missing in missingRecursive(), so it is fetched again and loadData() re-creates it with its server id.
serialize() then emits both the update of the live line and the still pending unlink, and sync_from_ui writes
lines: [[1, id, {...}], [3, id]]. The ORM applies commands in order, and pos.order.line.order_id is ondelete='cascade', so Command.UNLINK deletes the line right after writing it.
Fix:
Skip a removal command when the record is still linked to the parent at serialization time. A record cannot be both linked and removed in the same payload, so the pending command is stale and dropping it keeps the local state. Genuine removals, where the record is no longer linked, are still sent.
opw-6401146
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285427
Forward-Port-Of: odoo/odoo#284695This fixes an inventory issue where selecting a contact on a delivery transfer could incorrectly reset the destination from a custom customer stock location back to the generic Customers location. Businesses using tailored delivery operation types can now rely on their configured destination locations staying in place, reducing manual corrections and shipment errors.
Original PR description
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver…
### Steps to reproduce: - In the settings: Enable Storage Locations - Create a customer location "Customer stock" with "Customers" as its parent location - Create a delivery operation type "Deliver Super Customer" and set its default destination location to "Customer stock" - Go to Inventory > Overview > Deliver Super Customer > New - Set a contact on the transfer #### > The destination location switches from "Customer stock" to "Customers" ### Cause of the issue: The `location_dest_id` of `stock.picking` depends on its `partner_id`. So that changing the partner recomputes the locations of the transfer. However, as soon as the destination of the operation type has a `customer` usage, the `property_stock_customer` of the contact replaces it unconditionally: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L949-L963 However, the `property_stock_customer` falls back to an `ir.default` pointing at the default `Customers` location when nothing is set on the contact: https://github.com/odoo/odoo/blob/1c40fab04b71def8f3645c4c4bb0c1441057f307/addons/stock/data/stock_data.xml#L71-L72vs The override comes from 8a0775aa1dd9, which replaced an `elif` fallback on the contact by an "unconditional" substitution as this fallback had become unreachable once `default_location_src_id` and `default_location_dest_id` were made required: https://github.com/odoo/odoo/blob/04f3a7bca99d0144a4ea871be9625db368b196ca/addons/stock/models/stock_picking.py#L34-L41 opw-6421090 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284873 Forward-Port-Of: odoo/odoo#280016
Customers who place self-order takeout or delivery orders now receive an email that actually provides access to their receipt. This fixes a mismatch where the email promised a receipt but did not include one, reducing confusion and support follow-up.
Original PR description
Currently, when takeout and delivery mails are sent out to clients the mention "Attached you will find you receipt" can be seen but no receipt is sent. Steps to reproduce: ------------------- * Modify restaurent config * Enable QR + self ordering * Add Online payment method * Open the self order * Make an order for delivery or takout * Pay the order * Check the emails sent > No attachment provided Why the fix: ------------ Since this commit https://github.com/odoo/odoo/commit/a0b567508ffeb572a3c36bf28ae085d766d95f18 we now send the email only from the backend but the receipt couldn't be rendered from the backend at that time. In this version it is now possible so we cans attach the receipts with the mail. opw-6197985 Forward-Port-Of: odoo/odoo#285241 Forward-Port-Of: odoo/odoo#266007
Polish e-invoices now correctly treat K_12 taxes, such as 0% EU U, as reverse charge transactions. This helps ensure KSeF XML submissions contain the right reverse charge indicator and tax base, reducing compliance and reporting errors.
Original PR description
**PROBLEM** K_12 taxes needs to be reported as reverse charge tax. **STEP TO REPRODUCE** 1. Create an invoice with the tax 0% EU U. 2. Send the invoice the ksef. 3. Open the xml, an notice P_18 value is 2 (meaning no reverse charge), and there is no tag P_13_10. expected behavior: P_18 = 1, P_13_10 = base for the tax. opw-6460338 Forward-Port-Of: odoo/odoo#281934
This fix gives localizations the option to refuse future confirmed leave requests instead of deleting them when an employee departure is recorded. It preserves relevant leave history where required while keeping the standard behavior unchanged for other localizations.
Original PR description
Before: * Future leaves were deleted when an employee departure was applied. * There was no way for localizations to preserve these leaves when they should remain in the employee's history. After: * Add `refuse_future_leaves` to `_cleanup_employee_departure_leaves()`. * Approved leaves are still cancelled when they extend beyond the departure date. * Future confirmed leaves can now be refused instead of deleted when requested by a localization. * Keep the existing deletion behavior by default for other localizations. Impact: * Allows localizations to preserve future leave records while keeping the existing generic departure behavior unchanged. Task: 6453718 Forward-Port-Of: odoo/odoo#284398 Forward-Port-Of: odoo/odoo#281493
This fixes resizing issues in the HTML editor when tables or columns are nested inside each other. Users can now resize or reset table columns without inner content overflowing or blocking further adjustments, making page editing more reliable.
Original PR description
### Steps to reproduce **Issue 1:** * Create a table inside another table (e.g. `/table`). * Resize the last cell of the inner table beyond the outer `td` boundary. * Then try to resize the outer…
### Steps to reproduce **Issue 1:** * Create a table inside another table (e.g. `/table`). * Resize the last cell of the inner table beyond the outer `td` boundary. * Then try to resize the outer table `td` containing the inner table. * The outer table `td` can no longer be resized. **Issue 2:** - Create a table in the editor - Inside any cell, create a nested table - Resize the nested table by dragging its last column to the left to give it a fixed width - Resize the outer column that contains the nested table to the left - Observe that the nested table overflows its parent cell **Issue 3:** - Create a table in the editor - Inside any cell, create a nested table - Resize the nested table columns to give it a fixed width - Double-click the outer column border to reset its width - Observe that the nested table overflows its parent cell ### Purpose of this PR Prevent resizable elements from growing beyond the bounds of their nearest resizable ancestor. This fixes resize behavior for nested resizable structures such as: * tables inside tables * text columns inside tables * tables inside text columns The resize logic now clamps width expansion once the nearest resizable ancestor boundary is reached. - When resizing an outer table column that contains a fixed-width nested table, the outer column could be shrunk below the nested table's width, causing the nested table to overflow its parent cell. - The fix computes an effective minimum size per column by inspecting any fixed-width nested tables in that column's cells before the resize begins. This effective minimum size is passed through the resize_target_processors resource so ResizePlugin remains generic. - When resetting an outer column width, any fixed-width nested table inside the affected cells is also reset so it adapts naturally to the new outer column width instead of overflowing its parent cell. task-6215674 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#264633
Employee transport reimbursement details are now preserved when a company car is assigned. This prevents public transport, private car, and bicycle reimbursement information from being accidentally removed, supporting employees who use multiple commuting options.
Original PR description
When entering transport reimbursement values on an employee record, `_onchange_transport_mode` automatically resets public transport kilometers (bus, tram, metro), private car kilometers, and bicycle settings to zero whenever a company car is assigned. This commit removes the code that wipes alternative transport values when `transport_mode_car` or `car_id` is set, allowing employees to combine a company car with other transport reimbursement modes. task-6514557
The AI assistant now avoids unnecessary follow-up questions in Website Builder and presents choices as valid answers instead of more questions. Users can still type free-text answers by default, see cleaner prompts without selected-count clutter, and receive auto-approval notices right away.
Original PR description
Prevent unnecessary follow-up questions in Website Builder and ensure choices are valid answers rather than questions. Keep free-text answers enabled by default, remove the selected-count display, and post the auto-approval notice immediately.
Belgian employee departures now avoid unintended archiving when no archive date is set, preserve company car assignments, and refuse future leave instead of deleting it. This protects important HR records and reduces accidental loss of employee-related information during offboarding.
Original PR description
Before: * Belgian employees without an "Archive Employee On" date were automatically archived after their departure. * Future leaves were deleted when applying an employee departure. * The employee's company car was automatically unassigned on departure. After: * Belgian employees without an "Archive Employee On" date are no longer automatically archived. * Future leaves are refused instead of deleted. * Company car assignments are kept when applying a Belgian employee departure. * Display "Don't archive" when no archive date is set. Impact: * Prevents unintended archiving and preserves future leave and company car information for Belgian employees. Task: 6453718 Forward-Port-Of: odoo/enterprise#129127 Forward-Port-Of: odoo/enterprise#127414
Fixed an issue where adding all suggested products from the purchase catalog ignored the vendor-specific unit of measure. Purchase orders now use the correct vendor unit and matching suggested quantity, reducing ordering mistakes and manual corrections.
Original PR description
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in…
Issue ----- When adding all of the catalog's suggestions at once, the uom specified on the product's vendor lines is not respected. Steps to reproduce ----- - Create a product - Add a vendor line in a different uom - Create a past outgoing shipment of 100 of the product - Create a PO (same vendor as vendor line) - Open the Catalog - Click "Add All" in the suggestion section on the left - Go back to the PO > The product is in units instead of the different uom Cause ----- Clicking the button calls `action_purchase_order_suggest` in Python directly https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/models/purchase_order.py#L131-L134 whereas clicking on a product will go through `addProduct` in JS https://github.com/odoo/odoo/blob/3aec1c317aad547b1ad0c85a21cc53f735c5cbfd/addons/purchase_stock/static/src/product_catalog/record/kanban_record.js#L20-L28 Which leads to `_update_order_line_info` where the purchase line is created https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order.py#L1322-L1328 The uom is then retrieved in the create call through `_suggest_quantity` https://github.com/odoo/odoo/blob/f59783d43f891fea14d03f67d7c017d7ffbbe5a7/addons/purchase/models/purchase_order_line.py#L547-L549 Other issue ----- The quantity of the product also doesn't match the suggestion since the product `suggested_qty` is in the product's uom and not the vendor ones. ----- Ticket: opw-6417665 Forward-Port-Of: odoo/odoo#284841 Forward-Port-Of: odoo/odoo#280934
This change ensures that when a business sets Monday as the first day of the week, reports and scheduling calculations consistently group all seven days into the correct week. It prevents weekly hours or limits from being split incorrectly in locales that normally start the week on Sunday.
Original PR description
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it…
**Description of the issue/feature this PR addresses:** `weeknumber` takes a `first_week_day` override, documented as `(0 = Monday, ..., 6 = Sunday). If None, derived from the locale.`, but tests it with `if not first_week_day`, so Monday (`0`) is discarded and the locale's own day is used. `resource` passes `int(get_lang(self.env).week_start) - 1`, which is exactly `0` for Monday, at three call sites. #259600 added the parameter for the opposite case, a Monday locale with a Sunday-start user, and that direction works; this one never has. **Current behavior before PR:** With `en_US` (first day Sunday) and week start set to Monday, `resource_resource.py:299` builds `week_start_date` from Monday while `:303` buckets by Sunday. Monday 2026-08-24 to Sunday 2026-08-30 comes back as W35 for six days and W36 for the Sunday, so one week's hours are split across two buckets and the `hours_per_week` cap lands on the wrong days. **Desired behavior after PR is merged:** The override is honoured and those seven days are all W35. I checked this with a partition property: every day of a `first_week_day`-aligned week must share one `(year, week)`. Over 9 locales x 7 values of `first_week_day` x 12 years, 275940 combinations, 5 locales failed and every failure was in the `first_week_day=0` bucket. After the change there are none. I also diffed every output before and after, 315360 combinations of locale, `first_week_day` and date: 4460 rows change, all of them `first_week_day=0` on a non-Monday locale. `None` and values 1 to 6 are byte identical, so the change is contained to the broken case. Targeting 19.0 because that is the oldest branch carrying the parameter; 17.0 and 18.0 do not have it. AI-assisted: I used Claude to sweep the parameter space and write the tests. Forward-Port-Of: odoo/odoo#285706 Forward-Port-Of: odoo/odoo#285508
DHL deliveries from a company different from the main company now send a valid commercial invoice number. This prevents DHL validation errors and helps international shipments proceed correctly in multi-company setups.
Original PR description
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end.…
Issue ----- When validating a delivery in a different company from the main one, the commercial invoice's number field is incorrect, which raises an error on DHL end. `content/exportDeclaration/invoice/number: expected type: String, found: Boolean` Steps to reproduce ----- - Create a Belgian company - Setup DHL - DHL Product D - Express Worldwide - Dutiable Material enabled - Create an amrican customer - Deliver a product to the american customer > Validation Error Cause ----- The field is populated in https://github.com/odoo/enterprise/blob/cf3c2fce8a6b7b2d7547d44a0e4423f887986d52/delivery_dhl_rest/models/dhl_request.py#L204 The problem is that `next_by_code` uses the company found in the env, whereas the sequence's company is the main one, so it is not found when doing https://github.com/odoo/odoo/blob/e3b0ca11d99b2ef819cdad68b169112cd73668b6/odoo/addons/base/models/ir_sequence.py#L287 ----- Ticket: opw-6171886 Forward-Port-Of: odoo/enterprise#123170 Forward-Port-Of: odoo/enterprise#118379
Closing an AI Livechat conversation no longer triggers an unexpected chat window showing the prior AI exchange. This improves the website visitor experience by keeping the close action clean and preventing confusing follow-up messages.
Original PR description
closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. -…
closing the chat with the ai agent on the ai_livechat snippet results in a chatwindow popping up which gives a bad user experience. Steps to reproduce: - Log in as Mitchell Admin. - Go to website. - Click on edit and choose `Contact & Forms`. - Add the AI Livechat website snippet. - From the snippet options, add an AI Agent and choose a livechat team. Make sure that Mitchell Admin is configured as an operator for that livechat team (livechat channel). - Click on save. - Open an incognito tab. Log in as Marc Demo. - Go to Website. - Type a message inside the `ASK AI` text area and press enter. - Wait until you receive a response and then click close. - A chat window will popup with the messages of the conversation with the AI along with a message saying `Visitor has left the channel`. This happens because `close` button will call `closeConversation` => `livechatService.leave()` => `visitor_leave_session` => `_close_livechat_session` that posts a message that the visitor has left the channel. This commit solves the issue by setting the user who left the channel as the author of the `visitor left chat` message instead of OdooBot.
This fixes an installation failure in the Brazilian AvaTax Sales module when automatic dependency installation is skipped. The module now includes the needed dependency so sales order tax fields are available, preventing setup errors for affected deployments.
Original PR description
When installing l10n_br_avatax_sale with --skip-auto-install, you'll get an error about the l10n_br fields listed in views/sale_order_views.xml, because these fields don't fully exist without sale_external_tax. This happens because they're defined on a mixin, which is an abstract model. Abstract models only add their fields to a model that actually lists them in `_inherit`. sale.order should list this mixin, but currently doesn't. Adding that dependency is an unstable fix, so it will be added in master (20.0, or 20.1) runbot-237866 Forward-Port-Of: odoo/enterprise#128142
Restaurant table orders now keep the entered number of guests when opened from another device. This prevents staff from being asked to enter the same guest count again, reducing interruptions and keeping service flow smoother.
Original PR description
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the…
Steps to reproduce: - Enable presets on a restaurant PoS and tick "Amount of Guests" on the preset used for tables - On device A, open a table and enter the number of guests - On device B, open the same table Issue: Device B pops the guest count numpad again, even though the guest count was already entered on device A. Cause: ensureGuestCustomerCount guarded the popup on order.uiState.guestSetted. uiState is only serialized to IndexedDB (SERIALIZED_UI_STATE_PROP, used by serializeForIndexedDB); it is never sent to the server, so the flag is local to one browser and a second device always considers the guest count as not yet asked. customer_count is synced and could carry that information, but PosStore createNewOrder pre-filled it with the table seats, so it was never 0 for a table order and could not tell "not asked" from "answered". Fix: Stop storing the seats default on the record and expose it from getCustomerCount() instead, so customer_count == 0 means "no guest count entered yet". ensureGuestCustomerCount now guards on that synced value, so an order whose guest count was entered on another device is not asked for it again. Every display goes through getCustomerCount(), so the values shown on the Guests button, the receipt and the preparation ticket are unchanged. opw-6470180 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285444 Forward-Port-Of: odoo/odoo#283002
Original PR description
Before this commit- we used to check only if the vat has a value or not. But in odoo we also use `/`, `NA`, `na` as False values for the vat After this commit- We add a compute field `has_vat`, to check if the vat has any default falsy value or not Related PR- https://github.com/odoo/enterprise/pull/111396 task-5935566 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Before this commit- we used to check only if the vat has a value or not. But in odoo we also use `/`, `NA`, `na` as False values for the vat After this commit- We add a compute field `has_vat`, to check if the vat has any default falsy value or not related PR- https://github.com/odoo/odoo/pull/248132/ task-5935566