Friday, September 18, 2026
30 changes · master
Resolved issues and error corrections
Fixed an issue where online shop and product configurator pages could show an incorrect reference price when a product was sold in a different unit of measure than its base unit. This ensures customers see consistent price-per-unit information, reducing confusion and pricing errors.
Original PR description
Current behavior: On /shop and in the product configurator, the reference price (price per UoM, e.g. $/kg) shown next to a product's price is wrong when the website only sells that product in a uom…
Current behavior: On /shop and in the product configurator, the reference price (price per UoM, e.g. $/kg) shown next to a product's price is wrong when the website only sells that product in a uom other than its base uom. Steps to reproduce: - Enable "Units of Measure" and "Product Reference Price" - Create a product priced per g, with base_unit_count set for kg - Add kg as a packaging and restrict the base uom (g) on the website - Open /shop, or the product configurator, for that product Expected behavior: The reference price matches the "Price Per Unit" shown on the product's own form. Cause of the issue: `_get_sales_prices` and `_get_basic_product_information` divided the price directly by base_unit_count, without converting it from the uom used to compute that price (the main/pricing uom) to the product's base uom. When both differ, the result is off by their ratio. Fix: Convert the price to the base uom before dividing it by base_unit_count, in both the shop listing and the product configurator. opw-6566849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288079
Point of Sale online payments no longer include individual customer details in batched transfers. This prevents closing issues when a POS session contains payments from multiple customers, making end-of-day processing more reliable.
Original PR description
We were adding the partner of the payment in a batched transfer, which caused issue with the closing when several partner were involved in the same session. We're now avoiding adding the partner in the batched transfer. Forward-Port-Of: odoo/odoo#289156
Opening records with search panel filters linked to archived or restricted items no longer causes a crash. The system now ignores hidden filter groups that users cannot access, improving reliability in affected views such as Social Marketing posts.
Original PR description
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line…
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line 1496, in _search_panel_domain_image id_, display_name = group_id_name(group[field_name]) TypeError: cannot unpack non-iterable bool object Steps to reproduce: - On a db with Social Marketing installed, social accounts are automatically created per website - Open Social Marketing > Posts - Create a new Post with an social account - Open Settings > Social Accounts > the chosen social account - Archive it - Get back to the Post created above => crash `_search_panel_domain_image` restricts the domain with `(field_name, '!=', False)`, which is evaluated on the comodel with sudo and active_test=False, while the group by of the same query joins the comodel through `_search`, which applies record rules and active_test. A record whose values are all archived or hidden by a record rule therefore passes the condition but ends up in a group with no value, which the image loop unpacks as a tuple. This commit skips those groups: their values have no place in the range anyway. Seen on runbot, where `runbot.build.error.trigger_ids` points at `runbot.trigger` records that are archived or restricted by the project group rule. Forward-Port-Of: odoo/odoo#288731 Forward-Port-Of: odoo/odoo#288503
This fixes an issue where creating or editing a technical view could fail even when the selected model and view layout were valid. Odoo now applies the chosen model before validating the view layout, preventing false validation errors and making view setup more reliable.
Original PR description
**Problem:** Creating a view from Settings > Technical > User Interface > Views fails with a validation error, even though both the model and the architecture are valid. **Steps to reproduce:** 1. Go…
**Problem:**
Creating a view from Settings > Technical > User Interface > Views fails with a validation error, even though both the model and the architecture are valid.
**Steps to reproduce:**
1. Go to Settings > Technical > User Interface > Views
2. Click New
3. Set the View Name and pick a model in "Model of the view"
4. Set the View Architecture to `<list><field name="name"/></list>`
5. Save
**Current behavior:**
The record cannot be saved:
Error while validating view near:
<list __validate__="1"><field name="name"/></list>
Model not found: False
Duplicating an existing view and editing the copy works, so the issue only shows up on brand new records.
**Expected behavior:**
The view is saved, and validated against the model that was picked.
**Cause of the issue:**
The form edits `model_id`, a non-stored `Many2one` whose inverse `_inverse_compute_model_id` is what actually fills the stored `model` field. The architecture is edited through `arch_base`, whose inverse chain ends in `_inverse_arch` writing `arch_db`; `write` then runs `_check_xml`, which validates the architecture against `view.model`.
Both values therefore reach the record through inverse methods, so the order those run in decides whether `model` is set by the time the architecture is validated. That order became deterministic in this version: the ORM sorts inverse groups by `(field.write_sequence, field index)`. Both fields keep the default `write_sequence` of 0, which leaves the declaration order to break the tie, and `arch` is declared well before `model_id` — so the architecture is validated while `model` is still empty.
The same ordering also breaks a plain edit: changing the model and the architecture of an existing view in a single save validates the new architecture against the previous model.
**Fix:**
`write_sequence` is the ORM's hook for exactly this kind of ordering dependency, so a negative one on `model_id` states the real constraint — the model has to be known before anything is validated against it — rather than leaving it to where the fields happen to sit in the class. It also covers the write path, which a create-only workaround would miss.
opw-6467991
Forward-Port-Of: odoo/odoo#284143This fixes an issue when marking several manufacturing orders as done at the same time. The system now applies completion updates to the correct order, preventing the wrong stock moves from being marked as picked.
Original PR description
The lambda should refer to `p.qty_producing`. Since `button_mark_done` already has a `for production in self:` loop earlier, `production` is filtered, and the lambda evaluates the `qty_producing` of the last MO for all of them. When marking multiple MOs as done, it sets `picked=True` on the wrong moves. Introduced in bb56600bfc81. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288632
Fixes a Point of Sale issue where opening a company's orders could show a blank screen if one related delivery address had no name. Orders for those unnamed address contacts now display using the parent company name, keeping staff able to review customer orders without interruption.
Original PR description
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click…
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click "Orders" on the company Issue: The screen goes blank. Cause: The ticket screen is opened with the company name as search term. The server domain searches on `partner_id.complete_name`, so the orders of the address contact are returned too. They are then fuzzy matched on `getPartnerName()`, which returns `false` for a partner without a name, and `fuzzyLookup` calls a string method on it. The error is raised during rendering, so the whole app is destroyed. Fix: Make `getPartnerName()` always return a string, and fall back on the parent name, like the complete name does. This way the orders of the address contact are still listed when searching on the company name, instead of being filtered out by an empty name. opw-6578541 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288880
Installing Stock Fleet now correctly enables the required batch transfer setting so its features remain available after changing settings. This prevents the app from being accidentally marked for uninstall when users save configuration changes. Point of Sale stock permissions are also aligned so users can work with batch-related transfers consistently.
Original PR description
Steps to reproduce: - Install stock, stock_fleet - Open the settings - Enable packages (or do any other change) Previously, `stock_fleet` had `stock_picking_batch` as its dependency. However, since the merge of that module into `stock`, we still consider that this group should be enabled to be able to use `stock_fleet`'s features. However, when `stock_fleet` is installed through the apps/-i, the group won't be set, and when opening the settings, since the group isn't set the `onchange` triggers to set the `module_stock_fleet` to False, prompting its uninstall when saving the settings. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288352
Administrators can now view and create API keys for other users, making it easier to manage automated or bot accounts that rely on API keys instead of passwords. This helps teams centrally maintain script and integration access without logging in as each user.
Original PR description
This make it possible for an admin to list and also create API keys for other users. This is useful to manage "bot" users, i.e. accounts with no password that are only used with API keys in scripts.
Fixed an error that could prevent users from duplicating rental products when pricing rules were attached to a variant without variant-defining attributes. This keeps product setup workflows reliable and avoids interruptions when copying products such as rental listings.
Original PR description
Issue: Duplicating a product raises `ValueError: False is not in list` when the product has a pricelist rule set on its variant and that variant holds no attribute value. Steps to reproduce: 1.…
Issue: Duplicating a product raises `ValueError: False is not in list` when the product has a pricelist rule set on its variant and that variant holds no attribute value. Steps to reproduce: 1. Install `website_sale_renting` with the demo data. 2. Open the `Luxury Room` product, whose only attribute, `Breakfast`, doesn't create variants, and which has a pricelist rule set on its variant. 3. Duplicate it. Cause: `get_combination_key` builds the combination of a variant through `product_template_variant_value_ids.mapped(...)`. On an empty recordset, `mapped` calls the given function once with that empty recordset instead of not calling it at all, so `attribute_line_id.id` is `False` and `attribute_line_ids.index()` raises. A variant holds no attribute value when its template has no attribute line, or only lines whose attribute doesn't create variants. Fix: Iterate `product_template_variant_value_ids` instead of mapping over it, so that a variant without attribute value gets an empty combination key. Version:20 (bugfix) Forward-Port-Of: odoo/odoo#288252
Fixed an issue where manufactured lot-tracked products could be placed in the default stock location after components were unreserved and re-reserved. Putaway rules are now applied after production, helping ensure finished goods are stored in their intended warehouse locations.
Original PR description
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to…
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to Settings and enable Lots & Serial Numbers and Storage Locations. - Create a product named 'Laptop' and set it to be tracked by Lots. - Create a product named 'Graphics card' with some quantity on hand. - Create a Bill of Materials (BoM) for the Laptop with Graphics card as a component. - Create a location named 'Laptop Shelf' with 'WH/Stock' as its parent location. - Create a putaway rule: - When a product arrives in: WH/Stock - Product: Laptop - Store to: WH/Stock/Laptop Shelf - Create and confirm a Manufacturing Order (MO) for the Laptop. - Unreserve the components, open Details, and re-add the Graphics card. - Click Produce All and check the on-hand quantity list view of the Laptop. ## Issue: The Laptop is stored in WH/Stock instead of WH/Stock/Laptop Shelf. ## Root cause: When the user presses the unreserve button the finished move is passed to `_do_unreserve` https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L2407 which unlinks all the move lines on finished product moves if they are not picked: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L1043 Now when the user presses the Produce All button, `button_mark_done` is called, which calls `_post_inventory` at: https://github.com/odoo/odoo/blob/a51ca77c825d2dba326dd540e2b4c04ef6c2385d/addons/mrp/models/mrp_production.py#L2236 `_post_inventory` assigns `lot_ids` to the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1928-L1930 This calls `_set_lot_ids` which creates a move line with quantity 1 at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L679-L683 After that, `_post_inventory` sets the quantity for the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1935 Which calls `_set_quantity` and the delta quantity is now zero at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L477-L483 Because `_set_lot_ids` has already created a move line that satisfies the move quantity, `_process_increase` is not called. Since `_process_increase` is not called, `_set_quantity_done` is never called at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L463-L465 Since `_set_quantity_done` is not called, `_apply_putaway_strategy` is never called within that function at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L2565 As a result, the laptop ends up in stock instead of on the shelf. This issue did not occur in lower versions because the feature to produce multiple lots was introduced in version 19.0 by the following commit: https://github.com/odoo/odoo/commit/4bb4e08066449177f89382718ceadd840ce90d0e Before this commit, only `_set_quantity` was called. Since no move line was created because lot_ids were not set in `_post_inventory`, `_process_increase` was called, which then called `_apply_putaway_strategy`, so the putaway rules were applied correctly. ## Solution: Apply putaway rules in the post-inventory function after the user manufactures a product, ensuring the items are placed in the correct locations and preventing products from being misplaced even when putaway rules are defined. opw-6517324 Forward-Port-Of: odoo/odoo#288739 Forward-Port-Of: odoo/odoo#287390
This fixes a crash that could occur when users fetched the processing status of Romanian e-Factura documents from SPV. It ensures signature files are saved correctly, allowing businesses to continue invoice status checks without interruption.
Original PR description
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error:…
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error: Incorrect padding Cause of the issue: _request_ciusro_download_answer extracts the signature file from the SPV zip answer and re-serializes it with etree.tostring(...), which gives plain XML bytes, not base64 But _l10n_ro_edi_fetch_invoice_sent_documents and the 3 other callers that persist that signature still ran it through base64.b64decode() before wrapping it in BinaryBytes (which already expects raw bytes, not base64). Decoding real XML as base64 only "works" by accident when the XML happens to contain a number of base64-alphabet characters that's a multiple of 4 after the invalid ones get silently stripped - otherwise it blows up with "Incorrect padding". This got introduced by 41fe2ebdb9cc (fields.Binary return BinaryValue), which flipped a base64.b64encode() call to base64.b64decode() at these 4 spots instead of just dropping the base64 call entirely. A previous fix (0750ee145ae2) already fixed the sibling issue on the invoice's own attachment_raw but missed these. Solution: Drop the erroneous base64.b64decode around the signature's attachment_raw at the 4 call sites, and fix the tests that were mocking attachment_raw as base64-encoded instead of raw bytes opw-6562123 Forward-Port-Of: odoo/odoo#288140
Sales order previews and reports now show the correct section quantity, unit of measure, and unit price. This prevents customers and sales teams from seeing misleading quantities or pricing, especially on mobile previews and sections with hidden composition.
Original PR description
The section quantity and pricing displayed in the sale order preview and report are inconsistent in some cases. After https://github.com/odoo/odoo/commit/34c47e5493bc36af338efbb54050e060b04f8ea3, the section quantity and UoM are dynamically displayed in the preview, but the mobile preview still shows the static `1 Units`. Additionally, when a section has `hide_composition` enabled, the unit price is incorrectly displayed as the section's total price instead of being based on the section quantity. This PR fixes both issues by: - Displaying the dynamic section quantity and UoM in the mobile preview. - Computing the section unit price from its total price and quantity when the composition is hidden. Forward-Port-Of: odoo/odoo#288743
Changing the pricelist on a saved quotation now correctly updates prices for combo product items. This prevents quotes from showing outdated combo item prices and helps ensure customers receive pricing that matches the selected pricelist.
Original PR description
Versions -------- - 20 Steps ----- 1. Create and save a quotation with a combo product; 2. change the pricelist for one giving a different price on that combo product. Issue ----- The combo item…
Versions -------- - 20 Steps ----- 1. Create and save a quotation with a combo product; 2. change the pricelist for one giving a different price on that combo product. Issue ----- The combo item lines keep the prices given by the previous pricelist. Cause ----- Commit e78f729d removed the `Update Prices` button and moved `_recompute_prices` to the `pricelist_id` onchange. The lines are therefore in memory records now, whereas they were stored ones when the recompute was triggered by the button. `_get_combo_item_display_price` prorates the price of the combo product, computed on the line returned by `_get_linked_line`. As of commit f1e68cc, that method returns `linked_line_id` whenever it is set, i.e. the stored line, whose `order_id` is the stored order, hence still the previous pricelist. Solution -------- When the line is an in memory record, look its linked line up among the order lines, by origin, so that the in memory version is returned. opw-testing-day-v20 Forward-Port-Of: odoo/odoo#288543
Changing an employee's working schedule no longer shifts the dates on existing time off requests for employees in certain time zones. The request dates remain unchanged while the system still recalculates the time off duration based on the new schedule.
Original PR description
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values…
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values back to the request dates. For timezones ahead of UTC, midnight on a non-working day can therefore shift the requested date to the previous day. This fix marks the schedule-triggered re-computation as an internal fast update, ensuring that the dates selected on the time off request remain the source of truth while still allowing the leave duration to be recomputed according to the new working schedule. Regression coverage is added to ensure that changing an employee's working schedule: * preserves the dates selected on existing time off requests; * correctly recomputes the leave duration according to the new schedule. ## Steps to reproduce 1. Create an employee with timezone Europe/Brussels and a Monday–Friday working schedule. Before creating any leave, set the employment record’s effective date and contract start date to 3 March 2025. 2. Create a full-day time off request from 23 to 26 February 2026. Its duration is initially four working days. 3. Change only the employee’s working schedule to a duration-based Tuesday–Friday schedule, with 7.6 hours per working day and Monday off. Leave the effective date and contract dates unchanged, then save. **Before the fix:** the request’s start date incorrectly shifts to 22 February. **After the fix:** the dates remain 23–26 February, while the duration correctly decreases to three working days. opw-6456107 Forward-Port-Of: odoo/odoo#281385
The email plugin can now keep users signed in for longer, with a default token duration of seven days. Administrators can adjust this duration through a system setting, reducing the need for daily logins while keeping access limited to the Outlook plugin endpoints.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#288572 Forward-Port-Of: odoo/odoo#287767
Connecting bank synchronization no longer removes payment options that users previously configured on bank journals. This preserves existing incoming and outgoing payment settings while still adding any missing default options, reducing reconfiguration work after syncing a bank account.
Original PR description
**Steps to reproduce:** - Install Accounting - Configure "Bank" journal: * Add several incoming payments * Add several outgoing payments - From Accounting dashboard, connect Bank (e.g. Odoo Bank Sync Demo) **Issue:** After bank sync, all the incoming/outgoing payments added on the Bank journal are removed. Only the default ones are re-created. **Solution:** Keep all the existing incoming/outgoing payments (i.e. payment method lines) and only create the default ones for the payment method types that don't exist. opw-6424407 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281658
The Attendance app calendar now greys out holidays and other unavailable days, making it easier for users to understand attendance context at a glance. This aligns the calendar experience with the Time Off app and reduces confusion when reviewing schedules.
Original PR description
The calendar view on Attendance app should look similar to the one on Time Off app, with holidays greyed out. task-6576916 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update prevents users from being sent back to the login screen after signing in while a websocket-based service such as live chat is active. It ensures the existing session is saved without overwriting the newly logged-in session in the browser, improving login reliability.
Original PR description
Before this commit, signing in from a page holding a websocket connection could land the user back on the login form. This happens because the websocket handshake marks the session dirty to get it written on disk, and _save_session sends a "session_id" cookie back for every dirty session. A handshake answered between the login response and the request that follows it therefore puts the pre-login session back in the browser. Note that the failure was seen on master, where a livechat tour signs in with the bus connected, but every version since 17.0 answers the handshake the same way. This commit saves the session from the handshake itself, so that the response carries no session cookie. https://runbot.odoo.com/odoo/error/947173 Forward-Port-Of: odoo/odoo#288667 Forward-Port-Of: odoo/odoo#287960
This update fixes cases where Odoo could silently use desktop-style behavior on small screens or hide debug-only interface elements because outdated internal flags were still being read. It also adds stronger checks so similar mistakes are caught during testing instead of reaching users.
Original PR description
Removes depracated `env.isSmall` and `env.debug` and make it throw if used (to prevent forward port mistakes) Enterprise PR: https://github.com/odoo/enterprise/pull/130698 Forward-Port-Of: odoo/odoo#288843
Payslip corrections now use the appropriate accounting dates for both the refund and the corrected payslip. This helps payroll and accounting records reflect the correct timing, reducing confusion during financial reconciliation.
Original PR description
-The refund payslip's accounting date is set to the day of the correction (since it's put as paid directly). -The accounting date of the correction payslip us set to the day of its validation Forward-Port-Of: odoo/enterprise#131902
Payslips now calculate attendance-based work entries using paid working time only, excluding any recorded unpaid break duration. This prevents employees from being paid for break time when attendance records include unpaid breaks.
Original PR description
Steps to Reproduce: 1. Create an attendance from 8AM to 4PM (8 hours) 2. Add 2h of Break Duration -> This reduces the worked time from 8h down to 6h. 3. Generate a payslip for this employee -> 8H of attendance is included, but it should be 6H instead Fix: Exclude `attendance.break_duration` -if there is one set- from attendance work entries' total value. task-6573873
Blog imports now correctly handle multiple blogs with the same name by tracking them with their original external identifiers. This prevents posts from being linked to the wrong blog or failing to match during website generation.
Original PR description
Blogs with the same name was creating a conflict in the mapping of id to created odoo blog, which meant that the blog post matching with the blog itself was failing. This fix introduces external ids for the blogs which is the id of the foreign website, making it much easier to keep track of the mapping between the external records and the created odoo records.
This fixes several AI chat issues so users can again show agent steps, see accurate tool activity status, and get clearer chat names after the first message. It also aligns AI agent name styling with other chat participants for a more consistent experience.
Original PR description
Purpose: --------- #### Show agents steps toggler in composer actions The option to toggle the agents steps is missing in the composer actions. There were 2 ways of adding composer actions for AI…
Purpose: --------- #### Show agents steps toggler in composer actions The option to toggle the agents steps is missing in the composer actions. There were 2 ways of adding composer actions for AI chats (probably after resolving conflicts). #### Update tool status when tools are called The tool status in ai chats is not updated anymore when tools are called. This happens because `_store_session_fields` is called when the tool results are sent to the LLM since commit [1] Therefore, we are sending the tool status which is immediately overridden by this call to `_store_session_fields`. This commit fixes it by removing the direct updates, and let `_store_session_fields` handle this case as well. This PR also changes the font-weight of the agent header in ai chats to match the one of the other users #### Always rename ai chats on first message Since commit 1, AI chats created with a channel title are not renamed anymore. This commit restores the previous logic, which is to always rename AI chats on the first message (to make it easier to find them in the chat history). [1] a7523d2e84edb258f42141b3154493a8a8e12e58 Forward-Port-Of: odoo/enterprise#131681
Asset depreciation is now calculated using exact monthly portions when periods cover full months or years. This avoids small day-count differences, helping accounting results align more consistently with expected depreciation schedules.
Original PR description
Old behavior: Linear depreciation for constant periods used a day-based ratio (days_passed / total_days). New behavior: When evaluating full periods (exact months or years), the engine now bypasses day-based logic and uses strict monthly fractions: Depreciable_Value * (Period_Months / Total_Months).
Payroll warnings for split sick leave are now shown even when a leave request has not yet been saved. Employees and payroll teams can also see the warning directly on the employee form, helping avoid missed payroll issues.
Original PR description
Before this commit, the split warning was searched in the database, so a request not saved yet never showed it. The employee form showed no payroll warning either. After this commit, the warning filters the given records, and the banner is also on the employee form. taskid-6578279
Submitting a draft Denmark VAT report no longer triggers an error caused by missing report context. This ensures Danish VAT filings can proceed reliably without users being blocked at submission time.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202 Forward-Port-Of: odoo/enterprise#130320
This fix prevents the Bulgarian SAF-T setup from recreating deleted standard accounts or taxes with incomplete information. It only updates existing standard records, reducing the risk of setup errors or failed upgrades when customers have customized their accounting data.
Original PR description
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:…
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:
```
('l10n_bg_691001', {'l10n_bg_saft_account_code': '624'})
```
This can lead to incorrect records.
Only update records whose XMLIDs still exist, avoiding the creation of new records when the corresponding standard record has been deleted.
```
File "/home/odoo/src/enterprise/19.0/l10n_bg_saft/__init__.py", line 10, in _add_account_saft_code
Template._load_data({'account.account': Template._get_bg_saft_account_code()})
File "/tmp/tmpbu1ntr1j/migrations/account/0.0.0/pre-ensure-deferred-accounts.py", line 37, in _load_data
return super()._load_data(data, *args, **kwargs)
File "/home/odoo/src/odoo/19.0/addons/account/models/chart_template.py", line 697, in _load_data
created_records[model] = self.with_context(lang='en_US').env[model]._load_records(all_records_vals)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5196, in _load_records
records = self._load_records_create([data['values'] for data in to_create])
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5103, in _load_records_create
records = self.create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/account/models/account_account.py", line 1055, in create
)).create(vals_list_for_company)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/mail/models/mail_thread.py", line 329, in create
threads = super(MailThread, self).create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/tmp/tmpbu1ntr1j/migrations/util/orm.py", line 267, in wrapper
return f(*args, **kwargs)
File "/tmp/tmpbu1ntr1j/migrations/base/0.0.0/pre-models-match_uniq.py", line 25, in create
return super().create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4711, in create
records = self._create(data_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4887, in _create
cr.execute(SQL(
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.NotNullViolation: null value in column "account_type" of relation "account_account" violates not-null constraint
DETAIL: Failing row contains (734, null, 1, 1, null, null, null, t, f, f, 2026-08-25 08:18:15.751937, 2026-08-25 08:18:15.751937, no, f, null, null, null, null, 411).
```
upg-4611390
tbg-2902
Forward-Port-Of: odoo/enterprise#129145This fix ensures Belgian 13th month payslips only apply the Special Social Contribution when there is a regular monthly payslip for the same period. It prevents incorrect deductions when the 13th month payment is generated on its own, improving payroll accuracy for Belgian employees.
Original PR description
When we generate a 13th month payslip, we need to check if there is a monthly pay payslip in the same period. If not, the Special Social Contribution will be 0. task-6512347 Forward-Port-Of: odoo/enterprise#131844
Belgian payroll no longer applies private car reimbursement when an employee has no joint committee set. The update also standardizes how compensation thresholds are interpreted, reducing incorrect payroll calculations in edge cases.
Original PR description
Issue: - When no JC (joint committee) was specified for given employee, JC302 rule was applied to compute private car compensation. - Unconsistent format for representing intervals by single key in param rules. With key1 < key2 < key3, associated key values to key2 should be read as belonging to [key2, key3[ in one case, and ]key1, key2] in another case. Solution: - No JC, means no private car compensation. - Associated key value to key2 belongs to interval [key2, key3[ - Fix edge cases (value below min requirement for compensation) task: 6531956
Several Odoo Enterprise screens now use the correct mobile and debug-mode settings after an underlying framework change. This prevents menus and layouts from silently choosing the wrong desktop view on phones and restores debug-only options where relevant.
Original PR description
Removes depracated `env.isSmall` and `env.debug` and make it throw if used (to prevent forward port mistakes) Community PR: https://github.com/odoo/odoo/pull/287002 Forward-Port-Of: odoo/enterprise#131886