Wednesday, September 9, 2026
34 changes · master
Resolved issues and error corrections
This fix ensures invoices sent to ZATCA are properly marked as submitted even when the user only has read-only access to journals. This prevents the same invoice from being sent again accidentally, reducing duplicate submissions and reconciliation issues.
Original PR description
**Steps to reproduce:** This issue is hard to reproduce because it requires a live ZATCA connection: - As a user with read-only permission on journals, send an invoice to ZATCA. - You get an access error on the journal, and the invoice is unchanged (You can try sending it again to ZATCA). **Issue:** What happens is: - A user with read-only permission on journals sends an invoice to ZATCA. - If ZATCA responds with a 200 (successfully submitted), we try to write on the field `journal.l10n_sa_latest_submission_hash` - With no write permissions, the write fails and all changes are rolled back (on odoo, not on ZATCA) - We can send the invoice again to ZATCA, resulting in duplicates. **Solution:** - Added a sudo when writing on the field: `journal.l10n_sa_latest_submission_hash` opw-6320179 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287081 Forward-Port-Of: odoo/odoo#278728
The French simplified profit and loss report now includes all relevant expense lines in the Operating expenses total. This helps companies using the French fiscal declaration avoid understated expense totals and improves accuracy of the 2033 B report.
Original PR description
**Steps to reproduce:** - Install the `l10n_fr_reports` module and switch to the FR Company. - Navigate to Invoicing > Reporting > Fiscal Declaration and select `2033 B`. - Observe the formula of `A…
**Steps to reproduce:** - Install the `l10n_fr_reports` module and switch to the FR Company. - Navigate to Invoicing > Reporting > Fiscal Declaration and select `2033 B`. - Observe the formula of `A - Accounting income` > `Operating expenses (II)`. **Observation:** The formula does not include the balances of `FR_2033_B_c244`, `FR_2033_B_c250`, and `FR_2033_B_c252`. **Root Cause:** At [1], the formula for `Operating expenses (II)` is missing the balances of fields `244, 250, and 252`, even though these lines are part of the operating expenses section. **Fix:** This commit ensures that `Operating expenses (II)` displays the correct total balance. **Reference**: <img width="1422" height="490" alt="6482797" src="https://github.com/user-attachments/assets/95b35ac7-f410-413b-8ef9-29f97f16c35a" /> [1]: https://github.com/odoo/enterprise/blob/54bfcbee37a241ae70b9bebb9cf23d76172e18fe/l10n_fr_reports/data/fiscal_declaration/report_2033_B_simplified_profit_loss.xml#L302 opw-6482797 Forward-Port-Of: odoo/enterprise#129348
This fixes several issues in the time off request process, especially around partial days, hourly requests, timezone handling, and unusual work schedules. Employees and managers should see more accurate dates, durations, and calendar behavior when creating or editing leave requests.
Original PR description
Here is the leaves behavior: Stored field readonly (shouldn't be edited): `date_from/to` -> UTC reference Stored fields editable to request: - time type in days: `request_date_from/to` - time type in…
Here is the leaves behavior: Stored field readonly (shouldn't be edited): `date_from/to` -> UTC reference Stored fields editable to request: - time type in days: `request_date_from/to` - time type in halves: +`request_date_from/to_period`, `request_duration` (multidays/solo) - time type in hours: +`request_hour_from/to` (not in the view) Computed field, (editable for a time type in hours): `request_date_hour_from/to` -> converted to the reader tz It means no more cycle exits: `request_date_hour_from/to` edited => inverse will update requests stored fields => date_from/to will be recomputed based on the request stored fields The purpose of using `request_date_hour_from/to` is to display for the user the same range of hours for employees with different tz Other little fixes around: - _to_utc(): round the hour to the whole minute, where the carry can reach the hour, and stop at the end of the day, as float_to_time takes neither a minute 60 nor an hour 24. - _split_leaves(): both parts are whole where they are cut and carry the request_duration their periods describe, dates included, instead of the half day copied from the original. - _get_hours_for_date(): a day the schedule leaves out borrows the hours of the nearest day it names, not the widest hours of the whole schedule, which no single day of a two week rota works; a schedule naming no hour at all falls back on an average day centred on midday, where it used to leave the request without any extent. - _compute_last_several_days(): a request whose end precedes its start is one being edited, not one of several days, so moving one bound of the pair before the other catches up no longer snaps the duration back onto whole days. task-6459867
The Time Off Gantt views now better reflect the updated fixed leave request process and align dates with each user's local wall-clock time, like calendar views. This helps HR teams and managers see leave schedules more accurately in overview and management screens.
Original PR description
Gantt adapted with the new fixed leave request process Gantt view timezoned based on the wall clock of the user as calendar views now (Overview and Management) task-6459867
Users sending Colombian support documents to DIAN now see a clear message when the required operation mode has not been configured. This replaces a confusing server error with guidance on what is missing and where to fix it, helping businesses complete electronic document submissions faster.
Original PR description
**Steps to reproduce:** * Install `l10n_co_dian`. * Configure a company with the Colombian localization. * Set up a DIAN operation mode for Electronic Invoices only (no Support Documents mode). *…
**Steps to reproduce:** * Install `l10n_co_dian`. * Configure a company with the Colombian localization. * Set up a DIAN operation mode for Electronic Invoices only (no Support Documents mode). * Create a vendor bill marked as a Support Document and click **Send to DIAN**. **Observed behavior:** * A cryptic server error is raised: *"TypeError: unsupported operand type(s) for +: 'int' and 'str'"* * No actionable information is shown to the user. **Cause:** * `_add_document_config_vals` assigns `vals['l10n_co_dian_operation_mode']` via `.filtered()`, which returns an empty recordset when no matching operation mode exists. * Accessing a `Char` field on an empty recordset returns `False` (a `bool`, which is a subclass of `int` in Python). * Concatenating `False` with strings in the `sha384` hash calculation raises the `TypeError`. * The missing-mode guard only existed in the commercial events path, not in the main invoice export path. **Fix:** * Raise a descriptive `UserError` that tells the user which mode is missing and where to configure it. opw-6499321 Forward-Port-Of: odoo/enterprise#130853 Forward-Port-Of: odoo/enterprise#128935
Reconciliation model labels are now applied using the company’s language, so automated and manual reconciliation create journal items with the same wording. This prevents misleading labels when users work in different languages or models are duplicated and edited.
Original PR description
### Problem `label` on `account.reconcile.model.line` is a **translatable** field, but its value is written onto the journal item created when the model is applied (`account.move.line.name`). That…
### Problem
`label` on `account.reconcile.model.line` is a **translatable** field, but its value is
written onto the journal item created when the model is applied (`account.move.line.name`).
That means the label is read in the language of whoever applies the model:
- a user working in another language writes the translated value;
- the auto-reconciliation cron writes the **source** value, since it runs as OdooBot.
So the very same reconcile model ends up writing two different labels on the journal items,
depending on who applied it.
### How it shows up
It becomes visible when a reconcile model is created by **duplicating** an existing one and
the label is then edited while working in a non-source language. The translation holds the
new text, while the source value silently keeps the label of the original model — and the
source value is exactly the one the cron writes. The result is a set of journal items where
some carry the intended label and some carry the label of an unrelated model.
### Fix
The journal item belongs to the company, so the label is read in the **company** language
via a small `_get_aml_label()` helper, instead of the language of the current environment.
It falls back to the current behaviour when the company has no language set.
`_prepare_aml_vals()` is the only place in 18.0 that reads `self.label` for the journal item.
### Test
Adds `TestBankRecWidget.test_auto_reconcile_model_label_uses_company_lang`: a reconcile model
whose line label is translated in the company language, applied by the auto-reconciliation
cron running in the source language, and asserts the journal item carries the company-language
label.
```
odoo -d <db> -u account_accountant --test-enable --stop-after-init \
--test-tags /account_accountant:TestBankRecWidget.test_auto_reconcile_model_label_uses_company_lang
```
Without the fix the test fails with `[{'name': 'Frais bancaires'}] != [{'name': 'Bank fees'}]`;
with the fix it passes. The full `account_accountant` suite was also run on a clean 18.0
database: 203 tests, 0 failed, 0 errors.
Forward-Port-Of: odoo/enterprise#130865
Forward-Port-Of: odoo/enterprise#128433This fix restores correct height settings for website map sections, including simple maps that do not use a Google API key. It also brings back support for using map snippets in page templates with padding, helping website pages display maps as intended.
Original PR description
[Commit ace2431d] modified the way the height of Google maps snippets is computed. On simple map snippets (the one available without a Google API key), the height option didn't work properly or wasn't even available anymore. Additionally, [commit af49dbe8] added support to include the map snippet within a `t-call` on XML templates, together with a `padding` parameter. This was lost and not adapted when the other commit was merged in master. [Commit ace2431d]: https://github.com/odoo/odoo/commit/ace2431ddfdd7e99cf13e9a8458a109e92dd1a6a [commit af49dbe8]: https://github.com/odoo/odoo/commit/af49dbe8ccfbd6b31c899ea7699dff38d178ad4b task-6503185
Fixed an issue that caused an error when employee cashiers performed Cash In/Out operations in Point of Sale. The cash movement now records the employee correctly, helping stores continue cashier workflows without interruptions.
Original PR description
Steps to reproduce: -- - Enable Employee Login in PoS. - Log in as an employee cashier. - Perform a Cash In/Out move. - Error! Issue: -- An error occurs when making a cash move while logged in as an employee cashier. Cause: -- The cash move request array passed employee information in place of the partner ID, resulting in a database type error. Fix: -- Passed the employee ID as a separate parameter in the cash move payload, and updated the backend session to save the employee ID on the cash move record. Task- 6445993
Product identifiers sent to Google Analytics now match the identifiers used in Google Merchant Center feeds. This helps Google Ads connect Shopping ad clicks with purchases more reliably, reducing attribution mismatch warnings.
Original PR description
**Issue:** When google analytics (GA) and google merchant center (GMC) are setup, Google Ads diagonistic reports that item IDs cannot be matched to Merchant Center. **Why this happens:** `_get_google_analytics_data` sets `product.barcode or product.id` for `item_id`, while `product.feed._prepare_gmc_items` defaults to `product.default_code or product.id` for the feed's `id` field. Google Ads/Analytics attribution relies on GA4's `item_id` matching GMC's `id` for the same product to connect Shopping ad clicks to purchase events. **References:** https://support.google.com/merchants/answer/6324405?sjid=15224664355638221483-NC https://support.google.com/google-ads/answer/14943675?hl=en opw-6443326 Forward-Port-Of: odoo/odoo#287074 Forward-Port-Of: odoo/odoo#285010
This fix makes the website builder correctly remember changes to form field default values, so undo actions restore the page as expected. It also prevents stale values from remaining when switching date fields to date-and-time fields, improving editing reliability for website forms and translations.
Original PR description
The `value` property of the elements is not tracked by the history plugin, because `MutationObserver` does not produce mutations for that. This commit uses custom mutations when the `value` is changed, to restore the previous value on undo. Steps to reproduce: - Open website builder - Add a form - Set a "Default Value" on a text field - Press enter (to end preview) - Undo (with the button, or with focus out of the option's input) - Bug: The value shown in the page did not revert with undo Similar bug in translate mode task-6229671 Forward-Port-Of: odoo/odoo#287325 Forward-Port-Of: odoo/odoo#281232
Fixed an issue that prevented manufacturing orders from being created on small screens when work orders and operation instructions were enabled. The change ensures the related work order keeps its operation details, so required quality checks are created as expected.
Original PR description
Steps to reproduce --- 1. Enable Work Orders. 2. On a product's BoM, add an operation carrying an Instructions step. 3. On a small screen, create a manufacturing order for that product, set the…
Steps to reproduce --- 1. Enable Work Orders. 2. On a product's BoM, add an operation carrying an Instructions step. 3. On a small screen, create a manufacturing order for that product, set the product and save. Expected: the MO is saved and the generated work order carries its BoM operation. Actual: saving raises `Missing required field 'Product' (product_id) for model 'Stock Move'`, and once that is bypassed the generated work order has no operation, so its `quality_point_ids` stays empty and no `quality.check` is created. Issue --- On a small screen the MO form resolves its x2many relations to their kanban sub-views, so the fields carried in the onchange and save payloads are only those the active sub-view declares; two relations lose data this way. `move_finished_ids` is an invisible relation that always holds the product's finished move, built on the draft by `_compute_move_finished_ids` through `_get_moves_finished_values` with `product_id` and `product_uom_qty` set. https://github.com/odoo/odoo/blob/c9012f5ae48e29ff49391b722ed798237c448805/addons/mrp/models/mrp_production.py#L1354-L1371 https://github.com/odoo-dev/odoo/commit/c9012f5ae48e29ff49391b722ed798237c448805 gave that field `mode="list,kanban"` while only a `<list>` sub-view is defined for it. https://github.com/odoo/odoo/blob/c9012f5ae48e29ff49391b722ed798237c448805/addons/mrp/views/mrp_production_views.xml#L405-L410 On a small screen the field resolves to kanban mode, but since no kanban sub-view exists its field spec comes out empty, so on the first save the client sends the finished move as an empty create and the `stock.move.product_id` NOT NULL constraint aborts the whole save, so no MO can be created on mobile. The field is invisible and never rendered, so it has no use for a kanban view in the first place; dropping the kanban mode keeps it on its list spec, which carries `product_id`, on every screen size, while the visible Components and By-Products lists are unaffected because they resolve a populated kanban sub-view. The `workorder_ids` kanban sub-view must likewise carry `operation_id`, or the generated work order is saved without an operation and its `quality_point_ids` stays empty, so no `quality.check` is created. https://github.com/odoo-dev/odoo/commit/e3f6cef5cf94391b5c018c5eb44ce1ddee99290b had restored the invisible `operation_id` on that kanban and https://github.com/odoo-dev/odoo/commit/6318895101435a0a9d4bdff0b8dbfbb17179378e dropped it again in a kanban rework, so it is restored. opw-6488529 X-original-commit: https://github.com/odoo-dev/odoo/commit/af70218f325147a51ca6e46f4827d697c7ed7801
Fixed an issue where code editor fields could cause invoice forms to crash when document extraction features were installed. The change keeps field detection accurate without changing how the editor looks or behaves.
Original PR description
Fix for #286871. opw-6543997 ### The problem `web.AceField` and `web.IrUIViewAceField` render their root div with the `o_field_widget` class, but the `Field` wrapper already puts that class on the…
Fix for #286871.
opw-6543997
### The problem
`web.AceField` and `web.IrUIViewAceField` render their root div with the
`o_field_widget` class, but the `Field` wrapper already puts that class on the
div it renders, along with the `name` attribute. Every ace field therefore
renders a nested `o_field_widget` with no name:
```html
<div name="my_field" class="o_field_widget o_field_code ...">
<div class="o_field_widget oe_form_field o_ace_view_editor oe_ace_open">
<div class="ace_editor">... <textarea class="ace_text-input">
```
Any code that goes up from a DOM node with `closest(".o_field_widget")` to find
which field was clicked stops on the inner div, finds no `name` and cannot
resolve the field.
`iap_extract` does exactly that, in a `focusin` listener on `window`, and
`account_invoice_extract` installs its renderer for `account_move_form` with
`force: true` — so it runs on every invoice form. With no name on the inner div
it builds `"<parent_field>.null"`, and since the name now contains a dot,
`getBoxType` takes the x2many branch:
```js
modelFieldType = this.props.record.data[parentField]?._config.fields[fieldName]?.type;
```
For a `widget="code"` field, `record.data[parentField]` is a plain string. It is
truthy, so the optional chaining does not short-circuit, but it has no
`_config` → `undefined.fields` → `TypeError: Cannot read properties of
undefined (reading 'fields')`, and the whole form breaks.
### Steps to reproduce
1. Install `account_invoice_extract`.
2. Put a `widget="code"` field on the `account.move` form view (with
`l10n_ar_edi` installed there are already two on the ARCA tab).
3. Give the field a value — with an empty value the error does not happen.
4. Enable developer mode and click inside the code editor.
Reproduced on a plain 19.0 runbot build.
### The fix
Drop `o_field_widget` from the root div of both ace templates, since the
wrapper already provides it. The only rule that relied on both classes being on
the same element was `.o_field_widget { &.o_ace_view_editor { ... } }` in
`fields.scss`; it becomes a descendant selector, so the rendering is unchanged.
`account_invoice_extract` / `iap_extract` could be made more defensive as well
(`?._config?.fields[...]`, or not building a `parent.child` name when the
closest `.o_field_widget` has no `name`), but that lives in enterprise and the
duplicated class looks like the actual root cause.Depreciation models that are already linked to assets can no longer be deleted accidentally. This prevents assets from losing required setup information and becoming impossible to reset, cancel, or manage.
Original PR description
**Steps to reproduce:** * Install the **Accounting** (`account`). * Create a **Depreciation Model** (e.g. Linear, 5 years). * Create an asset, assign the depreciation model, and confirm it. * Go to…
**Steps to reproduce:** * Install the **Accounting** (`account`). * Create a **Depreciation Model** (e.g. Linear, 5 years). * Create an asset, assign the depreciation model, and confirm it. * Go to **Accounting → Configuration → Depreciation Models** and delete the model used by the running asset. * Try to **Reset to Draft** on **asset**. **Observed behavior:** * The depreciation model is deleted silently. * The asset's `model_id` FK becomes `NULL`, causing all related fields (`method`, `method_number`, `method_period`, `journal_id`, etc.) to become empty. * Any subsequent attempt to cancel, reset to draft, or delete the asset fails with a **missing required field** error, leaving the asset permanently unmanageable. **Cause:** * `account.depreciation.model` had no `@api.ondelete` guard — deletion was entirely unprotected, unlike `write()` which already blocks edits on models used by running assets. * `model_id` on `account.asset` had no `ondelete` constraint, so the database silently NULLed the FK on model deletion. **Fix:** * Add an `ondelete='restrict',` on a asset's `model_id` field to prevent deletion of a depreciation model that is in use by any asset. opw-6233210 Forward-Port-Of: odoo/enterprise#130859 Forward-Port-Of: odoo/enterprise#126979
French point-of-sale sales that encounter missing e-invoicing data will now download a standard invoice instead of an incorrect pro-forma invoice. This keeps the sale flow consistent while avoiding confusion for customers and staff; failed electronic transmission is not sent externally until the data issue is resolved.
Original PR description
## Steps to reproduce: - Create a customer with France E-Invoicing (UBL2.1) as eInvoice format in a French company - Go to the PoS, select that partner - Make a sale and ask for an invoice - The…
## Steps to reproduce: - Create a customer with France E-Invoicing (UBL2.1) as eInvoice format in a French company - Go to the PoS, select that partner - Make a sale and ask for an invoice - The downloaded invoice will be a pro-forma invoice ## Why the fix: The pro-forma should not be used here, it is because it is used as a fallback when we get an error while trying to print the invoice. https://github.com/odoo/odoo/blob/4a508586970e44367bbdbbb3cbe88ffb5a1eadb7/addons/account/models/account_move.py#L6187-L6204 As we get an error while trying to send the data with this setup, it goes to the fallback and prints a pro-forma invoice, even though this should not be the case, a regular invoice would do. This happens because when an error is found, we do not populate invoice_pdf_report_id, so it goes to the fallback. We now check if there are any errors in the order, and if there are and the customer requests an ubl_21_fr invoice, we just print the invoice as it is, without going to the pro-forma fallback, as this is not the intended flow. With this fix, we now have the same flow as we do in the sales module, that allows the sale even if the customer has missing data. It will just print the invoice and allow the sale but won't send anything to external entities. opw-6428369 Forward-Port-Of: odoo/odoo#286644 Forward-Port-Of: odoo/odoo#281420
Cancelled retail orders and order lines are now saved and marked correctly when sent to Fiskaly. This helps German point-of-sale certifications reflect cancellations accurately, reducing compliance and reporting issues.
Original PR description
In this commit: --------------- - We now store cancelled retail orders on the server and send the `storno` flag as `true` for cancelled lines and orders to ensure proper handling in Fiskaly. task: 6326042 Relataed PR: https://github.com/odoo/odoo/pull/277648 Forward-Port-Of: odoo/enterprise#130751 Forward-Port-Of: odoo/enterprise#120410
Fixed an issue that prevented invoices for Saudi Arabian customers from being printed when a tax identification number was present. This ensures Saudi e-invoicing users can print confirmed invoices without encountering an error.
Original PR description
Printing an invoice for a Saudi partner raises an error. Steps to reproduce the error: - Install ``l10n_sa_edi`` module with demo data - Switch to My Saudi Arabia Company - Create a new Partner A >…
Printing an invoice for a Saudi partner raises an error. Steps to reproduce the error: - Install ``l10n_sa_edi`` module with demo data - Switch to My Saudi Arabia Company - Create a new Partner A > Country: Saudi Arabia > VAT Number: 311111111111113 > Click on + Button > Click Tax Identification Number > Save - Create a new invoice > Customer: Partner A > Add a product > Confirm > Print the invoice Traceback: ```py AttributeError: 'account.edi.xml.ubl_21.zatca' object has no attribute '_l10n_sa_get_tin_from_vat' ``` https://github.com/odoo/odoo/blob/2e2054d4203f6453fb35ec6037cda0e2e9033cc3/addons/l10n_sa_edi/models/zatca_ubl_mixin.py#L154-L156 In [Commit], ``_l10n_sa_get_tin_from_vat()`` method is called on self to retrieve ``identification_number``. however, self is an ``account.edi.xml.ubl_21.zatca`` record, while ``_l10n_sa_get_tin_from_vat()`` is a method of ``res.partner``. So, It will lead to the above traceback when printing the invoice. Solution: Call the ``_l10n_sa_get_tin_from_vat()`` method on the partner record instead of self. [Commit]: https://github.com/odoo/odoo/commit/227f61e2cb8582d0c3269bb0ae7256250563847a sentry-7717905761 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287173
Users can now download files opened in the file viewer from Discuss channels without seeing an error. This restores the expected download behavior by using the request type accepted by those file links, while preserving correct downloaded filenames.
Original PR description
Description of the issue/feature this PR addresses: Downloading an attachment from the file viewer fails with a `405 Method Not Allowed` error when the attachment belongs to a Discuss channel. I've…
Description of the issue/feature this PR addresses:
Downloading an attachment from the file viewer fails with a `405 Method Not Allowed` error when the attachment belongs to a Discuss channel.
I've already submitted a ticket to Odoo: #6430586
Steps to reproduce (on a 18.0 runbot):
1. open Discuss and send an image in a channel
2. click the image to open the file viewer
3. click the download button (either the one in the header or the one in the bottom
toolbar)
The server rejects the request:
```
POST /discuss/channel/1/image/519861?filename=image.png&unique=32647b0f&download=true 405
```
and the user gets a `RPC_ERROR: Arbitrary Uncaught Python Exception` dialog reporting `405 Method Not Allowed`.
Cause: `download()` always issues a POST request, while the routes serving the attachments of a discuss channel only allow GET:
* `/discuss/channel/<int:channel_id>/attachment/<int:attachment_id>`
* `/discuss/channel/<int:channel_id>/image/<int:attachment_id>`
so the request never reaches the controller. Downloading the very same attachment from the attachment card in the conversation still works, because that one is a plain anchor navigation (GET).
This is a regression from fb152985f4b8 ("[FIX] web: download FileViewer files via blob helper"), which routed the file viewer download through `download()` in order to honor the filename sent by the server in the `Content-Disposition` header.
Only 18.0 is affected: saas-18.1 and saas-18.2 do not have the commit that introduced the regression, and from saas-18.3 on, the `urlRoute` override was dropped and channel attachments are served through the standard `/web/content` and /web/image` routes, which are not restricted to GET.
The download is still sent with POST on those branches though, hence forward-porting this up to master.
Current behavior before PR:
Downloading a Discuss channel attachment from the file viewer raises a 405 error and the file is not downloaded. Images and other file types are equally affected.
Desired behavior after PR is merged:
The file is downloaded, keeping the filename advertised by the server. The download is performed with a GET request through `downloadFile()`, which still goes through the blob helper, so the fix of fb152985f4b8 is preserved. This is already the way a file is downloaded from its url in `readonly_file.js`.
Added a test that downloads an image attachment of a channel from the file viewer and asserts the request is a GET on the channel attachment route. It fails before this fix with `POST /discuss/channel/1/image/1`.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287172
Forward-Port-Of: odoo/odoo#279232Address details from MapBox location lookups are now matched by their actual labels instead of relying on a fixed order. This prevents incorrect or failed address creation when MapBox returns missing or reordered fields, improving reliability for users who depend on map-based address entry.
Original PR description
The reverse-geocoding call to MapBox asked for five types of information (street, city, postcode, region, country) and read them back from the response by fixed position in the array. MapBox doesn't always return every requested type, or in the same order; if one is missing, every field after it shifts by one position, so the address ended up built from the wrong data, or the code crashed reading a feature that wasn't there. Each field is now looked up by its own type tag instead of its position in the array, so it's matched correctly regardless of order, and a genuinely missing type just resolves to nothing instead of corrupting the rest of the address. task-6542378 Forward-Port-Of: odoo/enterprise#130662
Website building blocks now display more reliably when dark color palettes are selected. This improves readability for previews and placed content such as countdowns, event cards, social icons, and card overlays, while also making snippet previews faster to load.
Original PR description
Fix some snippets in dark palettes task-6485048 Forward-Port-Of: odoo/odoo#284455
Helpdesk users can now create tickets for teams with automatic assignment without being stopped by a Time Off access error. The system still checks who is unavailable before assigning tickets, but it performs that check with the proper internal permissions.
Original PR description
Before this commit, a helpdesk user creating a ticket on a team that assigns tickets automatically got "You are not allowed to access 'Time Off' (hr.leave) records". This happens because picking the next assignee reads hr.leave as the acting user, to skip the members who are off, and a plain helpdesk user has no access to Time Off. This commit reads the employees of the members and their leaves in sudo, as whom to assign is a system decision. https://runbot.odoo.com/odoo/error/947053 Forward-Port-Of: odoo/enterprise#130796
Belgian payroll can now calculate reimbursements for employees who combine a company car with public transport or bicycle commuting. This fixes an issue where selecting a company car erased other transport choices, leading to incomplete payslip reimbursements.
Original PR description
When configuring transport modes on an employee contract version profile, selecting a company car automatically reset public transport (bus, tram, metro) kilometers to zero and disabled the bicycle option. This prevented employees who use multiple transport modes (e.g., combining a company car with public transport or a company bicycle) from having multiple transportation reimbursements calculated on their payslip. Remove the automatic field resets for public transport and bicycle options in `_onchange_transport_mode` and remove `_onchange_has_bicycle`, allowing these transport modes to co-exist with a company car. task-6514557 Forward-Port-Of: odoo/enterprise#130487 Forward-Port-Of: odoo/enterprise#129972
Website text animations now keep headings and paragraphs in their original layout when a selected range spans multiple text blocks. This prevents centered or structured content from shifting unexpectedly when editors apply animations in the website builder.
Original PR description
Problem: Text animation used a single span around the full selection range. When a selection crossed block elements, this placed headings and paragraphs inside a span, producing invalid HTML and changing their layout. Steps to reproduce: 1. Drag & drop a snippet (like the "Cover" block) that contains page centered text 2. Select a range of text spanning multiple block elements within the added snippet 3. Add an animation to the selected text. Solution: This PR splits the selection by block and creates one inline animation wrapper per block instead. The builder applies animation options to the resulting elements as one group, without changing the document's block structure. task-5155887
This fixes an issue where choosing an Unsplash image for records such as product images could fail during saving. The update ensures Unsplash images are properly converted and attached before the save completes, reducing errors for users editing images.
Original PR description
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to…
**Description of the issue/feature this PR addresses:** When selecting Unsplash images from a relational field (which uses `CustomMediaDialog`), the images would fail to process or link correctly to the target record. In the web editor, Unsplash image selections are handled by patching the base `MediaDialog`. When a user selects an Unsplash image, that patch intercepts the save action and uses the `unsplash` service to notify the backend. The server then fetches the external Unsplash URL and converts it into a native `ir.attachment` record before the frontend completes the save. Because `CustomMediaDialog` is a distinct component used for relational fields, it bypassed the existing `MediaDialog` patch entirely and lacked this specialized fetch-and-convert logic. This commit introduces a parallel patch specifically for `CustomMediaDialog`. It intercepts `imageSave`, routes any Unsplash records through the `unsplash` service, and replaces the raw Unsplash records with the newly generated Odoo attachments before executing the underlying save. **Steps to reproduce:** - POS > Products > Products > choose any product > click the ‘edit’ button in the image > search something, e.g. ‘burger’ > add Unsplash Access Key and Application ID when prompted > select one of the resulting Unsplash images **Current behavior before PR:** - Error when saving an unsplash image when editing product images **Desired behavior after PR is merged:** - No error when saving an unsplash image when editing product images opw-6445843 Forward-Port-Of: odoo/odoo#285954 Forward-Port-Of: odoo/odoo#283694
Point of Sale preparation tickets now use the shop's configured timezone when printing pickup or preparation times. This prevents customer receipts from showing misleading UTC times when orders are handled by automated or public system users.
Original PR description
Preparation tickets are rendered server-side for self orders. format_datetime and format_time only fall back on env.user.tz, and the render runs under whichever user triggered it: the public user for an online payment confirmed on /payment/status/poll, OdooBot for the payment cron, the self ordering default user for an OBOX print. None of them is guaranteed to have a timezone, so the ticket could be printed in UTC: an 18:15 pickup showed as 16:15 Take the timezone from res.company.tz instead. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286794 Forward-Port-Of: odoo/odoo#285370
This fix prevents users from saving public holidays without the payroll information needed to calculate payslips. It avoids payroll creation failures when a holiday falls in the payslip period and keeps holiday records consistent across views.
Original PR description
**Steps to reproduce:** 1. Install Payroll and Time Off modules on v19.2. 2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type…
**Steps to reproduce:**
1. Install Payroll and Time Off modules on v19.2.
2. Create a public holiday via the form view (Time Off -> Configuration -> Public Holidays). Do not enter a work entry type and save.
3. Open Payroll and try to create a payslip for any employee in the same month as the public holiday you will face below traceback.
**Issue:**
The `work_entry_type_id` is required for payroll calculations. If it is null the `_round_days` calculation evaluates an empty recordset, producing a [ValueError](https://github.com/odoo/enterprise/blob/39095789c2b6a7e558d11f479a249871b3b800b6/hr_payroll/models/hr_payslip.py#L1021
).
While in this [PR](https://github.com/odoo/odoo/pull/254666/changes) made this field was made required in the
list view, it was missed in the form view. This allows users to save a holiday without a work entry type, crashing payslip generation later.
**Solution:**
Make the `work_entry_type_id` field required in the form view as well to prevent the creation of inconsistent public holiday records.
**Traceback:**
```.py
File "/home/odoo/src/enterprise/saas-19.2/hr_payroll/models/hr_payslip.py",
line 1021, in _round_days
day_rounded = float_round(days, precision_rounding=precision_rounding,
rounding_method=work_entry_type.round_days_type)
File "/home/odoo/src/odoo/saas-19.2/odoo/tools/float_utils.py", line 152, in
float_round
raise ValueError(msg)
ValueError: unknown rounding method: False
```
opw-6477587
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285597
Forward-Port-Of: odoo/odoo#283762Inventory users without Accounting permissions can now view and create Indian E-Waybills without access errors. This removes a permission blocker in warehouse workflows and helps shipments proceed more smoothly.
Original PR description
Before this commit Inventory users without Accounting permissions could get an Access Error when viewing or creating an E-Waybill because they could not access the required document types. After this commit Inventory users can now view and create E-Waybills without an Access Error. task-6515093 Forward-Port-Of: odoo/odoo#287121 Forward-Port-Of: odoo/odoo#285023
This fixes an issue that could block users from changing the prefix or suffix on date-based sequences. The change prevents an error during editing so sequence configuration updates can be saved normally.
Original PR description
Currently an exception is generated when the user tries to change the value of `Prefix` or `Suffix` in sequence as the following steps - Create a sequence with `Use subsequences per date_range` and…
Currently an exception is generated when the user tries to change the value of `Prefix` or `Suffix` in sequence as the following steps - Create a sequence with `Use subsequences per date_range` and add any `From` and `To` dates - Save record > Change the value of `Prefix` or `Suffix` Error: `TypeError: %d format: a real number is required, not NewId` This issue was introduced by the recently refactored changes in commit [1], which added an onchange method for the prefix and suffix fields. When the user changes either field, the onchange is triggered and recomputes all dependent fields, including `_get_number_next_actual` on `ir.sequence.date_range`. During this computation, the record contains the `NewId` record. As a result, using `%03d` to format the record ID raises the reported error, since NewId cannot be formatted as an integer. This commit fixes the issue by defaulting `number_next_actual` to `0` when `_get_number_next_actual` is invoked with a `NewId` for the related sequence while computing the value during `onchange`. [1]: https://github.com/odoo/odoo/commit/387b2289da68454599666bf96844b22c41c5cc55 Sentry-7609468821 Forward-Port-Of: odoo/odoo#277766
The live map now shows technicians one by one as their locations are found, instead of waiting for every address lookup to finish. This reduces delays when using OpenStreetMap and helps dispatchers see field activity sooner.
Original PR description
Opening the live map took a long time whenever technicians were sharing their live location and OpenStreetMap was used to find their address, since only one address lookup can be done per second, and every technician had to be fully processed before anything was shown on the map. Technicians are now displayed on the map as soon as their address is found, one by one, instead of waiting for all of them at once, similar to the way customer pins are already handled. task-6524180 Forward-Port-Of: odoo/enterprise#130648
The AI cleanup process now correctly finds content using older embedding models when those models are retired. This helps ensure stored AI data can be kept compatible with supported models instead of being missed by scheduled maintenance.
Original PR description
The embedding model deprecation cron starts by searching for chunks that are embedded using a model that hasn't been deprecated. However, due to an optimization in the ORM (optimize_type_selection),…
The embedding model deprecation cron starts by searching for chunks that are embedded using a model that hasn't been deprecated. However, due to an optimization in the ORM (optimize_type_selection), the search doesn't return any result. A domain that uses the 'not in' operator is converted by the optimization to use the 'in' operator and the values are replaced by the difference between the available selection values (self._selection) and the values in the domain. So, given that the selection values of the embedding_model field are the non deprecated embedding models and the search domain of the cron is ['embedding_model', 'not in', non deprecated embedding models] , the final domain will be ['embedding_model', 'in', non deprecated models list - non deprecated models list] = ['embedding_model', 'in', []] which will retrieve nothing from the DB because all records have an embedding_model. The main issue is that the optimization assumes that the available selection values are static. However, an old embedding model can be deprecated and a new one added which makes the optimization fail. So, the selection values are changed to be computed dynamically which will be skipped by the optimization. Forward-Port-Of: odoo/enterprise#128130
Nilvera refund documents in Turkey are now recognized as refunds even when they arrive with positive amounts. When possible, the refund is also linked 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) Forward-Port-Of: odoo/odoo#286964 Forward-Port-Of: odoo/odoo#259672
This fix changes how mail update tracking data is packaged so it stays compact even when older database activity remains open for a long time. It helps prevent memory errors and improves reliability without changing what users see or do.
Original PR description
The store version snapshot encoded in progress transactions (xip) as a bitmap spanning the whole [xmin, xmax) range, so its size grows with how far apart those bounds are rather than with how many transactions are actually in progress. A single long-lived transaction can push that range into the hundreds of thousands, leading to memory errors, most of it being zeroes. Sending it as a list of strings is much smaller (95-98% smaller tested on odoo). 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#287191
SEPA direct debit XML files now include a special scheme name field only for Nordea countries where it is required. This prevents Italian banks from rejecting payment files that previously contained an unexpected extra field.
Original PR description
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause:…
### Issue: An unexpected `<SchmeNm><Cd>CUST</Cd></SchmeNm>` node was added inside `<InitgPty><Id><OrgId><Othr>` for all countries Some Italian banks reject SDD files containing this node ### Cause: https://github.com/odoo/enterprise/commit/3c3c64b511d07fc1ba33c30363972f5b0f7283d6 added `<SchmeNm><Cd>CUST</Cd></SchmeNm>` unconditionally for all countries The original fix was intended for Nordea (Sweden) only, which requires this node explicitly The assumption that other countries would accept it was incorrect ### Steps to reproduce: - Install `account_sepa_direct_debit` and `l10n_it` - Switch to the IT company - In Settings, set SEPA Direct Debit Creditor Identifier to `BE30ZZZ300D000000042` - Create and confirm a Payment (Method: SEPA Direct Debit, any customer and amount) - Create and validate a Batch Payment with that payment - Open the generated PAIN008 XML Before the fix, `<SchmeNm><Cd>CUST</Cd></SchmeNm>` is present opw-6530996 Forward-Port-Of: odoo/enterprise#130304
Portal users can now update their preferred electronic invoicing format even when they have added a company name to their address. This prevents the field from appearing empty later and helps ensure invoice delivery settings remain accurate.
Original PR description
Steps: - Install accounting app. - Login with portal user and set `Company name` on `my/address`. - Try to edit `Electronic Format` field on my details. Issue: - `Electronic Format` field stays empty. Cause: - Since [PR](https://github.com/odoo/odoo/pull/211043) when user set `Company name` on the portal it'll create parent company and since `Electronic Format` is computed from `commercial_partner_id`, so when I update `Electronic format` field on `my/address` it'll set that value on `invoice_edi_format_store` on current address and now when I re-open `my/address` it'll compute `invoice_edi_format` from `commercial_partner_id`'s `invoice_edi_format_store` which is 'none' and it'll set `invoice_edi_format` to False and there is no way portal user can update that company's record Fix: - Update inverse of `Electronic Format` field to properly store invoice_edi_format_store value on commercial partner. Forward-Port-Of: odoo/odoo#286599 Forward-Port-Of: odoo/odoo#277527
Reconciliation now creates the needed exchange difference entry when matching foreign-currency journal items on accounts that allow reconciliation. This prevents leftover balances in company currency and keeps accounting records properly balanced while preserving existing behavior for non-reconcilable accounts.
Original PR description
Steps to reproduce: 1. Create two misc entries with the same foreign currency amount but different company currency amounts, on an account with "Allow Reconciliation" enabled (one debit, one credit). 2. Go to Journal Items, select both lines and click Reconcile. Issue: No exchange difference entry is generated, so the residual amount in company currency is left unbalanced. Fix: Only skip the exchange difference when the account is not reconcilable, which keeps the deferral behaviour untouched while restoring the exchange difference entry in the standard case. Causing PR: https://github.com/odoo/enterprise/pull/108362 task-6535655 Forward-Port-Of: odoo/enterprise#130541