Daily updates from Odoo
Thursday, July 16, 2026
70 changes · saas-19.3
Enhancements to existing features
Payment initiation data now includes additional payer and creditor information such as names, VAT numbers, and addresses. This helps Powens process online payments with richer transaction details and may improve compatibility with its requirements.
Original PR description
Powens requires more information that what is already provided in the payment initiation payload. This commit adds payer/creditor name and VAT. task-5977148
Draft bank statement lines now appear in blue in the reconciliation view. This helps accounting users quickly distinguish unfinished statement lines from confirmed ones, reducing review time and confusion.
Original PR description
This commit will put the text in blue when the statement line is in draft to be able to see quickly which lines are in draft. task-6327308 Forward-Port-Of: odoo/enterprise#121775
The journal creation wizard now supports navigating options with keyboard arrow keys. This makes setup faster and easier for users who prefer keyboard navigation or rely on it for accessibility.
Original PR description
This commit aims to allow for navigation through the journal create wizard via keybaord arrows. Related Odoofin PR: https://github.com/odoo/odoofin/pull/502 task-5796200 Forward-Port-Of: odoo/enterprise#105541
LEGAL REQUIREMENTS - As of January 2026, the 9% VAT will increase to 12%. PURPOSE - For each 9% VAT, add 12% VAT with the same tax tag and descriptions, so in the VAT report, it's put under the same lines. - And add the missing taxes from the sheet provided in the task description. Related PR: https://github.com/odoo/enterprise/pull/101773 Task-5269617 Forward-Port-Of: odoo/odoo#276414 Forward-Port-Of: odoo/odoo#239388
Original PR description
LEGAL REQUIREMENTS - As of January 2026, the 9% VAT will increase to 12%. PURPOSE - For each 9% VAT, add 12% VAT with the same tax tag and descriptions, so in the VAT report, it's put under the same lines. - And add the missing taxes from the sheet provided in the task description. Related PR: https://github.com/odoo/enterprise/pull/101773 Task-5269617 Forward-Port-Of: odoo/odoo#276414 Forward-Port-Of: odoo/odoo#239388
When importing an xml, in the notes you can have codes. We don't want them to be shown in the form view of invoice. task-6365267 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274996
Original PR description
When importing an xml, in the notes you can have codes. We don't want them to be shown in the form view of invoice. task-6365267 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274996
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in another country. The accounting flows must be then adjusted: - In company Origin, the invoice must be matched by a clearing entry - In company MoneyHandler, payment must match its move (if it exists) with a clearing entry. The payment move doesn't exist if `account_accountant` is installed but no O
Original PR description
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in…
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in another country. The accounting flows must be then adjusted: - In company Origin, the invoice must be matched by a clearing entry - In company MoneyHandler, payment must match its move (if it exists) with a clearing entry. The payment move doesn't exist if `account_accountant` is installed but no Outstanding account is configured on the payment method line. Same but opposite thing must happen for credit notes in company Origin that match a reimbursement in company MoneyHandler. Cancellation of a payment must be reflected on the entries: deleting when feasible, reversing when not (unless a lock date/hash is present, which would block the cancellation) _(To do: testing/review, credit note, cancellation/reversal of the payment)_ Task [link](https://www.odoo.com/odoo/project.task/6037525) task-6037525 Forward-Port-Of: odoo/odoo#259197
Resolved issues and error corrections
Indian GST reports now better match legal reporting requirements for imports. Import of services is no longer shown in GSTR-2B, and GSTR-3B import reporting has been aligned with the latest section changes for goods and services.
Original PR description
As per the law, import of services is not required to be shown in GSTR-2B. Therefore, the related report lines are removed in this commit. Additionally, GSTR-3B reporting is now handled according to the updated section changes for import of goods and services. task-6330737 Forward-Port-Of: odoo/enterprise#124316 Forward-Port-Of: odoo/enterprise#121925
This fixes incorrect web address handling when opening or leaving Studio from a project's task list. Users should no longer see navigation errors or extra record identifiers in the URL when using Studio in this flow.
Original PR description
Go on a project, then open its task list view Open studio with the menu item. At this point, studio is open but the url looks like: `/odoo/project/5/tasks/studio/5` the last `/5` is wrong ; this commit fixes this. Then, hit the browser's back button. There is an error because the active_id was not correctly set when leaving studio that way Try loading `/odoo/project/5/tasks/studio`, again, there is an error because the active_id is read from the wrong object Forward-Port-Of: odoo/enterprise#124412 Forward-Port-Of: odoo/enterprise#122405
This fixes an issue where attendee emails could show an outdated event start date after a multi-day event was rescheduled. Event registrations now refresh their stored date information when the event dates change, helping prevent incorrect details from being sent to attendees.
Original PR description
Steps to reproduce: ------------------------------------------------ 1. Install Event module 2. Create a multi day event 3. Create one attendee for the event 4. Change the event dates 5. Go to…
Steps to reproduce: ------------------------------------------------ 1. Install Event module 2. Create a multi day event 3. Create one attendee for the event 4. Change the event dates 5. Go to attendee and Click on Send by Email Observation: ------------------------------------------------ The event start date displayed in the email body is not updated after the event dates are modified. Issue: ------------------------------------------------ In `saas-18.2`, `event_begin_date` and `event_end_date` were simple related fields that automatically updated when their source fields changed. https://github.com/odoo/odoo/blob/saas-18.2/addons/event/models/event_registration.py#L57-L58 However, in `saas-18.3`, slots were introduced and these fields were converted to computed fields https://github.com/odoo/odoo/pull/205945/changes/e2bf8a89d6a50bd40f4673bef38176465f83ba0f * `event_begin_date` is made stored for cohort view grouping * However, the base compute method only depends on `event_id` and `event_slot_id` https://github.com/odoo/odoo/blob/ac37b479321dbe9dbf864e833900e043b1cc70df/addons/event/models/event_registration.py#L177-L180 * When you change `event.date_begin` or `event.date_end`, the registration records don't recompute because the dependency is on the `event_id`, not on the related date fields (`event_id.date_begin`, `event_id.date_end`) * Non-stored computed fields recalculate on-the-fly when accessed, so `event_end_date` appeared to work * Stored computed fields only recalculate when their explicit dependencies change Solution: ------------------------------------------------ * Corrected the dependencies of `_compute_event_begin_date` to recompute value on changing the date of the event opw-6284576 Forward-Port-Of: odoo/enterprise#120184
This fixes several details in Hong Kong IRD payroll reports so the reported tax year matches the employee's start or leaving date. It also ensures a required explanation is included when an employee leaving reason is marked as 'Other', helping submissions meet IRD certification requirements.
Original PR description
As we now have complete support for IRD reports (in master), we started to try to get our system certified by the IRD.
A first submission highlighted a few issues that we are now fixing.
From 19.0:
- In IR56F, the RTN_ASS_YR should be the tax year in which the employee left the company. E.g. after april, the next year.
- In the same report, if the code for the cessation reason is 5 (other), the reason MUST be provided.
From 19.2:
- Same change has to be done when setting RTN_ASS_YR for IR56G
- A same change has to also be done for IR56E, based on the date the employee joined the company.
task-6332150
Forward-Port-Of: odoo/enterprise#124256
Forward-Port-Of: odoo/enterprise#121877This fix makes HR and payroll processes explicitly look only at active records when selecting relevant versions or allocations. It prevents archived or inactive records from being included unexpectedly, reducing the risk of incorrect payroll or time-off calculations.
Original PR description
We cannot assume in methods that the active_test is set. Therefore, we should always add active=True in search domains. Forward-Port-Of: odoo/enterprise#124459
Creating digital certificates could fail for Peruvian companies when Chilean localization was also installed because a Chile-specific serial number field was incorrectly required. The fix limits that requirement to the Chilean localization, preventing unnecessary setup errors for other Latin American companies.
Original PR description
With a l10n_pe company and having a l10n_cl company installed: - Try to create a certificate in the settings, there is a missing field error. The template certificate_certificate_view_form have a required subject_serial_number field in l10n_cl but it shouldn't in other latam localization. opw-6274126 Forward-Port-Of: odoo/enterprise#120211
Budget Reports now load much faster for companies with large volumes of accounting, budget, and purchase data. This fixes timeouts that previously made the report unusable from budget records, improving day-to-day budget tracking and review.
Original PR description
**Description** Opening the Budget Report from any budget record times out on databases with significant data volume. The request to `budget.report/formatted_read_grouping_sets` consistently times…
**Description**
Opening the Budget Report from any budget record times out on databases
with significant data volume. The request to
`budget.report/formatted_read_grouping_sets` consistently times out,
making the Budget Report completely unusable.
**Root cause:**
`budget.report` is an SQL view that consists of 5 UNION ALL branches.
When the list view loads, the ORM translates the `budget_analytic_id`
domain into a WHERE clause on the outer query wrapping the full UNION
ALL subquery. PostgreSQL cannot push this filter through a UNION ALL as
it's a hard optimization barrier. It must fully materialize the subquery
regardless of which budget is being viewed.
**Fix:**
Override _search on budget.report to extract budget_analytic_id and
budget_line_id conditions from the incoming domain using the Domain API.
budget_line_id is rewritten as Domain('id', op, value) so _to_sql()
correctly emits bl.id in the raw SQL. The resulting domain is injected
in context under budget_line_domain and read in _get_bl_query,
_get_aal_query (base module), and _get_pol_query (purchase module) to
filter budget_line rows inside each branch's LEFT JOIN ON clause.
This also removes the budget_report_budget_line_ids context key from
budget_line._compute_all, unifying both filters under one mechanism.
---
On customer DB (568k `account_analytic_line`, 27k `budget_line`,
116k confirmed `purchase_order_line`, 114k posted vendor bill lines
with purchase link):
| Budget | Before | After |
|---|---|---|
| 8 lines, 730d span | timeout | 2.27s |
| 14 lines | timeout | 2.39s |
| 14 lines, 1095d span | timeout | 1.63s |
- Before: https://explain.dalibo.com/plan/ehed5eb8de251426
- After: https://explain.dalibo.com/plan/db8aef35cag9hg6f
opw-6098047
Forward-Port-Of: odoo/enterprise#119728
Forward-Port-Of: odoo/enterprise#114692Timesheet Assistant now recognizes events from coding editors correctly, so they display with the expected code-related icon. This makes activity information clearer for users reviewing their time and work events.
Original PR description
In the assistant, events from a coding editor use the "code" event type, which doesn't exist. This PR changes it to the correct value, which is "development". Task-6392585
VoIP call recordings made from Apple mobile devices could previously be saved as silent audio. The recording quality setting has been adjusted so these recordings capture sound correctly, helping users reliably review calls.
Original PR description
Before this commit, recording a VoIP phone call from an Apple mobile device generated a silent audio file. This issue happened because the configured 8000 `audioBitsPerSecond` value was too low. Apple mobile browsers strictly respect this value, while other browsers ignore it and default to a higher bitrate to 128000. Increasing `audioBitsPerSecond` to 32000 on WebKit browsers fixes the issue on Apple mobile devices. How to reproduce: - Set up a DIDWW user. - Enable call recording. - Make a call. - Open the call and play the recording. opw-6046534 Forward-Port-Of: odoo/enterprise#117885
The Dutch reports module no longer shows an outdated website link in its module information. This prevents users from being directed to an unrelated external site and keeps the module details accurate.
Original PR description
The URL leads to a website that has nothing to do with what it used to be so it needs to be removed. Task-6360682 Forward-Port-Of: odoo/enterprise#124380 Forward-Port-Of: odoo/enterprise#123019
Payroll frequency options now appear in the user's selected language across the main payroll app and country-specific payroll modules. This fixes a display issue where employees viewing payroll settings in French and other languages still saw untranslated salary schedule values.
Original PR description
Issue: ---------------------------------------- The values of the field `schedule_pay` aren't translated. Steps to reproduce: ---------------------------------------- - Switch the language to French - Open an employee form, "Paie" tab - The selection in the "Salaire" tab is not translated to French Cause: ---------------------------------------- When the selection values were moved to a method in 7a123d71925b25f26ba0a8abff0c4a159147bdd0. The strings were not declared as translatable. opw-6359395 Forward-Port-Of: odoo/enterprise#124349 Forward-Port-Of: odoo/enterprise#123718
Chilean Point of Sale receipts with official SII barcodes now print correctly again. This prevents checkout staff from encountering a crash when validating and printing orders for companies using Chilean electronic invoicing.
Original PR description
Steps to reproduce: - Have a Chilean company with DTE configured (a resolution number/date and a signed boleta/factura, so the order's move has an SII barcode) - Open a PoS session, pay an order and validate it Issue: The receipt fails to render and printing crashes with: `TypeError: ctx.image.l10n_cl_sii_barcode_image.to_base64 is not a function` Cause: Commit 0b50021bdae adapted this template as part of the BinaryValue migration (odoo/odoo#244421), calling `to_base64()` on the barcode image. However `l10n_cl_sii_barcode_image` is a computed `fields.Char` that already holds a base64 string (`_pdf417_barcode` returns `b64encode(...).decode()`), not a Binary, so it is never wrapped in a `BinaryValue`. Moreover, this template is also rendered client-side by the PoS QWeb engine, where the value loaded from the server is a plain string with no `to_base64` method either. opw-6389718
This update prevents an error that could occur when preparing signature fields while the system is running in debug mode. It ensures the signing interface correctly identifies the intended field even when extra behind-the-scenes comments are present, improving reliability for users testing or configuring documents.
Original PR description
Use lastElementChild when retrieving the sign item from the target element. In debug mode, inherited templates may introduce HTML comments into the DOM. Since lastChild return a comment node, accessing classList on the returned node raises an error. Using lastElementChild ensures that the last HTML element is always retrieved, regardless of comment nodes in the DOM. Forward-Port-Of: odoo/enterprise#123991 Forward-Port-Of: odoo/enterprise#122106
Belgian blackbox POS devices are now locked only after the first signed order, instead of when the POS is merely opened. This prevents businesses from accidentally tying a device to a POS before any sale is made, while also improving cost center handling and clearer blackbox error messages.
Original PR description
- `log_device` registered the device as soon as a blackbox POS was opened, so a device was locked before making any sale. Register it only once the config has at least one signed order, and add unit tests covering both branches. - correctly `trim()` cost center - display details inside syntax error & invalid input blackbox error popups
Opening Studio from the Working Files menu no longer triggers an error. This keeps the accounting report workflow stable and prevents interruptions for users customizing that view.
Original PR description
Open Studio while on "Working Files" menu and view. Before this commit, the python raised an error becaude at some point `record[False]` (returning the current virtual record) was put in the return values of the onchange. After this commit, there is no error. runbot-error-941248 Forward-Port-Of: odoo/enterprise#124239
This update prevents failures when preparing PDF documents for signing in environments using Python 3.10. It keeps the Sign app compatible with the PDF library version used in that setup, helping automated builds and document signing flows run reliably.
Original PR description
The flatten_pdf helper was written against the snake_case pypdf API (append_pages_from_reader, .pages, get_object), but that API does not exist on PyPDF2 1.26.0, the camelCase-only release still pinned on Python 3.10 in 19.0, so runbot builds on 3.10 failed with an AttributeError on 'BrandedFileWriter' object has no attribute 'append_pages_from_reader'. Switching to the camelCase spelling (appendPagesFromReader, getNumPages/getPage, getObject) fixes it. RunbotError: https://runbot.odoo.com/odoo/runbot.build.error/941405 Forward-Port-Of: odoo/enterprise#124482
This fix prevents the time off grid from crashing when an employee does not have a work schedule configured. The system now reliably uses the employee’s resource information when loading the grid, so affected users can continue viewing time off data normally.
Original PR description
When the `resources_per_tz` parameter was added to the `_work_intervals_from_batch` method, it wasn't properly added to all method calls in the code base. In this case, a user without a work schedule set would have no resource pulled uo for them when trying to render the time off grid causing an error. This PR adds this parameter into that flow, because if an employee is defined in the context then they will defenitionally have a resource which can be used as a reference point. opw-6366955
This fixes Norwegian SAF-T exports so account grouping codes are read from the correct part of the account number. Businesses using customized account numbers will get grouping codes that better match the official Norwegian chart of accounts, reducing reporting errors.
Original PR description
Steps to reproduce: - change 1920 Banck account to 19204321 - go in general ledger and export to "SAF-T" Issue: The grouping code is 4321 Grouping code should match official grouping code. As a matter of fact the chart of account seems to match thos grouping account if we slice them correctly. opw-6285078 Forward-Port-Of: odoo/enterprise#122213 Forward-Port-Of: odoo/enterprise#121932
The test setup for Odoo Cloud Notifications now mirrors real behavior by registering devices only for internal users. This reduces false test conditions and helps ensure notification-related checks stay accurate without affecting day-to-day users.
Original PR description
Only devices of internal users are registered in order to send them Odoo Cloud Notifications (OCN). However, the test setup registers devices for non-internal users as well. This commit ensures devices are only registered for internal users. Forward-Port-Of: odoo/enterprise#119956
This fix prevents the Belgian payroll app from failing during installation when some setup data is not loaded yet. Businesses can now install or update the app on populated databases without encountering this blocking error.
Original PR description
Currently during the installation process the compute is called before the data of the module is loaded. The compute uses a env.ref that searches for an external id that will only exist later on. this creates a traceback in populated databases, since the compute will be processed, and the app won't be installed. Here we cannot overwrite the auto_init since the field is not stored The only option left was to adapt the comupte to not throw a traceback in case the fields are not found, and instead proceed with the compute/installation opw-6340800 Forward-Port-Of: odoo/enterprise#123744
Uruguayan electronic invoices now avoid extra blank lines when combining addenda text with terms and conditions. This helps keep addenda content on the expected page when it fits, preventing unnecessary separate-page rendering in generated CFE PDFs.
Original PR description
## Context When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from…
## Context
When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from the invoice's `narration` field, the resulting addenda string could end up with unnecessary blank lines between the two sections, causing the addenda to be rendered on a separate page even when the logical content fits within the 6-line threshold.
## Root Cause
`_l10n_uy_edi_get_addenda` joins both parts without stripping whitespace from either of them first, and adds two lines between addendas and terms and conditions:
addenda = addenda + "\n\n" + term_and_conditions if addenda else term_and_conditions
Two sources independently introduce extra newlines around the separator:
1. **Addenda content** — `_get_legends` returns the raw `content` field value of each addenda record. These fields commonly end with a trailing `\n`, so the addenda string already ends with a newline before the `"\n"` separator is concatenated.
2. **`html2plaintext`** — the `narration` field is stored as HTML. When converted to plain text, `html2plaintext` typically wraps paragraph content in leading/trailing newlines.
The combination of the trailing `\n` from the addenda, the explicit `"\n\n"` separator, and the leading/trailing `\n` from `html2plaintext` produces 2–3 consecutive newlines, which `splitlines()` counts as blank lines.
A realistic 4-line addenda + 1-line narration thus produces **7 lines** instead of the expected 5, crossing the 6-line threshold in `_get_report_params` and triggering `adenda=true` — which forces the addenda onto a separate page unnecessarily.
## Steps to Reproduce
1. Configure a `l10n_uy_edi.addenda` record of type `addenda` with multi-line content (4 lines)
2. Create and confirm an invoice with `narration` set to a short single-line term
3. Generate the CFE PDF via Uruware.
4. Observe that the addenda is rendered on a separate page despite the logical content being only 5 lines.
<img width="1042" height="448" alt="image" src="https://github.com/user-attachments/assets/b538211c-5f37-4648-979d-99cd75cf31c2" />
## Fix
Strip leading and trailing whitespace (including newlines) from both parts before joining them. The ternary is also replaced with an explicit `if/else` for clarity:
def _l10n_uy_edi_get_addenda(self):
addenda = self.l10n_uy_edi_document_id._get_legends("addenda", self)
if self.narration:
term_and_conditions = html2plaintext(self.narration).strip()
if addenda:
addenda = addenda.strip() + "\n" + term_and_conditions
else:
addenda = term_and_conditions
return self._l10n_uy_edi_clean_non_ascii_chars(addenda)
This guarantees exactly one `\n` separator between sections regardless of how the content fields were stored or how `html2plaintext` formatted the narration.
The threshold logic in `_get_report_params` is unchanged: addendas that genuinely exceed 6 lines (after wrapping at 140 chars) continue to be printed on a dedicated page.
Result
<img width="1117" height="456" alt="image" src="https://github.com/user-attachments/assets/3a2d942c-8c37-40f6-bc25-470c0bd25b08" />
Forward-Port-Of: odoo/enterprise#119283New employee contracts created from a template now correctly inherit the template's analytic distribution. This helps payroll accounting stay aligned with intended cost allocations and avoids manual re-entry or reporting gaps.
Original PR description
Problem: When creating a new contract from a template, the analytic distribution field is not copied from the template to the contract. Steps to reproduce: 1. Create a contract template with an analytic distribution. 2. Create a new contract for an employee from the template. 3. Check the analytic distribution field on the new contract. 4. Notice how the analytic distribution field is empty, even though it was set on the template. Cause: The field is not included in the list of whitelisted fields to copy from the template. https://github.com/odoo/odoo/blob/0133e46f89df7dce8c39d2bacd29579d57a83fad/addons/hr/models/hr_version.py#L443 opw-6370781 Forward-Port-Of: odoo/enterprise#123955
This fix ensures the Avalara tax integration proxy connection is neutralized where required, preventing unintended external tax service communication in copied or non-production environments. It helps reduce the risk of accidental transactions or data exchange when databases are prepared for safe use.
Original PR description
backport of https://github.com/odoo/enterprise/pull/122024 no-task
This fixes an error that could occur when validating stock operations using the Kenyan OSCU stock integration. It ensures the process uses the updated stock movement information, helping affected workflows complete reliably.
Original PR description
**CAUSE** super()._action_done() delete a record from self, and return a new recordset of stock.moves. We filter the old recordset instead of filtering the new one, leading to an MissingError traceback. **STEP TO REPRODUCE** On a fresh db, install: `l10n_ke,l10n_ke_edi_oscu,l10n_ke_edi_oscu_mrp,l10n_ke_edi_oscu_pos,l10n_ke_edi_oscu_stock,l10n_ke_edi_tremol,l10n_ke_hr_payroll,l10n_ke_hr_payroll_account,l10n_ke_reports` and run `TestKitPicking.test_add_sml_with_kit_to_confirmed_picking`. runbot-241262
Fixes an issue where German point-of-sale receipts could fail to download or print when Fiskaly certification data was present. This helps shops using German fiscal certification reliably access receipts from the backend.
Original PR description
With fiskaly in production, when printing the pos receipt, it crashes because the tss values dictionnary is not correctly interacted with. To reproduce: install l10n_de_pos_cert create a DE shop activate fiskaly and the tss in the settings of the POS create an order in the POS and pay it go to the backend, open the pos order and download the receipt it will crash To reproduce without production credentials, you can not activate fiskaly and the tss but still create and pay the pos order. Then, you can change the pos.config to add the l10n_de_fiskaly_tss_id and change the pos.order to add the l10n_de_fiskaly_time_start. Then download the receipt. opw-6356628 Fixes https://github.com/odoo/enterprise/pull/115676 Forward-Port-Of: odoo/enterprise#123473
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one. This happens when a many2one field appears twice in the same view tree with different widget configurations — one plain, one with `relatedFields`. The concrete trigger: 1. `stock.picking.batch` has an x2many `picking_ids` whose inline list/kanban view contains `partner_id` as a plain many2one.
Original PR description
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one.…
`completeActiveField` crashes with "Cannot read properties of undefined (reading 'activeFields')" when the `extra` argument carries a `.related` object but the target `activeField` does not have one.
This happens when a many2one field appears twice in the same view tree with different widget configurations — one plain, one with `relatedFields`. The concrete trigger:
1. `stock.picking.batch` has an x2many `picking_ids` whose inline list/kanban view contains `partner_id` as a plain many2one. `extractFieldsFromArchInfo` creates an activeField for `partner_id` with no `.related` property.
2. `website_sale_stock` inherits the `stock.picking` form view and adds a second `partner_id` node with `widget="pickup_location_many2one"`. That widget declares `relatedFields` (`pickup_location_data`), which `Field.parseFieldNode` converts into a synthetic `views.default`. When `extractFieldsFromArchInfo` processes the inline form view of `picking_ids`, the resulting activeField for `partner_id` gets a `.related` object from those fields.
3. `extractFieldsFromArchInfo` then merges the form view fields into the list view fields via `completeActiveFields`. For `partner_id` the field already exists in the list's activeFields (without `.related`), so `completeActiveField` is called. It checks `if (extra.related)` — true — then immediately accesses `activeField.related.activeFields`, which is undefined → crash.
The sibling function `patchActiveFields` already handles this exact scenario correctly:
activeField.related = activeField.related || { activeFields: {}, fields: {} };
Apply the same defensive initialisation in `completeActiveField`.
Part-of: odoo/odoo#160187
Related: odoo/enterprise#59935
Related: odoo/upgrade#6315
Backport-of odoo/odoo@03c0d6F
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276489The `bus subscription is refreshed when channel is joined` test is sometimes failing. This test doesn't make sense: it opens the command palette and wait for a subscription to be made. However, a subscription is only done when needed (opening the thread or being a member of the channel). The step was satisfied by luck. This commit fixes the test to reflect production code: the subscription is made once the channel is opened. runbot-941462 Description of the issue/feature this PR a
Original PR description
The `bus subscription is refreshed when channel is joined` test is sometimes failing. This test doesn't make sense: it opens the command palette and wait for a subscription to be made. However, a subscription is only done when needed (opening the thread or being a member of the channel). The step was satisfied by luck. This commit fixes the test to reflect production code: the subscription is made once the channel is opened. runbot-941462 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274741
Previously: 1.`purchase_cdnur_regular` section was assigned to credit/debit notes of: - import of goods - import of services without RCM However: - import of goods should be handled through bill of supply - import of services without RCM is not possible Therefore, with this commit, such journal items are moved to `purchase_out_of_scope`. 2.`purcha
Original PR description
Previously:
1.`purchase_cdnur_regular` section was assigned to credit/debit notes of:
- import of goods
- import of services without RCM However:
- import of goods should be handled through bill of supply
- import of services without RCM is not possible Therefore, with this commit, such journal items are moved to `purchase_out_of_scope`.
2.`purchase_imp_services` section included import of services both with and
without RCM. Since import of services without RCM is not possible, those
journal items are now moved to `purchase_out_of_scope`.
3.Credit/debit notes of import of services with RCM were previously moved to
`purchase_out_of_scope`, which was incorrect. With this commit, they are now
correctly moved to `purchase_imp_services`.
task-6330737
Forward-Port-Of: odoo/odoo#276301
Forward-Port-Of: odoo/odoo#272453We cannot assume in methods that the active_test is set. Therefore, we should always add active=True in search domains. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276540
Original PR description
We cannot assume in methods that the active_test is set. Therefore, we should always add active=True in search domains. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276540
If we are in a case of a salary simulation, we don't care about future public holidays. The unlink done in _delete_future_public_holidays_timesheets was causing some cache invalidations which were messing up with the original offer. Forward-Port-Of: odoo/odoo#276520
Original PR description
If we are in a case of a salary simulation, we don't care about future public holidays. The unlink done in _delete_future_public_holidays_timesheets was causing some cache invalidations which were messing up with the original offer. Forward-Port-Of: odoo/odoo#276520
Loading a certificate could raise an unhandled exception instead of failing gracefully. Clearing the content, uploading a bundle with a corrupted certificate block, or handling certificates with unsupported signature algorithms or malformed extensions all could end up in a traceback. Guard those paths to ensure loading errors are handled. opw-6370529 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I
Original PR description
Loading a certificate could raise an unhandled exception instead of failing gracefully. Clearing the content, uploading a bundle with a corrupted certificate block, or handling certificates with unsupported signature algorithms or malformed extensions all could end up in a traceback. Guard those paths to ensure loading errors are handled. opw-6370529 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276193 Forward-Port-Of: odoo/odoo#275325
When creating an event in Odoo and syncing it to Outlook, the event appeared in the correct slot on Outlook's calendar grid, but its detail panel showed start/end times labelled as UTC, causing a mismatch between the user's wall-clock time and what was displayed in Microsoft Outlook. The sync now sends the event in the organizer's local timezone with a matching timezone label, so Outlook displays the same wall-clock time and timezone that was entered. task-6167258 Description of the issue/
Original PR description
When creating an event in Odoo and syncing it to Outlook, the event appeared in the correct slot on Outlook's calendar grid, but its detail panel showed start/end times labelled as UTC, causing a mismatch between the user's wall-clock time and what was displayed in Microsoft Outlook. The sync now sends the event in the organizer's local timezone with a matching timezone label, so Outlook displays the same wall-clock time and timezone that was entered. task-6167258 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276556 Forward-Port-Of: odoo/odoo#262488
Issue: ---------------------------------------- When generating work entries with the CRON "Generate Missing Work Entries", the name of the work entries is always in English. Steps to reproduce: ---------------------------------------- - Create a new employee, setup a running contract for them - Run the schedule action "Generate Missing Work Entries" - In Payroll > Work Entries, search for the work entries of the new employee - Their name are in French Cause: ----------------------
Original PR description
Issue: ---------------------------------------- When generating work entries with the CRON "Generate Missing Work Entries", the name of the work entries is always in English. Steps to reproduce: ---------------------------------------- - Create a new employee, setup a running contract for them - Run the schedule action "Generate Missing Work Entries" - In Payroll > Work Entries, search for the work entries of the new employee - Their name are in French Cause: ---------------------------------------- When running the cron, `self.env.lang` is `False` so the text aren't translated. Solution: ---------------------------------------- In `_cron_generate_missing_work_entries()` we specify `self.env.user.lang` in the context. As `_cron_generate_missing_work_entries()` uses the root user to run, the language of the work entries will be the one specified on Odoobot. opw-6369109 Forward-Port-Of: odoo/odoo#275952
The "bus subscription is refreshed when channel is joined/left" tests were flaky: - `mockDate` needs 2 digit date/time parts. The format used here didn't always produce them, so it silently fell back to a past date. That was enough to make the "left" test pass even without actually leaving. - The "left" test never actually left the channel: a confirm dialog blocked it. - The "join" test never actually joined the channel. - The tests expected `runAllTimers` to guarantee that every initial sub
Original PR description
The "bus subscription is refreshed when channel is joined/left" tests were flaky: - `mockDate` needs 2 digit date/time parts. The format used here didn't always produce them, so it silently fell back…
The "bus subscription is refreshed when channel is joined/left" tests were flaky: - `mockDate` needs 2 digit date/time parts. The format used here didn't always produce them, so it silently fell back to a past date. That was enough to make the "left" test pass even without actually leaving. - The "left" test never actually left the channel: a confirm dialog blocked it. - The "join" test never actually joined the channel. - The tests expected `runAllTimers` to guarantee that every initial subscription was done, but thats not the case, making the number of `subscribe` calls non-deterministic (e.g. flushing calls to `bus_service.add` but not ensuring the worker received them through its message port, and triggered the debounced `updateChannels`). Fixing the tests exposed a real bug: `memberBusSubscription` is meant to trigger a refresh whenever membership changes relative to the bus start time. As a boolean, "member, no refresh needed" and "not a member" are indistinguishable (both `false`), so leaving a channel joined before the bus started never changed the value and never triggered a refresh. This PR add a third state so membership and non-membership stay distinguishable regardless of when the bus started. runbot-941462 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276205 Forward-Port-Of: odoo/odoo#275938
**PROBLEM** `_cron_migrate_local_to_cloud_storage()` is used to migrate attachment to a cloud storage. It delete the attachment from the database, rendering it innacessible from the server-side. The problem is it can delete attachment used in business logic. **STEP TO REPRODUCE** 1. Install and configure cloud_storage + a provider (e.g. cloud_storage_google) and cloud_storage_migration. 2. Install l10n_mx_edi and stamp a customer invoice. The CFDI XML is stored as an ir.attachment that:
Original PR description
**PROBLEM** `_cron_migrate_local_to_cloud_storage()` is used to migrate attachment to a cloud storage. It delete the attachment from the database, rendering it innacessible from the server-side. The…
**PROBLEM** `_cron_migrate_local_to_cloud_storage()` is used to migrate attachment to a cloud storage. It delete the attachment from the database, rendering it innacessible from the server-side. The problem is it can delete attachment used in business logic. **STEP TO REPRODUCE** 1. Install and configure cloud_storage + a provider (e.g. cloud_storage_google) and cloud_storage_migration. 2. Install l10n_mx_edi and stamp a customer invoice. The CFDI XML is stored as an ir.attachment that: - is referenced by a business Many2one l10n_mx_edi.document.attachment_id (copied into account.move.l10n_mx_edi_cfdi_attachment_id), - has res_field = NULL (record attachment, not a field binary), - is posted to the chatter → it has a row in message_attachment_rel. 3. Add account.move to cloud_storage_migration_message_models and set a low enough cloud_storage_min_file_size. 4. Run the cron _cron_migrate_local_to_cloud_storage. 5. Open the invoice and trigger any recompute of the EDI chain (e.g. Update Payments, a reconciliation, or re-stamping). **FIX** Filters out attachment linked to a model listed by the already existing function `_get_cloud_storage_unsupported_models()` opw-6347198 Forward-Port-Of: odoo/odoo#275945
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-bill
Original PR description
## Steps to reproduce: - Install sale_timesheet - Create a timesheet with a Sale order item linked to it - Change the sale order item on that timesheet - Change the project linked to the timesheet to a non-billable project - Notice the sale order item still linked to the timesheet ## Cause: When computing the so_line we filter out the records that has is_so_line_edited as true, so when changing the SOL before changing the project we don't reset so_line field when setting a non-billable project. ## Fix: We reset the is_so_line_edited field to false when changing the project to a non-billable one. opw-6311549 Forward-Port-Of: odoo/odoo#276386 Forward-Port-Of: odoo/odoo#275919
Before this commit, when making a test print from the POS backend, the following issues would occur: - Very slow response - Missing cut, and extra 'A' character is printed This commit fixes both these issues. The slow response is avoided by not performing the network tests when they aren't used in the printed receipt. The cut issue is solved by appending a newline character to the message. task-6391141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/s
Original PR description
Before this commit, when making a test print from the POS backend, the following issues would occur: - Very slow response - Missing cut, and extra 'A' character is printed This commit fixes both these issues. The slow response is avoided by not performing the network tests when they aren't used in the printed receipt. The cut issue is solved by appending a newline character to the message. task-6391141 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276524
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes. The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` fro
Original PR description
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session…
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes.
The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` from Python 3.14's stricter base64 validation in the `datas` auto-decode path. That switch silently changed what ends up on disk (`datas` decodes its input, `raw` does not)
Storing that string in the binary `raw` field encodes it as UTF-8, so the file on disk ends up as the literal ASCII of the base64 text. The attachment is served as `application/pdf` but the browser receives base64 ASCII and cannot preview or download the PDF.
Call `b64decode(response)` before storing so the attachment contains the actual PDF bytes.
OPW-6302803
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274931
Forward-Port-Of: odoo/odoo#270759Steps to reproduce: - Open the website editor. - Go to the Theme tab. - Inspect the Primary or Secondary color picker title. => The title prop is undefined. - Go to a product page with several product images. - Edit the carousel thumbnail position option. => The Left and Bottom button titles are undefined. Before this commit, some builder option titles were passed as OWL expressions instead of translated string props. After this commit, these titles use translated string props and are
Original PR description
Steps to reproduce: - Open the website editor. - Go to the Theme tab. - Inspect the Primary or Secondary color picker title. => The title prop is undefined. - Go to a product page with several product images. - Edit the carousel thumbnail position option. => The Left and Bottom button titles are undefined. Before this commit, some builder option titles were passed as OWL expressions instead of translated string props. After this commit, these titles use translated string props and are properly available to the builder components. task-6034856 Forward-Port-Of: odoo/odoo#275609 Forward-Port-Of: odoo/odoo#275235
Issue: ---------------------------------------- The units (day, year, etc.) aren't being translated in the Milestones view. Steps to reproduce: ---------------------------------------- - Switch the language to French - Go on an Accrual plan form view - In the milestones view, the units aren't translated Cause: ---------------------------------------- We input the key value of the selections fields `start_type` and `added_value_type`. These values aren't translated. Solution: --
Original PR description
Issue: ---------------------------------------- The units (day, year, etc.) aren't being translated in the Milestones view. Steps to reproduce: ---------------------------------------- - Switch the language to French - Go on an Accrual plan form view - In the milestones view, the units aren't translated Cause: ---------------------------------------- We input the key value of the selections fields `start_type` and `added_value_type`. These values aren't translated. Solution: ---------------------------------------- We create a dictionary with the same keys as the fields and a translated value as values. In the view, we read the values of the dictionary to get the translated units. opw-6367235 Forward-Port-Of: odoo/odoo#276327 Forward-Port-Of: odoo/odoo#275575
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or
Original PR description
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. -…
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or decreased depending on the operation performed. - As a consequence, creating or updating a sale order triggers a write to this `qty_available` field on the related product. This write happens under the current user's permissions, so users who only have read access to products (but can create/edit sale orders) hit an `AccessError`, since they lack write access on `product.product`. Solution: --- - Use `sudo()` when accessing the required product quantity information to ensure the operation can be completed without requiring additional product access rights. The same issue also occurs when confirming a purchase order. [commit]: https://github.com/odoo/odoo/commit/ca96992919b11105da44238c3e522f8eec4a740b opw-6290608 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276253 Forward-Port-Of: odoo/odoo#270067
Currently, if a discount program has a max discount, this value is not taken into account when computing the amount left to discount. Steps to reproduce: ------------------- * Create a loyalty program, it needs a reward as such: 100% discount on product A, maximum discount 100 * Create a discount code program, it needs a reward as such: 15% discount on product A * Open pos, make an order, add product A, change its price to 1000 * In this order: * Add the loyalty program reward (100%)
Original PR description
Currently, if a discount program has a max discount, this value is not taken into account when computing the amount left to discount. Steps to reproduce: ------------------- * Create a loyalty…
Currently, if a discount program has a max discount, this value is not taken into account when computing the amount left to discount. Steps to reproduce: ------------------- * Create a loyalty program, it needs a reward as such: 100% discount on product A, maximum discount 100 * Create a discount code program, it needs a reward as such: 15% discount on product A * Open pos, make an order, add product A, change its price to 1000 * In this order: * Add the loyalty program reward (100%) > Only 100$ discount since it's the max * Add the discount code reward > Nothing happens, no reward line added, no message saying the code didn't applied Why the fix: ------------ When we compute the amount left to discount `getDiscountable` when we try to apply the last reward we have `discount = 1` as the loyalty reward line is set up to a 100% discount. This ends up leaving `remainingAmountPerLine[line.uuid]` to be 0. The current state of the code does not take into account the maximum discount which, if triggered, means we still have something remaining to discount. We introduce this discount in a straightforward way for the moment. We simply compare the theoretical discount `remainingAmountPerLine[line.uuid] * discount` to the max. This is a simple version intended to make this work. In the future we could imagine taking all the lines the discount applies to and compute the proportion that is applied to the specific line. This approach was already discussed in the past for fixed amount discounts and was not implemented as it would make the code more complex (and unreadable) than needed. We're assuming the same approach applies here. We make it work first and see if there's ever a need to complexify it. opw-6129625 Forward-Port-Of: odoo/odoo#262499
_**Steps to reproduce:**_ * Install `l10n_pl_edi` and enable **Allow KSeF integration** from Accounting settings. * Switch to a Polish company. * Create an EU customer with a valid VAT number. * Create a sale order containing a service product taxed with **0% EU S**. * Confirm the sale order and create a down payment invoice. * Send the invoice to KSeF and inspect the generated XML. **_Observed behavior:_** * The generated KSeF XML does not contain the `P_13_9` field. **_Cause:_
Original PR description
_**Steps to reproduce:**_ * Install `l10n_pl_edi` and enable **Allow KSeF integration** from Accounting settings. * Switch to a Polish company. * Create an EU customer with a valid VAT number. *…
_**Steps to reproduce:**_ * Install `l10n_pl_edi` and enable **Allow KSeF integration** from Accounting settings. * Switch to a Polish company. * Create an EU customer with a valid VAT number. * Create a sale order containing a service product taxed with **0% EU S**. * Confirm the sale order and create a down payment invoice. * Send the invoice to KSeF and inspect the generated XML. **_Observed behavior:_** * The generated KSeF XML does not contain the `P_13_9` field. **_Cause:_** * For down payment invoices involving services taxed with **0% EU S**, the value corresponding to `P_13_9` was not being assigned during XML generation, causing the tag to be omitted from the exported KSeF document. **_Fix_**: * Populate the value of `P_13_9` during KSeF XML generation for service down payment invoices, ensuring the field is correctly included in the exported XML. * This PR updates the computation of tag `P_13_10` to ensure consistency with the expected reporting logic, where the tag is computed solely from `K_31`. Here is the [Documentation](https://ksef.podatki.gov.pl/media/gtjhkeek/information-sheet-on-the-fa-3-logical-structure-04032026.pdf) link for the reference of the Ksef structure. opw-6294181 Forward-Port-Of: odoo/odoo#275755 Forward-Port-Of: odoo/odoo#270986
When Google sends a recurrence with UNTIL in UTC (UNTIL=...Z), users in timezones behind UTC can get one extra occurrence on the boundary day. Google's UNTIL represents the last allowed start in UTC, but that UTC date fell into the previous local day. Because Odoo was comparing event start times as naive local datetimes against a cutoff derived from the wrong date, the boundary occurrence passed the check and was created. Steps to reproduce: 1. Set the user's timezone to a UTC-negative offse
Original PR description
When Google sends a recurrence with UNTIL in UTC (UNTIL=...Z), users in timezones behind UTC can get one extra occurrence on the boundary day. Google's UNTIL represents the last allowed start in UTC,…
When Google sends a recurrence with UNTIL in UTC (UNTIL=...Z), users in timezones behind UTC can get one extra occurrence on the boundary day. Google's UNTIL represents the last allowed start in UTC, but that UTC date fell into the previous local day. Because Odoo was comparing event start times as naive local datetimes against a cutoff derived from the wrong date, the boundary occurrence passed the check and was created. Steps to reproduce: 1. Set the user's timezone to a UTC-negative offset (e.g. America/Argentina/Buenos_Aires, UTC-3). 2. In Google Calendar, create a weekly recurring event (e.g. every Thursday at 12:00 local). 3. Edit the series with "This and following events" so the old series ends with UNTIL set to 02:59:59 UTC of the next day (= 23:59:59 local of the last valid occurrence day). 4. Sync with Odoo -> an extra event is created on the day after the last valid Thursday, which does not exist in Google Calendar. opw-6024835 Forward-Port-Of: odoo/odoo#274593 Forward-Port-Of: odoo/odoo#265297
Description of the issue/feature this PR addresses: Current behavior before PR: meeting.rrule is stored as a full dateutil rrule string, e.g.: "DTSTART:20250218T113209\nRRULE:FREQ=YEARLY;COUNT=720" Passing the full multi-line string as a single RRULE property value causes vobject to emit two RRULE lines, where the first one ("RRULE:DTSTART:...") has no FREQ. This is not standard-compliant and is rejected by calendar clients (e.g. Thunderbird: "invalid frequency null"). Desired behavior af
Original PR description
Description of the issue/feature this PR addresses:
Current behavior before PR: meeting.rrule is stored as a full dateutil rrule string, e.g.: "DTSTART:20250218T113209\nRRULE:FREQ=YEARLY;COUNT=720"
Passing the full multi-line string as a single RRULE property value causes vobject to emit two RRULE lines, where the first one ("RRULE:DTSTART:...") has no FREQ. This is not standard-compliant and is rejected by calendar clients (e.g. Thunderbird: "invalid frequency null").
Desired behavior after PR is merged: Only a single RRULE line is generated.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274490webhooks don't work because of it Forward-Port-Of: odoo/odoo#275932 Forward-Port-Of: odoo/odoo#275845
Original PR description
webhooks don't work because of it Forward-Port-Of: odoo/odoo#275932 Forward-Port-Of: odoo/odoo#275845
Click Working Files menu, then open studio. Before this commit there was an error, because the accounting code tried to check access rights on an new record (no id) After this commit there is no crash. runbot-error-941248 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276269
Original PR description
Click Working Files menu, then open studio. Before this commit there was an error, because the accounting code tried to check access rights on an new record (no id) After this commit there is no crash. runbot-error-941248 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276269
**Issue:** The course-specific options should appear after the forum page options, but they are displayed before them. **Reason:** Due to refactoring [commit], the course-specific options were inserted after a generic page hook. Since `website_slides_forum` is loaded before the forum module, the options were added before the forum page options, resulting in an incorrect order. **Steps to Reproduce:** 1. Go to /forum. 2. Enter edit mode. 3. Notice that the course-specific options a
Original PR description
**Issue:** The course-specific options should appear after the forum page options, but they are displayed before them. **Reason:** Due to refactoring [commit], the course-specific options were…
**Issue:** The course-specific options should appear after the forum page options, but they are displayed before them. **Reason:** Due to refactoring [commit], the course-specific options were inserted after a generic page hook. Since `website_slides_forum` is loaded before the forum module, the options were added before the forum page options, resulting in an incorrect order. **Steps to Reproduce:** 1. Go to /forum. 2. Enter edit mode. 3. Notice that the course-specific options appear before the forum page options. **Fix:** Extend the forum page options instead of the generic page hook ensuring course-specific options to be inserted after the forum page options. | Before | After | | ----- | -----| | <img width="285" height="267" alt="image" src="https://github.com/user-attachments/assets/73fb1f27-4edc-47eb-8bed-a873aea8a427" /> | <img width="285" height="268" alt="image" src="https://github.com/user-attachments/assets/d56aac2a-c66f-4c81-b04e-f204613d2c9c" /> | [commit]: https://github.com/odoo/odoo/commit/3f63c76da0b867facd5ed021beea79efabea15f1 task-[6359989](https://www.odoo.com/odoo/project/974/tasks/6359989)
Steps to reproduce: =================== 1. Drop an "Events" block on a website page. 2. In edit mode, try selecting the inner text by clicking multiple times. => Uncaught client error: TypeError: Cannot read properties of undefined (reading 'nodeType'). Root cause: =========== When a mouse selection crosses an uncrossable element, the selection restriction plugin moves the focus to the deepest position of the element sibling adjacent to the uncrossable one. When the first selec
Original PR description
Steps to reproduce: =================== 1. Drop an "Events" block on a website page. 2. In edit mode, try selecting the inner text by clicking multiple times. => Uncaught client error: TypeError:…
Steps to reproduce: =================== 1. Drop an "Events" block on a website page. 2. In edit mode, try selecting the inner text by clicking multiple times. => Uncaught client error: TypeError: Cannot read properties of undefined (reading 'nodeType'). Root cause: =========== When a mouse selection crosses an uncrossable element, the selection restriction plugin moves the focus to the deepest position of the element sibling adjacent to the uncrossable one. When the first selected node is itself an uncrossable element (event cards are `div` elements) that has no previous/next element sibling, `node.previousElementSibling` is null and `tempFocusNode` was never assigned by a previous iteration, so it is undefined. `nodeSize` then reads `nodeType` on undefined and throws. The plugin only exists from saas-19.3, which is why the issue is not reproducible on earlier versions. Fix: ==== When there is no sibling to place the focus on, fall back to the boundary just outside the uncrossable node itself (`leftPos` when selecting left to right, `rightPos` when selecting right to left) instead of calling `nodeSize`/`getDeepestPosition` with undefined. opw-6362955 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
**Steps to reproduce:** - Create a fiscal position T1, with detect automatically - Create another one T2, without the detect automatically - When looking at the fiscal positions list, make sure T1 is on top, followed by T2 - Go to the PoS settings, check flexible taxes - Put T2 as default and in allowed, don't put T1 in allowed - Go to the PoS, chose a customer - The fiscal position is T1 even though it's not allowed **Why the fix:** Currently, the fiscal position is chosen like thi
Original PR description
**Steps to reproduce:** - Create a fiscal position T1, with detect automatically - Create another one T2, without the detect automatically - When looking at the fiscal positions list, make sure T1 is…
**Steps to reproduce:** - Create a fiscal position T1, with detect automatically - Create another one T2, without the detect automatically - When looking at the fiscal positions list, make sure T1 is on top, followed by T2 - Go to the PoS settings, check flexible taxes - Put T2 as default and in allowed, don't put T1 in allowed - Go to the PoS, chose a customer - The fiscal position is T1 even though it's not allowed **Why the fix:** Currently, the fiscal position is chosen like this in order: - A FP specified on the customer's profile - A FP detected with the detect automatically setting - The default FP from the PoS settings When we have a tie, it's the first one in the fiscal positions list that is chosen. Before this commit, we did not check that the fiscal position was allowed to be used in the PoS, so we just fetched whatever fiscal position fit the best for a given customer and didn't check if we could actually use it. We now make sure that the fiscal position we try to use is allowed in the current PoS, and if it's not we fall back to the default one. opw-6032031 Forward-Port-Of: odoo/odoo#271343
The service worker required for push notifications is only available to internal users. This commit fixes the test setup by ensuring non-internal users are no longer registered, matching the expected flow. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269210
Original PR description
The service worker required for push notifications is only available to internal users. This commit fixes the test setup by ensuring non-internal users are no longer registered, matching the expected flow. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269210
Steps to reproduce: 1. Drop a .s_tabs snippet 2. Click inside a tab to move the selection in it 3. Press backspace (remove each tab name + the last one should be empty) 4. Click on the "+" in the sidebar to add a Tab => Crash or on step 3: 3. Press backspace to delete one tab => Check the DOM: the tab has been removed, but the tab-pane element is still in the DOM and won't be deleted. This is easily fixed by adding `oe_unremovable` on tab links. task-4671317 Forward-Port-Of: odo
Original PR description
Steps to reproduce: 1. Drop a .s_tabs snippet 2. Click inside a tab to move the selection in it 3. Press backspace (remove each tab name + the last one should be empty) 4. Click on the "+" in the sidebar to add a Tab => Crash or on step 3: 3. Press backspace to delete one tab => Check the DOM: the tab has been removed, but the tab-pane element is still in the DOM and won't be deleted. This is easily fixed by adding `oe_unremovable` on tab links. task-4671317 Forward-Port-Of: odoo/odoo#275240
The more() action helper caches the More Actions object and only refreshes its inner actions list, leaving disabledCondition unchanged. As a result, if the dropdown is created while disabled, it remains disabled even after the composer is re-enabled. Individual actions (e.g., Attach files) don't exhibit this issue because they use a dynamic callback `(({ owner }) => owner.areAllActionsDisabled)` that is evaluated when needed. task-6393956 --- I confirm I have signed the CLA and read th
Original PR description
The more() action helper caches the More Actions object and only refreshes its inner actions list, leaving disabledCondition unchanged. As a result, if the dropdown is created while disabled, it remains disabled even after the composer is re-enabled.
Individual actions (e.g., Attach files) don't exhibit this issue because they use a dynamic callback `(({ owner }) => owner.areAllActionsDisabled)` that is evaluated when needed.
task-6393956
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prIt can happen that _ref_vat has some lazy translate object. Without the self.env._ the translation would be ignored. (no translation language detected, skipping translation) runbot-941504 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275561
Original PR description
It can happen that _ref_vat has some lazy translate object. Without the self.env._ the translation would be ignored. (no translation language detected, skipping translation) runbot-941504 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275561
Version: --------- - 19.0+ Steps to Reproduce: ----------------------- 1. Install sale_management, purchase, stock modules. 2. Create a storable product with Tracking: By Lot, 3. Create two Purchase Orders, each for 10 units. Receive PO-1 → 10 units with tagged as lot-1 Receive PO-2 → 10 units with tagged as lot-2 4. Create two Sale Orders: SO-1 → deliver 2 units from lot-1 (validate) SO-2 → deliver 4 units from lot-2 (validate) 5. Open Inventory > Reporting > Stock,
Original PR description
Version: --------- - 19.0+ Steps to Reproduce: ----------------------- 1. Install sale_management, purchase, stock modules. 2. Create a storable product with Tracking: By Lot, 3. Create two Purchase…
Version:
---------
- 19.0+
Steps to Reproduce:
-----------------------
1. Install sale_management, purchase, stock modules.
2. Create a storable product with Tracking: By Lot,
3. Create two Purchase Orders, each for 10 units.
Receive PO-1 → 10 units with tagged as lot-1
Receive PO-2 → 10 units with tagged as lot-2
4. Create two Sale Orders:
SO-1 → deliver 2 units from lot-1 (validate)
SO-2 → deliver 4 units from lot-2 (validate)
5. Open Inventory > Reporting > Stock,
click "Total Value", then check the "Remaining Quantity" column
Issue:
-------
Observed : remaining_qty = 10 for lot-2 receipt, 4 for lot-1 receipt
Expected : remaining_qty = 8 for lot-1 receipt (10−2), 6 for lot-2 receipt (10−4)
Cause:
--------
When the "Remaining Quantity" column is computed, the following call
chain executes:
stock.move._compute_remaining_qty()
→ calls product.product._get_remaining_moves()
→ calls product._run_fifo_get_stack() ← HERE is the problem
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L372
`_get_remaining_moves` calls `_run_fifo_get_stack()` with NO lot
argument. Inside `_run_fifo_get_stack`, because no lot is given, it
computes the stack size from the TOTAL product qty across all lots:
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L583
fifo_stack_size = 14 (10 received lot-1 + 10 received lot-2
− 2 delivered lot-1 − 4 delivered lot-2)
It then builds a domain to find incoming moves with NO lot filter:
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L607
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L614-L618
```Domain: [('is_in', '=', True), ('product_id', '=', X)]
↳ returns both receipts ordered:
[lot-2 receipt (10 qty), lot-1 receipt (10 qty)]
then walks this list consuming `fifo_stack_size = 14`:
So it take: [move_lot1_receipt(10)] First Lot
remaining_qty_on_first = min(10, 14) = 10
after consuming fifo_stack_size → 14−10=4 left → move_lot1 gets 4
```
So back in `_get_remaining_moves`:
qty_by_move = {
lot-2 receipt → 10, ← wrong (should be 6)
lot-1 receipt → 4, ← wrong (should be 8)
}
- The root cause: `_run_fifo_get_stack` is designed for products that
have one shared FIFO stack. For lot-valuated products, each lot is an
independent inventory layer. Running a single combined stack mixes both
lots together, so the deductions (2 from lot-1, 4 from lot-2) are not
attributed to the correct receipt moves — the algorithm just consumes
from the oldest receipts first with no awareness of which lot was
actually delivered.
Fix:
-----
`_run_fifo_get_stack` already accepts a `lot=` argument that:
- sets `fifo_stack_size = lot.product_qty` (correct per-lot qty)
- adds `('move_line_ids.lot_id', 'in', lot.id)` to the domain
so only the receipts that touched that specific lot are returned
The only missing piece was calling it per lot instead of once globally.
- With the fix, the stack for each lot is built correctly:
lot-1: fifo_stack_size = 8 → lot-1 receipt remaining_qty = 8 ✓
lot-2: fifo_stack_size = 6 → lot-2 receipt remaining_qty = 6 ✓
---
opw-6311341
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#272411Issue: There is a missing closing curly bracket on line 67 in odoo/addons/stock/static/src/stock_forecasted/forecasted_details.xml (View) This PR corrects this error opw-6367046 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274575
Original PR description
Issue: There is a missing closing curly bracket on line 67 in odoo/addons/stock/static/src/stock_forecasted/forecasted_details.xml (View) This PR corrects this error opw-6367046 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274575
When the pos_discount module is installed while a session is open (the typical case: the user enables "Global Discounts" in the PoS settings while the PoS is running), `_default_discount_value_on_module_install` skipped the configs having a non-closed session (or any rescue session, even a closed one). Those configs ended up with the Global Discount feature enabled but no `discount_product_id`, and opening the PoS then raised "A discount product is needed to use the Global Discount feature." wit
Original PR description
When the pos_discount module is installed while a session is open (the typical case: the user enables "Global Discounts" in the PoS settings while the PoS is running),…
When the pos_discount module is installed while a session is open (the typical case: the user enables "Global Discounts" in the PoS settings while the PoS is running), `_default_discount_value_on_module_install` skipped the configs having a non-closed session (or any rescue session, even a closed one). Those configs ended up with the Global Discount feature enabled but no `discount_product_id`, and opening the PoS then raised "A discount product is needed to use the Global Discount feature." with no way to recover other than manually re-saving the PoS settings. The skip was introduced in 13.0 by 4c4adf472453 because, at the time, `pos.config.write()` refused any modification while a session was open, which made the module installation crash. That blanket restriction has since been narrowed to a few specific fields (`module_pos_restaurant`, `payment_method_ids`, `active`), so writing `discount_product_id` on a config with an open session is now perfectly valid. Remove the obsolete exclusion so that all configs get the default discount product at install time, regardless of their session state. opw-6385274 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the messag
Original PR description
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the message as read and drops the needaction counter to 0 before markAsRead runs. mark_all_as_read is then skipped and the step assertion receives nothing. Give the member a non-zero separator (the pre-existing message is already read) so opening the channel no longer fetches around 0, leaving mark_all_as_read as the flow that marks the inbox message read. https://runbot.odoo.com/odoo/error/243651 Forward-Port-Of: odoo/odoo#276181
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't appear anymore - Refreshing shows it but will remove it from the other tab **Issue:** Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`. Computed fields are not recomputed on the receiver side after value inserti
Original PR description
**Steps to reproduce:** - Install Contacts app - Open any record - Go to the chatter - Create an activity with a description - Duplicate the tab - Go back to the initial tab - Description doesn't…
**Steps to reproduce:**
- Install Contacts app
- Open any record
- Go to the chatter
- Create an activity with a description
- Duplicate the tab
- Go back to the initial tab
- Description doesn't appear anymore
- Refreshing shows it but will remove it from the other tab
**Issue:**
Behavior comes from the broadcasting of activity changes between tabs `new browser.BroadcastChannel("mail.activity.channel");`.
Computed fields are not recomputed on the receiver side after value insertion in `_onActivityBroadcastChannelMessage` (also related components are not (re)mounted, e.g. when a new activity is created the other tab doesn't show it without a refresh).
This means that `isNoteEmpty` keeps its default value `true` (added by `this.toData()`) and the `note` stays hidden here [1]:
```xml
<div t-if="!props.activity.isNoteEmpty" class="o-mail-Activity-note text-break" t-out="props.activity.note"/>
```
**Fix:**
Remove computed fields in activity `serialize` before broadcasting them to ensure they don't force the default value.
(note installing `calendar` in 19.3+ removes this issue due to [2] which overrides the condition on `isNoteEmpty`)
[1] https://github.com/odoo/odoo/commit/eb9f0658c3da1a9fef69f1cc1117c2d44f9d61b1
[2] https://github.com/odoo/odoo/commit/44e2c2c5ca07849fd8964140f3ca61122c47f0c6
opw-6247412
Forward-Port-Of: odoo/odoo#276032
Forward-Port-Of: odoo/odoo#275528When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking p
Original PR description
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via…
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking push notifications after the first subscription renewal. Fix by extracting the applicationServerKey from the new subscription's options and encoding it as a base64url string (without padding) — matching the existing logic in webclient.js _arrayBufferToBase64(). Description of the issue/feature this PR addresses: Current behavior before PR: Subscriptions don't get renewed causing push notifications to stop eventually. Desired behavior after PR is merged: Subscriptions get renewed successfully. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276085 Forward-Port-Of: odoo/odoo#275217
**Steps to reproduce:** - Make a product with a category - In the category, make the inventory valuation to automated - Set the income and expense account - Set a cost for the product - Make a fiscal position and set it as default for the PoS - In the Account Mapping tab, map the income and expense to two other accounts - Go to the PoS - Make a sale for that product, without invoice - Close the session and in the backend check the session - Check the journal entries - The income acc
Original PR description
**Steps to reproduce:** - Make a product with a category - In the category, make the inventory valuation to automated - Set the income and expense account - Set a cost for the product - Make a fiscal…
**Steps to reproduce:** - Make a product with a category - In the category, make the inventory valuation to automated - Set the income and expense account - Set a cost for the product - Make a fiscal position and set it as default for the PoS - In the Account Mapping tab, map the income and expense to two other accounts - Go to the PoS - Make a sale for that product, without invoice - Close the session and in the backend check the session - Check the journal entries - The income account has been mapped to the fiscal position's - The outcome account stayed the same as in the category's **Why the fix:** When we invoice an order, the income and expense accounts are immediately updated, in a different place than if it has not been invoiced. At the session's closure, we update the accounts for every order that hasn't been invoiced. In this flow, the account mapping defined on the fiscal position was not applied, so we took the one defined on the product's category. The income account was already mapped as we need to do it earlier than the session closure, so it had already been set as the right one before our flow. For the expense account, we only need it at this specific time, so we can map it as the session's closure. We now map the account depending on the fiscal position if we are able to find one, otherwise, we use the category's default as we did before. opw-6171677 Forward-Port-Of: odoo/odoo#276158 Forward-Port-Of: odoo/odoo#266700
Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a compa
Original PR description
Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a company related one. Note: this is how things were already in 19.0 and this is how they should be, the changes happened by mistake as an unwanted side effect of commit 4f6068a6c88bf0530c19254df403e1194823b415 task-[6226138](https://www.odoo.com/odoo/project/967/tasks/6226138) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265196
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue method tied to the cron was blindly batching sms's belonging to multiple companies without an sms_api context. The error results because the _send method that's called expects a singleton company when it tries to set the sms_api for the record set, but this isn't the case when the selected sm
Original PR description
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue…
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue method tied to the cron was blindly batching sms's belonging to multiple companies without an sms_api context. The error results because the _send method that's called expects a singleton company when it tries to set the sms_api for the record set, but this isn't the case when the selected sms batch is multi company. After this commit, the _process_queue method now follows the same pattern as the send method, grouping by sms_api / company within the batch, and eliminating the need to check for singleton, as all calls to _send will now have the sms_api context passed in. ### Steps to Reproduce on fresh 19.0 db: 1. Make sure sms / sms_twilio are installed. 2. Create two companies with their own SMS config. 3. Create two sms records, one with each company. 4. Ensure the state of the sms's is 'outgoing'. 5. Execute the SMS Queue Manager Cron. Observe the traceback: ValueError: Expected singleton... opw-6371272 Forward-Port-Of: odoo/odoo#276426
Miscellaneous changes
Fix field name typo. @qrtl QT6381 Forward-Port-Of: odoo/odoo#276119
Original PR description
Fix field name typo. @qrtl QT6381 Forward-Port-Of: odoo/odoo#276119