Monday, August 31, 2026
34 changes · 19.0
Resolved issues and error corrections
This update fixes several user-facing issues, including unreliable timers, broken IoT device pagination, document creation crashes, payroll leave calculations, and display problems in HR and communication tools. It also improves timesheet suggestions and restores a progress bar in manufacturing planning, helping users track work more accurately and avoid confusing interface behavior.
Italian electronic credit notes now list only the specific down payment invoice they reverse, instead of including unrelated past down payment documents or the credit note itself. This prevents inaccurate FatturaPA XML references and reduces the risk of confusion or compliance issues in Italian invoicing workflows.
Original PR description
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without…
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without reconciling it with anything else. - Generate the FatturaPA XML for credit note A. - Observe that DatiFattureCollegate lists both down payment invoice A and credit note A itself, instead of only down payment invoice A. On an order with several down payment invoices/credit notes over time, every one of them is listed instead of just the document the current credit note actually reverses. Cause of the issue: _l10n_it_edi_export_data built downpayment_moves from self.invoice_line_ids._get_downpayment_lines().move_id with no filtering. _get_downpayment_lines() (sale override) returns every invoice line ever created against the same down payment sale order line, across the whole life of the order, not just the invoice the current document actually relates to. The template renders DatiFattureCollegate for every one of those moves unconditionally, on top of the already-correct reversed_entry_id/reconciled_moves fallback, including the current document itself. Solution: Restrict downpayment_moves to lines with a negative price_subtotal, mirroring the existing down payment deduction-line detection a few lines above, and explicitly exclude the current document. This limits DatiFattureCollegate's down payment entries to the case they are meant for: a final invoice actually deducting a previously invoiced down payment. opw-6429716 Forward-Port-Of: odoo/odoo#281355 Forward-Port-Of: odoo/odoo#279938
Clearing an internal note on a restaurant point-of-sale order line now updates the existing preparation display item instead of cancelling it and creating a new one. This prevents kitchen staff from seeing the same quantity as a fresh order and accidentally preparing it twice.
Original PR description
Steps to reproduce ------------------ 1. Open a restaurant PoS with a preparation display. 2. Add a product, put the internal note "A" on the line, press Send. 3. Change the note from "A" to "B", then open the note again and click "discard", that will clear it. 4. Press Send. -> The line is fully cancelled on the preparation display and the same quantity is sent again as a new order. Expected Behavour ----------------- The existing line should be updated in-place instead of cancelling and re-creating a new one. Why it's happening ------------------ In `_process_preparation_changes` we calculate a key for every line and the `note` is inside this key. For the order lines we take `line.note or "[]"`, but for the note history we take `note['new'] or ''`, in that case, the keys don't match, so we cancel the current one and create a new line!! The fix ------- We use `"[]"` now, in many places (for completness), a follow up of 484ef2e8b2b. opw-6467346
The Project Update activity list now opens with the correct filters, so users see only updates linked to their own overdue, due today, or upcoming activities. This prevents unrelated project updates from other users from appearing and makes the activity menu more accurate.
Original PR description
**Problem:** Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's…
**Problem:**
Opening a Project Update activity from the activity menu (the clock icon in the systray) lists every project update of every user, instead of the ones carrying the current user's activities.
**Steps to reproduce:**
1. Schedule an overdue activity on a project update
2. Have a colleague create another project update with no activity
3. Click the clock icon in the systray
4. Under "Project Update", click the "Late" count
**Current behavior:**
The list opens unfiltered and shows all project updates, including the ones of other users and the ones carrying no activity at all.
**Expected behavior:**
The list shows only the updates carrying the user's own activities, restricted to the bucket that was clicked.
**Cause of the issue:**
The activity menu never builds a "my activities" domain. `openActivityGroup` in `mail/static/src/core/web/activity_menu.js` narrows the generic act_window it opens purely through context keys — `search_default_filter_activities_my` plus `search_default_activities_overdue` / `_today` / `_upcoming_all` — and the only domain it forwards comes from `_get_activity_groups`, which is limited to `[('active', 'in', [True, False])]`. A `search_default_<name>` key is resolved against a filter of that name in the model's search view, and is silently dropped when no such filter exists. `project.update`'s search view never declared them, so every key the menu sends is discarded and the action opens on an empty domain.
**Fix:**
`project.project` and `project.task` — the addon's two other `mail.activity.mixin` models — already declare this block of invisible activity filters, as does every other model reachable from the activity menu. Declaring them on `project.update` is what makes the menu's context keys resolvable, and keeps the model consistent with the rest of the codebase instead of special-casing `project.update` on the client side.
opw-6416397This fixes how WhatsApp connection details are sent to Odoo's proxy service so the service can read them reliably. It helps prevent subscription checks or related WhatsApp setup flows from failing because required account information was not received in the expected format.
Original PR description
The calls to the WhatsApp proxy sent their parameters as a JSON body, which a `type='http'` route does not unpack into its arguments, so the proxy needed a decorator to read them back before `check_subscription` could see `db_uuid`. Send them as form fields instead. `requests` encodes a dict as `application/x-www-form-urlencoded` and sets the header itself, so the routes fill their arguments on their own and the decorator goes away on the proxy side.
Opening a page from the Website Pages list now keeps users on their current Odoo host when the page belongs to the default website. This prevents unexpected redirects to a configured real domain, avoiding login interruptions and preserving the user's context.
Original PR description
Steps to reproduce: =================== 1. Go to Website > Pages 2. Set a real domain on the default website 3. Open any page linked to that website => User is redirected to the real domain When clicking on a page linked to the default website from the Website > Pages list, if that website had a real domain configured, the action was redirecting the user to that domain instead of staying on the current host (e.g. <db>.odoo.com). since it's a different domain the user would then end up in the login page and lose the context of the page they wanted to open. Solution: ========= Now we change the behavior as requested by PO. On <domain>.odoo.com, in the Pages list view, if the page is linked to the default website, keep <domain>.odoo.com and don't redirect to the real domain => User should stay on the current host opw-6141457 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixes an accounting display issue where a reconciled bank transaction could hide the related vendor bill reference when exchange rate differences were involved. This helps users correctly identify matched bills during bank reconciliation, especially for foreign-currency payments.
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
Argentinian electronic invoices can now be printed when an export customer uses a generic identification type without an ARCA code. This prevents printing failures for affected foreign customer invoices and adds coverage to avoid the issue returning.
Original PR description
## Description Printing any posted electronic invoice whose commercial partner has an identification type **without** ARCA code (typically the generic `VAT` type from `l10n_latam_base`, common on…
## Description
Printing any posted electronic invoice whose commercial partner has an identification type **without** ARCA code (typically the generic `VAT` type from `l10n_latam_base`, common on foreign partners of export invoices) crashes:
```
File ".../l10n_ar_edi/models/account_move.py", line 161, in _compute_l10n_ar_afip_qr_code
data.update({'tipoDocRec': int(rec._get_partner_code_id(commercial_partner_id))})
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
```
## Steps to reproduce
1. Install `l10n_ar_edi`.
2. Create a customer: Country **Spain**, Identification Type **VAT** (no ARCA code), Identification Number `ESA12345674`, ARCA Responsibility Type **Cliente / Proveedor del Exterior**.
3. Create and validate an invoice for this customer on an export electronic journal (document type 19).
4. Print the invoice → traceback above.
## Root cause
Since bb8e6fda72ae6364cf7806acb400b071f4108fc9 ([FIX] l10n_ar_edi: return the correct ARCA code when final consumer, odoo/enterprise#106881), `_get_partner_code_id()` lost its final `return partner_id_code` fallback: when the identification type has no ARCA code and the partner is not a Final Consumer, the method now returns an implicit `None`. The QR code compute casts the result with `int()`, which accepted the previous falsy return (`int(False) == 0`, rendering `tipoDocRec: 0` as in 17.0/18.0) but raises on `None` — making every such posted invoice impossible to print.
## Fix
Restore the fallback return so the method always returns the identification type code (possibly falsy) instead of an implicit `None`. The intent of bb8e6fda72ae is preserved: a Final Consumer with an identification number still gets its real code, and the other callers already handle falsy values (`partner_id_code or 0`).This fix ensures that applying text color in the HTML editor only affects the exact selected text, even when the selection crosses formatted text. It prevents unintended coloring of nearby bold or inline content, improving editing accuracy for users.
Original PR description
Problem: When applying color on a selection that spans across partially selected inline elements (e.g. `<p><b>a[b</b>c]d</p>`), container elements whose contents are not fully selected (such as…
Problem: When applying color on a selection that spans across partially selected inline elements (e.g. `<p><b>a[b</b>c]d</p>`), container elements whose contents are not fully selected (such as `<b>`) were included in `targetedNodes`. This caused improper color formatting/nesting on partially selected elements. Cause: In `ColorPlugin._applyColor()`, `targetedNodes` were filtered by checking `isNodeEditable(node)` and `nodeName !== "T"`, but did not check whether the contents of `node` were fully selected (`areNodeContentsFullySelected(node)`). As a result, partially selected ancestor elements were included in `targetedNodes`. Solution: Filter `targetedNodes` in `_applyColor()` using `this.dependencies.selection.areNodeContentsFullySelected(node)` to ensure only fully selected nodes are targeted when applying colors. Steps to reproduce: 1. Open html_editor. 2. Insert content: `<p><b>ab</b>cd</p>`. 3. Select `b` inside `<b>` and `c` inside `<p>` (`<p><b>a[b</b>c]d</p>`). 4. Apply text color (e.g. red). 5. Observe "ab" and "c" was colored instead of just "b" and "c". task-6456443 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285337 Forward-Port-Of: odoo/odoo#281462
Nilvera refund documents for Turkish e-invoicing are now recognized as refunds even when they are sent with positive amounts. When possible, the imported refund is also connected to the original invoice, improving accounting accuracy and traceability.
Original PR description
# Description of the issue/feature this PR addresses: Nilvera can send refund documents as Invoice with refund-specific InvoiceTypeCode values. # Current behavior before PR: The default UBL import logic only treats an Invoice as a refund when its amount is negative. As a result, Nilvera refund documents sent as Invoice with positive amounts are imported as invoices. Also, imported refunds are not linked to their original invoice. # Desired behavior after PR is merged: Nilvera refund documents using refund-specific InvoiceTypeCode values are imported as refunds. When a unique match is found, the imported refund is linked to its original invoice through reversed_entry_id. task-id-5948275 I confirm I have signed the CLA and read the PR guidelines at [www.odoo.com/submit-pr](http://www.odoo.com/submit-pr)
Users can now customize the Inventory Overview by removing graph cards in Studio without causing the page to crash. The update makes the overview handle missing graph data safely, preserving access to inventory dashboards after customization.
Original PR description
Steps to reproduce:
- Go to Inventory > Overview
- Enable Studio, delete a kanban card that contains the "picking_type_dashboard_graph" widget (field kanban_dashboard_graph)
> UncaughtPromiseError > OwlError
> TypeError: Cannot read properties of undefined (reading 'includes')
at StockKanbanRenderer.getGroupsOrRecords
Cause of the issue:
`StockKanbanRenderer.getGroupsOrRecords` assumes the field `kanban_dashboard_graph` is always present on every record to detect if all Inventory Overview graphs are sample data and, if so, replace them with randomized values.
By removing the card with studio, we remove the field from the fields fetched for the record, and so `r.data.kanban_dashboard_graph` is `undefined`.
Fix by ignoring records for which the field isn't fetched instead of assuming it's always there.
opw-6512743Belgian payroll now avoids incorrectly paying a public holiday when it falls at the start of a renewed sickness certificate after the guaranteed salary period has ended. This helps ensure employees on long-term sick leave are paid according to the correct legal payroll rules and prevents overpayments.
Original PR description
Steps to reproduce: - Employee sick long-term, medical certificate renewed month by month. - Guaranteed salary period (30 days) already over. - Compute payroll for a month whose public holiday falls on day 1 of a new certificate. - That holiday is paid. Every other day that month is correctly unpaid. Cause: the 30-day lookback in _get_leave_work_entry_type_dates compared exact datetimes, not dates. A certificate starting the same day as the holiday, just later in the clock, got excluded from the lookback. The holiday then fell back to its default paid type. Fix: compare by calendar date instead, in both the lookback query and its day-coverage loop. Added a regression test for the split-certificate case. Task 6512860
This fixes week calculations so a Monday week start is correctly respected even when the user's locale normally starts weeks on another day. It prevents weekly work hours from being split across the wrong weeks, helping scheduling limits apply to the correct days.
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.
This update fixes an issue in SMS marketing mailings. It helps ensure SMS campaign handling works more reliably for users managing mass messaging.
This fix makes the creation order of accounting lines from manufacturing stock valuation predictable. It helps avoid inconsistent test results and reduces the chance of confusing differences in subcontracting purchase bill accounting records.
Original PR description
The search order on account move line depends on date, move name and id. This search result order can be non deterministic because, when creating account move line from stock valuation, it depends on…
The search order on account move line depends on date, move name and id. This search result order can be non deterministic because, when creating account move line from stock valuation, it depends on the order from a set, but sets are unordered. **Step to reproduce** Run [test_subcontracting_purchase_bill](https://github.com/odoo/odoo/blob/13b2781978b0edad0df0800404276919b767e32b/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py#L268) in: Single app, community, with demo data. **Observation** * The search: When doing the search since we didn't specify any order, the search from account.move.line will ordered by: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/account/models/account_move_line.py#L23 https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/account/models/account_move_line.py#L1664 Since, for the components the date and move name are the same it will only depend on the aml ids: * `Account.move.line` creation: When it validate the receipt (`button_validate`) https://github.com/odoo/odoo/blob/869c750f978b1b00a4a04bd61226f0e20d2e7729/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py#L296 It will mark as done the picking (`_action_done`) and the productions (`button_mark_done`) linked to this picking. https://github.com/odoo/odoo/blob/869c750f978b1b00a4a04bd61226f0e20d2e7729/addons/stock/models/stock_picking.py#L1429 https://github.com/odoo/odoo/blob/869c750f978b1b00a4a04bd61226f0e20d2e7729/addons/mrp_subcontracting/models/stock_picking.py#L49 Modify the inventory accordingly (`_post_inventory`) https://github.com/odoo/odoo/blob/869c750f978b1b00a4a04bd61226f0e20d2e7729/addons/mrp/models/mrp_production.py#L2231 while inside of `_post_inventory`, it will process all the production moves, for this it will divided them in set to process them by batch: https://github.com/odoo/odoo/blob/f0e58b9324af18d0cf0264aec2886d098e997f03/addons/mrp/models/mrp_production.py#L1904-L1911 From this set, it will create the `account.move.line`: It retrieve the actual stock move with the browse, and call `_action_done`, from where the stock valuation layer will create the `account.move.line`. https://github.com/odoo/odoo/blob/f0e58b9324af18d0cf0264aec2886d098e997f03/addons/mrp/models/mrp_production.py#L1913 https://github.com/odoo/odoo/blob/f0e58b9324af18d0cf0264aec2886d098e997f03/addons/stock_account/models/stock_move.py#L187 The issue arise because a set read order is non deterministic. runbot-939794
Calendar invitation files now include the online meeting link in a standard URL field. This helps recipients and calendar apps surface the video call link more reliably when opening or importing invites.
Original PR description
Add the meeting's videocall_location as a URL property in generated iCalendar (.ics) invitation files. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283934 Forward-Port-Of: odoo/odoo#283562
Manufacturing bills of materials now exclude combo products from component selection. This prevents configurable sales bundles from being treated as physical manufacturing inputs, reducing setup errors and keeping production data accurate.
Original PR description
Version: --------- - 19.0+ Steps to reproduce: ------------------- - Install `mrp` and `sale_management` - Create a product of type `combo` - Go to Manufacturing > Products > Bills of Materials - Create or edit a BoM - Add a component line - Select the combo product Issue: ------ Combo products can be selected as BoM components. Since combo products represent configurable sales bundles rather than physical products, they are not meaningful manufacturing components. Before Commit: ------------------- - It was possible to select a product of type 'combo' as a component in a Bill of Materials. After Commit: ----------------- - Combo products are filtered out of the component picker and cannot be added as BoM components --- opw-6425024 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The property editor now stays available even when a saved relationship filter cannot be checked by the server. Users can still open, correct, or delete the affected property instead of being blocked by an error.
Original PR description
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it…
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it opens and on every later render. The server raises a ValueError and the call has no error handling, so the whole editor goes down. The bad domain stays saved on the property, so reopening the editor fails the same way and the property can no longer be edited or deleted. Wrap the search_count call in _updateMatchingRecordsCount (property_definition.js) in a try/catch and show no count when it fails. This is the only place the editor counts matching records, so guarding it here handles a bad domain from any source, the field selector or the code editor. The field selector still shows its warning on the invalid path, so the user can fix or delete the property. Steps to reproduce: 1. On a model that has a Properties field, add a Many2one property and set its Model to a model that itself has a Properties field. 2. Open the property Domain and click New Rule. 3. In the field selector pick the Properties entry, then close the selector. => An error dialog appears and the property can no longer be edited or deleted. Ticket [link](https://www.odoo.com/odoo/project.task/6101311) opw-6101311 Forward-Port-Of: odoo/odoo#281221 Forward-Port-Of: odoo/odoo#259886
This fix prevents an upgrade-time warning when employee records are temporarily missing version data. It helps keep HR module upgrades from 18.0 to 19.0 cleaner and avoids misleading error logs during a normal migration state.
Original PR description
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls…
### Issue: A warning `IndexError: tuple index out of range` is logged on RunBot when upgrading from 18.0 to 19.0 for employees without any version ### Cause: `_get_version` on `hr.employee` falls back to `versions[0]` when no version matches the given date But if the employee has no versions at all, `versions` is empty and `versions[0]` raises an `IndexError` ### Steps to reproduce: - Create a database with `hr_attendance` installed in 18.0 - Upgrade to 19.0 Before the fix, a warning is logged during the upgrade ### Notes: An employee without any version is a transient state during upgrade `hr/saas~18.4.1.1/end-migrate.py` backfills a version for every employee still missing one (`current_version_id IS NULL`) once the whole upgrade chain is done Since it only runs at the very end, an employee can still be found without a version by earlier steps (e.g. modules reloading their demo data), which is when this warning was logged Confirmed on an upgraded RunBot database that employees without a version before the upgrade do end up with one once fully completed runbot-241187
Credit card and cash journals configured for file imports now show the Import File button on the Accounting dashboard. This removes confusion for users and makes file-based statement imports accessible wherever the journal type supports them.
Original PR description
The "Import File" button on the dashboard card only shows up for bank journals. Credit card journals are treated the same way as bank journals pretty much everywhere else: the dashboard card itself,…
The "Import File" button on the dashboard card only shows up for bank journals. Credit card journals are treated the same way as bank journals pretty much everywhere else: the dashboard card itself, the statements list, the "Transaction Feeds" setting on the journal form (where you can pick the file import option), and even `create_document_from_attachment` in this module, which already accepts them. So you end up with a credit card journal set to import files but nothing on its card to actually do it, and people assume the feature is simply not there. Show the button on credit card journals too. Steps to reproduce: - Install account_bank_statement_import_csv (or any other import format) - Create a "Credit Card" journal and set its Transaction Feeds to the file import option - Go to the Accounting dashboard - The bank journal card has an "Import File" button, the credit card one doesn't --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#129546
AvaTax invoice PDFs now display section or divider rows with the correct width when the Taxes column is hidden. This prevents misaligned borders and unwanted blank space on printed invoices, improving document presentation for customers.
Original PR description
Steps: 1. Turn on AvaTax for a company (connect it to Avalara). 2. In Settings > General Settings > Invoicing, set the Document Layout to Boxed (Box), just for a clear view. 3. Create a customer…
Steps: 1. Turn on AvaTax for a company (connect it to Avalara). 2. In Settings > General Settings > Invoicing, set the Document Layout to Boxed (Box), just for a clear view. 3. Create a customer invoice: - Use the customer with an AvaTax fiscal position and proper address details. - Add a product with an AvaTax category. - Add a section line (a divider/heading row). - Compute Taxes for the AvaTax. 4. Print the invoice as a PDF. 5. Look at the section/divider row. Its cell border does not line up with the other rows. On longer invoices, this can also cause a big blank gap at the bottom of a page. Cause: When an invoice is computed by AvaTax, the Taxes column is hidden from the PDF (this is intentional, since AvaTax shows tax differently). But a different part of the same template decides how wide the section row should be. This part does not know the Taxes column was hidden, so it still counts it. That makes the section row's width wrong by one column, which breaks the table layout in the PDF. Fix: Instead of hiding the Taxes column only in one place, fix it at the source: the variable that decides (does this invoice show a Taxes column) now also checks if the invoice is an AvaTax invoice. Every other part of the template that uses this variable (the header, the tax cells, and the section row width) automatically gets the right answer, since they all read from the same place. Result: - Section rows on AvaTax invoices now have the correct width. opw - 6455674
Point of Sale invoice settlements are no longer treated as new invoices when they only settle existing amounts due. This prevents validation failures and avoids creating unnecessary zero-value electronic invoice numbers in Argentina, while keeping normal sales invoicing unchanged.
Original PR description
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices",…
Steps to reproduce: - Argentinean company, Responsable Inscripto (l10n_ar_pos installed) - A posted customer invoice with VAT, partially paid - In the PoS, pick the customer, "Settle invoices", select that invoice, pay the balance by bank transfer and validate Issue: Validation fails with "There should be a single tax from the "VAT" tax group per line, but this is not the case for line ..." and the settlement cannot be recorded at all. The invoice being settled is correct; the rejected line belongs to a second invoice the PoS creates for the settlement itself. Cause: `setToInvoice` already refuses to invoice a settlement, but its condition, `is_settling_account and no line`, only describes a deposit: the deposit line is added at validation. When settling a due or an invoice, `is_settling_account` stays false and the order does carry lines - the settle lines - so the guard never fires. l10n_ar_pos then sets `to_invoice` on mount, as a sale must generate an electronic document in AR, and the settle line, untaxed on purpose since it pays an existing document rather than selling anything, reaches `_check_argentinean_invoice_taxes`. Fix: Refuse the flag as well when every line is a settle line. Such an order generates an empty document - all its lines, the receivable one included, have a zero balance - so it records nothing and only consumes a document number, which in AR means an AFIP number for a zero-amount invoice. An order that also sells something keeps its invoice, since the sale still has to be reported. Reconciliation is unaffected: it happens in `_reconcile_account_move_lines` at session close, and is already covered for both invoiced and non-invoiced settlement orders. opw-6464675
Project updates now show the same accurate profitability amounts as the project dashboard, including the correct net amount still to invoice. This prevents overstated expected revenue when vendor bills or invoicing adjustments affect a project's profitability.
Original PR description
Steps to reproduce: ------------------- 1. Install sale_project and accounting. 2. Create a service product with: - Invoicing Policy: Prepaid/Fixed Price - Create on Order: Project & Task - Cost:…
Steps to reproduce: ------------------- 1. Install sale_project and accounting. 2. Create a service product with: - Invoicing Policy: Prepaid/Fixed Price - Create on Order: Project & Task - Cost: $50, Sales Price: $100 3. Create a sales order with this product, quantity = 1, and confirm it. 4. Create a vendor bill for this product with a line price of $50 (linked via analytic distribution to the project's analytic account) 5. Open the generated project's dashboard and verify that "To Invoice" shows $50 (to_bill -50 + to_invoice 100). 6. Create a project update and observe. Issue: ------ The project update total displays $100 under "To Invoice" instead of $50. Cause: ------ The project update template displays the aggregated profitability totals (`profitability['total']['revenues']` and `profitability['total']['costs']`) instead of the dedicated `to_bill_to_invoice` and `billed_invoiced` values, causing stale "to invoice" amounts to persist after invoicing. Solution: --------- Use the `to_bill_to_invoice` and `billed_invoiced` values when rendering the project update profitability report. opw-6323869 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278334 Forward-Port-Of: odoo/odoo#273605
Romanian SAF-T (D406) exports now use the required file version value of 2.0 instead of 2.4.8. This helps ensure generated declarations match the expected Romanian reporting format and reduces compliance submission issues.
Original PR description
**Steps to reproduce:** - Install the `l10n_ro_saft` module and switch to the RO Company. - Navigate to Accounting > Reporting > General Ledger. - Click the gear icon > SAF-T (D406 Declaration). - Open the generated XML file and check the `AuditFileVersion` node. **Observation:** The `AuditFileVersion` node is set to `2.4.8`. **Expected behavior:** The `AuditFileVersion` node should be set to `2.0` (confirmed with the PO [1]) **Root Cause:** At [2], the `file_version` value is incorrectly set to `2.4.8` instead of `2.0`. [1]: https://www.odoo.com/mail/message/1144385840 [2]: https://github.com/odoo/enterprise/blob/ce2ad80c91aea27b143a80018aa73ed10d16cdbe/l10n_ro_saft/models/account_general_ledger.py#L179-L183 opw-6452918 Forward-Port-Of: odoo/enterprise#127935
Closing a Point of Sale session could fail when orders included both a tracked product and a kit containing that same product. The fix correctly totals quantities across matching order lines, preventing the error and allowing sessions to close normally.
Original PR description
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The…
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The Kit product - A combination of Product A and the Kit product 4. Create three different orders with these combinations. 5. Try to close the PoS session. **Issue :** An error occurs when attempting to close the PoS session. The issue is in the pos_mrp module, specifically in the _get_lot_line_qty method. When accessing the following line: `lines_data[move.bom_line_id.bom_id.product_tmpl_id.product_variant_id.id]['order_lines'].qty` we expect order_lines to contain a single record. However, in this scenario, multiple order lines can be returned for the same product. As a result, accessing .qty directly on order_lines raises a singleton error. **Solution :** With this fix, we first retrieve the quantity from each order line and thensum the quantities together. This ensures that multiple matching order lines are handled correctly and prevents the singleton error when closing the PoS session. opw-6442765 Forward-Port-Of: odoo/odoo#284526 Forward-Port-Of: odoo/odoo#281622
Tax return workflows now only move to a paid status when that status is valid for the selected process. This prevents an error when users reply to messages on certain corporate tax return records, keeping collaboration and tax return handling uninterrupted.
Original PR description
**Steps to reproduce:** - Log in as the `Mitchell Admin` and install the `accountant` module. - Open Accounting and click `Set Period` under `Tax Returns`. - Set the `Opening Date` and apply the…
**Steps to reproduce:** - Log in as the `Mitchell Admin` and install the `accountant` module. - Open Accounting and click `Set Period` under `Tax Returns`. - Set the `Opening Date` and apply the accounting periods. - Log in as another user (e.g., Mark Demo). - Open the Accounting app and click `Tax Returns`. - In the kanban view, remove the search filter and open `Annual Closing: Corporate Tax 2026`. - From the chatter, send a message. - Log in again as the Mitchell Admin user. - Open `Annual Closing: Corporate Tax 2026`. - Click the `Reply All` icon on the message sent by the other user and click `Send`. **Error:** `ValueError: Wrong value for account.return.generic_state_review_submit: 'paid'` **Root Cause:** At [1] and [2], only the `generic_state_only_pay` and `generic_state_tax_report` workflows define `paid` as a valid state. The `generic_state_review_submit` and `generic_state_review` workflows do not include this state. When replying to a message, [3] calls the `action_send_mail` method, which eventually calls `_action_finalize_payment` at [4]. This method unconditionally sets the tax return `state` to `paid`, even for the `generic_state_review_submit` and `generic_state_review` workflows, causing an error in the `_inverse_state` method. **Fix:** This commit prevents the error by ensuring that the state is changed to `paid` only when the configured workflow supports the `paid` state. [1]: https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/account_reports/models/account_return.py#L83-L95 [2]: https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/account_reports/models/account_return.py#L724-L768 [3]: https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/account_reports/wizard/mail_compose_message.py#L46 [4]: https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/account_reports/models/account_return.py#L1461-L1465 opw-6404112
Maintenance requests now create follow-up activity deadlines based on the user's local timezone instead of UTC. This prevents scheduled work from appearing due a day early for users in timezones far ahead of UTC, improving planning accuracy.
Original PR description
Steps to reproduce: =============== - Install `maintenance` module - Set your user's timezone to Pacific/Auckland (UTC+12/+13). - Go to Maintenance > Maintenance Requests > New. - Set the Scheduled…
Steps to reproduce:
===============
- Install `maintenance` module
- Set your user's timezone to Pacific/Auckland (UTC+12/+13).
- Go to Maintenance > Maintenance Requests > New.
- Set the Scheduled Date to tomorrow morning (e.g. 8:00 am).
- Save and check the auto-generated activity in the chatter.
Issue:
====
The activity generated for the maintenance request's Scheduled Date is
created with a deadline of Today instead of Tomorrow, even though the
Scheduled Date was explicitly set to Tomorrow.
Cause:
=====
`schedule_date` is a `Datetime` field, always stored in UTC. In
`MaintenanceRequest.activity_update()`, the deadline for the activity
(a `Date` field) was computed with:
```py
fields.Datetime.from_string(request.schedule_date).date()
```
`from_string()` only parses the stored value; it does not convert it
to any timezone. Calling `.date()` on it therefore truncates the raw
UTC datetime directly, ignoring the timezone of the user who picked
the date.
For a user ahead of UTC by a large enough offset (e.g. Pacific/Auckland,
UTC+12/+13), picking "Tomorrow 8:00 am" in their local timezone can
resolve to a UTC instant that still falls on Today's date (Tomorrow
8:00 local minus 13 hours lands at 19:00 UTC on Today). Truncating
that UTC value straight to a date silently shifts the deadline back
by one full day versus what the user selected.
Fix:
===
The deadline is now computed from the schedule date after converting
it to the record's (user's) timezone, using
`fields.Datetime.context_timestamp()`, instead of truncating the raw
UTC value. This fixes the reported issue for any user whose timezone
offset makes the truncation land on the wrong side of midnight UTC.
---
Note - automatic activity creation is remove from this [commit](https://github.com/odoo/odoo/commit/ab63e9709d8ea790a8a72a0a86b8bccc478c02d5) in saas-19.2
---
opw-6363868
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#275330This fixes an issue that prevented non-admin users from sending Vietnam electronic invoices through SInvoice after migrating from version 18. The change restores the expected invoicing workflow while keeping sensitive company credentials restricted from normal user access.
Original PR description
### Steps to Reproduce: 1). Install l10n_vn_edi_viettel ('Vietnam E-Invoicing') module in v18. 2). Migrate the database in any version above v18. 3). AccessError will appear while generating ('Send…
### Steps to Reproduce:
1). Install l10n_vn_edi_viettel ('Vietnam E-Invoicing') module in v18.
2). Migrate the database in any version above v18.
3). AccessError will appear while generating ('Send to SInvoice') on invoice for non-admin users.
### Issue:
- In v18, users were able to send and generate documents via (Send to SInvoice). Since v18.1 onwards, field access [check] is enforced during this flow, and since `l10n_vn_edi_username` is restricted to admin users only [here], non-admin users hit an AccessError as soon as
`_l10n_vn_edi_get_credentials_company` reads this field on`res.company`.
```py
You do not have enough rights to access the field "l10n_vn_edi_username" on Companies (res.company). Please contact your system administrator.
Operation: read
User: 12
Groups: allowed for groups 'Role / Administrator'
```
### Solution:
- This commit fixes the issue by adding a `sudo()` call on the company inside [_l10n_vn_edi_get_credentials_company] itself, so that non-admin users can successfully send and generate documents like in the previous version, without any hassle.
[check]: https://github.com/odoo/odoo/blob/5ca10578a2fd1b40cd371ed5ad20c1654dfe54d3/odoo/orm/models.py#L3384
[here]: https://github.com/odoo/odoo/blob/5ca10578a2fd1b40cd371ed5ad20c1654dfe54d3/addons/l10n_vn_edi_viettel/models/res_company.py#L9
[_l10n_vn_edi_get_credentials_company]: https://github.com/odoo/odoo/blob/ecc267a231958c2dd99a7287c6bd1adbdbd22965/addons/l10n_vn_edi_viettel/models/account_move.py#L885
Ticket [link](https://www.odoo.com/odoo/project.task/6434854)
opw-6434854
Forward-Port-Of: odoo/odoo#281396The Planning auto-plan action now works when open shifts include different roles. This prevents an error that blocked scheduling for employees assigned to multiple planning roles, making workforce planning more reliable.
Original PR description
Currently, using the 'Auto Plan' feature on several open shifts that have different roles raises an error. ### Steps to reproduce: - Install Project Planning (`project_forecast`) with demo data. -…
Currently, using the 'Auto Plan' feature on several open shifts that have different roles raises an error. ### Steps to reproduce: - Install Project Planning (`project_forecast`) with demo data. - Create an employee and in the settings tab under planning section assign multiple roles to that employee. - Navigate to Planning app, remove all filter and search for this employee. - Click on Actions>Auto Plan ### Error: ``` ValueError: Expected singleton: planning.role(2, 4, 3, 6, 1) ``` ### Root Cause: Since [commit](https://github.com/odoo/enterprise/pull/122035/changes/cccee5bc7da8195fa65dad0dbf65709c929c3fdf), `_get_open_shifts_resources` builds one domain for the whole batch of open shifts being planned. `self.role_id` is a mapped field over that whole recordset, so when the shifts span more than one role it returns several planning.role records. Accessing `.id` on that multi-record recordset raises "Expected singleton" instead of the intended list of role ids. ### ref: https://github.com/odoo/enterprise/blob/cebef1c1e364ec2dea0de3a4fd72a32c65116620/project_forecast/models/planning_slot.py#L109 ### Fix Use `self.role_id.ids` so the domain works with any number of distinct roles among the open shifts, matching how `project_id` is already handled in the same domain. **opw-6484707**
Worksheet template field descriptions are now automatically available in the user's active language. This prevents untranslated labels from appearing in model details after creating worksheet templates, improving consistency for multilingual teams.
Original PR description
When creating a worksheet template, the field description are not translated. Steps to reproduce: ------------------- * Install quality_control_worksheet * Add and activate a new language * Go to Quality → Configuration → Worksheet Templates. * Create a new worksheet template. * Go to Settings → Technical → Database Structure → Models. * Open the new model -> the field_description is not translated Observation: ------------- The field_description is a translated field: https://github.com/odoo/odoo/blob/9c67949be529eb86886b3d5bde08e81e048ecfe7/odoo/addons/base/models/ir_model.py#L533 opw-6475896
The Uzbek balance sheet now includes current year profit or loss in the equity section. This keeps the report balanced and accurate before and after year-end closing, supporting more reliable statutory financial reporting.
Original PR description
The Uzbek balance sheet was unbalanced because the current year's profit/loss was not included in the Equity section, even though it is part of the official report structure. This commit adds a dedicated 'Current Year Earnings' report line, computed from the current fiscal year's income, expense and equity_unaffected accounts. This ensures the balance sheet correctly reflects the current year's result both before and after the year-end closing process. The new line is also included in the Total Equity computation, and the displayed formula in the report label is updated accordingly. see https://github.com/odoo/odoo/pull/280594 task-6361059
The Uzbekistan accounting setup now classifies current-year and period profit accounts correctly so they are counted only once in the balance sheet. This prevents overstating the Equity section and improves the accuracy of local financial reports.
Original PR description
Accounts 8710 (Current Year Profit/Loss) and 9910 (Net Profit for the Period) were equity_unaffected, causing their balances to be picked up both by the retained earnings tag-based formula and by the current-year-earnings domain formula in l10n_uz_reports, double- counting them in the balance sheet's Equity section. This commit changes both accounts' type to Equity and adds the BS Line 0540 tag to 9910 (8710 already carried it), so their balances are captured through the tag alone. see https://github.com/odoo/enterprise/pull/123845 task-6361059
Confirmed sales order reports will no longer show optional products that the customer did not choose after a migration to Odoo 19. This keeps printed sales documents accurate by showing only selected optional items with a quantity above zero.
Original PR description
Steps to Reproduce: 1. Create a sale order in v18. 2. Add optional products to the quotation. 3. Keep the sale order in Quotation state. 4. Migrate the database from v18 to v19. 5. Open the migrated…
Steps to Reproduce: 1. Create a sale order in v18. 2. Add optional products to the quotation. 3. Keep the sale order in Quotation state. 4. Migrate the database from v18 to v19. 5. Open the migrated sale order in v19. 6. Confirm the sale order. 7. Print the sale order report. Current Result: All optional products, including the products that were not selected by the customer, are displayed in the confirmed sale order report. Expected Result: Only the products selected by the customer should be displayed in the confirmed sale order report. https://github.com/user-attachments/assets/e91479c1-0313-4d43-9f38-184ecc67d437 Unselected optional products should be hidden. Cause: In v18, optional products were stored in the `sale.order.option` model. During the migration to v19, the `sale.order.option` model is [removed](https://github.com/odoo/upgrade/blob/7318af8e1754efddbadfa30cd7a33b66668cb8dd/migrations/sale_management/saas~18.5.1.0/post-migrate.py#L175) and its records are migrated to `sale.order.line`. As a result, regular sale order lines and optional products are now stored in the same `sale.order.line` model with is_optinal = True. The report was not handling these migrated optional product lines correctly, so unselected optional products were also included in the confirmed sale order report. Fix: Hide optional products with zero quantity from confirmed sale order reports. Selected optional products with a quantity greater than zero are still displayed. upg - 4610921 opw - 6481753 [here]: https://github.com/odoo/upgrade/commit/ef8a887780a007b3fb890803e6e3938a7915a50b [here]: https://github.com/odoo/enterprise/commit/d6231bacb840b85c8ab8d22e00e8f5be7e5c0941 [here]: https://github.com/odoo/odoo/commit/5f4db1d08a9aec7bd4f2a168e279bf5b719592a3 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes a Microsoft Calendar sync issue where updating one moved occurrence in Outlook could incorrectly recreate the whole recurring series in Odoo. Individual changes such as adding an attendee now stay limited to the intended event, while real recurrence pattern changes still update the series.
Original PR description
The stored rrule of a recurrence embeds a DTSTART line based on the recurrence dtstart (the smallest start among its events). When the first occurrence is moved in Outlook, the Odoo dtstart no longer…
The stored rrule of a recurrence embeds a DTSTART line based on the recurrence dtstart (the smallest start among its events). When the first occurrence is moved in Outlook, the Odoo dtstart no longer matches that DTSTART, but the stored rrule is not recomputed at that point (it only depends on the pattern fields). On a later sync touching the seriesMaster (e.g. after adding an attendee to the moved occurrence), the recurrence values are written again and the rrule is reserialized with a DTSTART based on the moved occurrence. _write_from_microsoft() took this new rrule string as a pattern change and reapplied the recurrence: all occurrences were deleted and recreated as copies of the moved one, spreading its specific data (e.g. the newly added attendee) to the whole series. Ignore the DTSTART line when comparing the rrule before and after the write: a DTSTART-only difference does not reflect any pattern change in Outlook. An actual pattern change (FREQ, UNTIL, INTERVAL, ...) still reapplies the recurrence as before. Steps to reproduce: 1. In Outlook, create a recurring event 2. In Odoo, run the calendar sync 3. In Outlook, move the first occurrence of the recurrence (shift its start and stop time by 30 minutes) 4. In Odoo, run the sync 5. In Outlook, add an attendee to that same first occurrence 6. In Odoo, run the sync All the occurrences are deleted and recreated as copies of the first one, and the attendee ends up on every occurrence instead of one. opw-5129848 Forward-Port-Of: odoo/odoo#284747 Forward-Port-Of: odoo/odoo#269549