Daily updates from Odoo
Thursday, July 16, 2026
273 changes
33 changes
Enhancements to existing features
We remove the "refresh" button used to refresh the customer display screen from the debug windows as it's almost never used. see odoo/enterprise#123483 task-6357970
Original PR description
We remove the "refresh" button used to refresh the customer display screen from the debug windows as it's almost never used. see odoo/enterprise#123483 task-6357970
Resolved issues and error corrections
Belgian payroll salary simulations now ignore earlier payslips from the same month when calculating results. This prevents items such as employment bonuses from being skewed by real payroll history, giving HR teams more reliable simulation figures.
Original PR description
Some computations such as the employment bonus rely on the previous payslips of the month. In case of salary simulation, we shouldn't take these into account to have an accurate result. task-6385091 Forward-Port-Of: odoo/enterprise#124081
Comments in the Social demo mode now use the correct shared demo data after an older demo contact was removed. This prevents incorrect author details or images from appearing when users try the feed comment experience in demo environments.
Original PR description
Bug === Since ce264a2 , we remove the demo partner in the social_demo module, but we didn't update the code to use the demo data in base. Task-6293738 Forward-Port-Of: odoo/enterprise#124151 Forward-Port-Of: odoo/enterprise#120821
AI tool failures caused by invalid AI-generated inputs are now logged without lengthy technical tracebacks at the standard error level. This keeps system logs cleaner while still preserving detailed diagnostic information for developers when needed.
Original PR description
Tool failures from bad LLM arguments were logged at ERROR level with a full traceback, polluting the logs. I have changed the logger to log error not exception so we get rid of the traceback error added another debug logger to show the traceback also task-6250418 Forward-Port-Of: odoo/enterprise#120281
Opening Studio from a project task list now keeps the browser address clean and accurate. Users can also go back from Studio or open the Studio URL directly without triggering errors, making customization navigation more reliable.
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
The Dutch reports module no longer displays an outdated website link in its module information. This avoids sending users 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
Belgian payroll contract versions now correctly copy three previously missing fields from contract templates. This helps keep generated contracts complete and avoids triggering Dimona-related processes when working with simulation versions.
Original PR description
Three fields were missing in the copying process from the contract template Forward-Port-Of: odoo/enterprise#124327 Forward-Port-Of: odoo/enterprise#124235
Creating an employee contract from a template now also copies the analytic distribution set on that template. This helps payroll costs remain allocated correctly without requiring users to manually re-enter the same accounting split.
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 fixes an issue where eco voucher amounts were missing from Belgian payslip reports after a recent payroll change. Payroll reports will now show the eco voucher line correctly, improving accuracy for employees and HR teams.
Original PR description
Since changing Eco vouchers to property input, the eco vouchers line on the report does not appear, this commit fixes it by calling the correct method in the template task-6370164 Forward-Port-Of: odoo/enterprise#123498
After a field service worksheet is signed, the Back to Shift button now uses a stronger primary style. This helps users more easily identify the next action and return to their shift without hesitation.
Original PR description
Apply the primary button style to the Back to Shift button after the worksheet is signed, making the next step more visible to users. Task: 6358752
Shopee order lines now show the SKU for the specific product variant purchased instead of the general product template SKU. This helps sales teams and customers identify the exact item ordered when listings include variants.
Original PR description
Currently, _prepare_order_lines_values resolves SKU as `item_sku or model_sku`. Shopee always sends item_sku (product.template SKU), so when a listing has variants the order line description shows the template SKU ([item_sku]) even though the correct variant is linked via `model_sku`. task_id: 6335110 Forward-Port-Of: odoo/enterprise#124357 Forward-Port-Of: odoo/enterprise#124031
The AI chatbox now appears above the website editor toolbar and snippet selector as expected. This prevents the assistant from being hidden while users edit pages, including setups where mass mailing is installed.
Original PR description
This PR addresses two problems relative to the AI chatbox z-index. 1. AI chatbox should appear above the toolbar, but used to appear below instead. 2. AI chatbox should appear above snippet selector dialog, but used to appear below if `mass_mailing` was installed. task-6366360 Forward-Port-Of: odoo/enterprise#123719
The French DAS2 report export now formats the fiscal year end date consistently with the required MMdd format. This prevents invalid values when the fiscal year end month has only one digit, reducing the risk of rejected or incorrect submissions.
Original PR description
Aspone force the end fiscal year in zone AD to follow the format MMdd. Before this commit, fiscalyear_last_month could be only one number and so we would end up with something like '930'. We will now add :02d to format the integer with a width of 2. task-6253745 Forward-Port-Of: odoo/enterprise#123925
This update fixes an automated test for the overtime ruleset flow in Payroll Attendance. It helps keep quality checks reliable so future payroll attendance changes can be validated with fewer false failures.
Original PR description
backport of 1f4da357e316e5663f3ca93c05f39768b279bece
This fixes an issue where companies using the Peru localization could be blocked from creating a certificate if the Chile localization was also installed. The Chile-specific serial number requirement now applies only where appropriate, reducing setup errors for other Latin American localizations.
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
The Rental Kanban view now shows and handles the New button correctly alongside the custom dashboard. This removes a usability issue that prevented users from starting new rental records from that view.
Original PR description
The code that adds the custom dashboard conflicted with the code that adds the New button. The fix was already implemented for the list view but not for the Kanban view. This commit applies the same solution used for the list view to the Kanban view. original-task-6088445
The test setup now only registers devices for internal users, matching how Odoo Cloud Notifications work in practice. This reduces misleading test coverage and helps keep notification-related checks aligned with real business behavior.
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
The Norwegian SAF-T export now uses the correct part of account numbers when assigning official grouping codes. This prevents incorrect reporting values when account numbers are extended, helping businesses produce compliant accounting exports.
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
Opening Studio from the Working Files area no longer triggers an error. This prevents an unexpected interruption for users customizing or reviewing accounting return workflows.
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
A new automated test verifies that overtime is calculated correctly when flexible employees take leave. This helps prevent payroll or attendance errors from returning in future updates.
Original PR description
For PR: https://github.com/odoo/odoo/pull/274831 This commit adds a test case to ensure that overtime is correctly calculated for the flexible employee opw-6259328,6284145 Forward-Port-Of: odoo/enterprise#124051 Forward-Port-Of: odoo/enterprise#123830
This fix makes HR payroll processes consistently ignore inactive records when looking up versioned employee data. It reduces the risk of outdated or archived information affecting payslips or paid time off allocation 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
Accepted Colombian DIAN credit notes no longer show the option to reset them to draft. This prevents users from reopening documents that have already been approved by the tax authority, helping maintain compliance and data consistency.
Original PR description
Issue: The reset button would still appear for credit notes that were already accepted by the DIAN. Steps to reproduce: Create a credit note, confirm it and send it to DIAN. You will be able to select Reset to Draft even though it shouldn't be possible to convert to draft after accepted by DIAN. Cause: The function to compute if the reset button would appear or not was only taking into account Invoices. Solution: Added credit notes, to the function that verifies if the reset button should appear. opw-6219265 Forward-Port-Of: odoo/enterprise#119696
Fixed extra blank lines appearing between invoice addenda and terms and conditions in Uruguayan electronic invoices. This helps prevent addenda content from being pushed onto a separate page unnecessarily when it still fits on the main document.
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#119283Opening a budget report record and then returning to the report list no longer triggers an error. This keeps the analytic budgets audit flow usable and avoids disruption for accounting users.
Original PR description
Problem:
The `budget.report` model had its default sorting (`_order`) set to False. When a user navigates back to the report list view via the breadcrumbs, the web client invokes `web_read_group`, which runs `self._order.split(',')`. Because `_order` is a boolean rather than a string, this raises an AttributeError and throws an RPC_ERROR.
Solution:
Set `_order = 'date desc'` on `budget.report`. Both queries within the `_table_query` UNION ALL expose a `date` column, providing a semantically correct and safe default ordering constraint.
Steps to replicate:
- Go to Accounting > Accounting > Analytic Budgets.
- Select any budget.
- Click 'Audit' on any budget line to land on the budget report view.
- Click to open any individual record.
- Navigate back using the breadcrumbs.
- -> RPC_ERROR: AttributeError: 'bool' object has no attribute 'split'
opw-6372610
Forward-Port-Of: odoo/enterprise#124191At the end of a successful populate session, run VACUUM ANALYZE so PostgreSQL statistics reflect the newly generated data and trigram indexes are updated. Usually, the autovacuum is triggered after bulk creation, but users may run their benchmarks before it has completed. Doing the maintenance explicitly makes populate absorb that waiting time instead. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
At the end of a successful populate session, run VACUUM ANALYZE so PostgreSQL statistics reflect the newly generated data and trigram indexes are updated. Usually, the autovacuum is triggered after bulk creation, but users may run their benchmarks before it has completed. Doing the maintenance explicitly makes populate absorb that waiting time instead. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Before this commit, custom Many2XAutocomplete for resource has been removed to simplify the code, the problem is the m2m avatar user component has a custom Many2XAutocomplete for res.users record and that custom behavior is not expected for resource.resource records. This commit defines the standard Many2XAutocomplete component to m2m resource widget to make sure the one from user avatar is not used in resource components. task-[6361082](https://www.odoo.com/odoo/project/4105/tasks/6361082
Original PR description
Before this commit, custom Many2XAutocomplete for resource has been removed to simplify the code, the problem is the m2m avatar user component has a custom Many2XAutocomplete for res.users record and that custom behavior is not expected for resource.resource records. This commit defines the standard Many2XAutocomplete component to m2m resource widget to make sure the one from user avatar is not used in resource components. task-[6361082](https://www.odoo.com/odoo/project/4105/tasks/6361082)
The code view for new mailings could no longer be accessed; instead, the screen would become entirely white.. This was due to a new behavior from the mailing editor that would auto-set fullscreen edition mode on new mailings by recalling that a theme was just selected, and passing on a default fullscreen state to the iframe as a prop. Upon entering the code view, the iframe component gets recreated. The "newly selected theme" state was not reset, however; as a result, the component
Original PR description
The code view for new mailings could no longer be accessed; instead, the screen would become entirely white.. This was due to a new behavior from the mailing editor that would auto-set fullscreen…
The code view for new mailings could no longer be accessed; instead, the screen would become entirely white.. This was due to a new behavior from the mailing editor that would auto-set fullscreen edition mode on new mailings by recalling that a theme was just selected, and passing on a default fullscreen state to the iframe as a prop. Upon entering the code view, the iframe component gets recreated. The "newly selected theme" state was not reset, however; as a result, the component would grow to its fullscreen size as if a theme was just selected. However, as code view mode was active, there was no builder nor content elements, resulting in a white screen. Leaving this white screen was only possible through the <esc> hotkey. Steps to reproduce: - Open the Mailing app in developper mode (?debug=assets, ...) - Create a new mailing - Select the Events theme - In the top-right corner, click on the Code View icon Fix: Upon a code view toggle, isNewlySelectedTheme is reset. task-6350867
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
Original PR description
Modified `SampleServer._mockWebReadGroup` to intercept `groupby_read_specification`. It now dynamically fetches the requested related fields using `_mockWebSearchReadUnity` and safely injects them into the `__values` payload for each mock group, perfectly mirroring the standard ORM behavior. Task: [6307582](https://www.odoo.com/odoo/project/133/tasks/6307582) Forward-Port-Of: odoo/odoo#272135
When computing taxes for the order, we group the base lines based on a grouping key. One of the components of this key is `is_refund`, and reward lines where considered as refund lines, which caused them to be grouped separately. This caused a discrepency between the tax amount computed in the frontend and the one computed in the backend. Steps to reproduce: ------------------- * Change the rounding method to globally * Create a 21% tax not included in price * Create a product with a pric
Original PR description
When computing taxes for the order, we group the base lines based on a grouping key. One of the components of this key is `is_refund`, and reward lines where considered as refund lines, which caused…
When computing taxes for the order, we group the base lines based on a grouping key. One of the components of this key is `is_refund`, and reward lines where considered as refund lines, which caused them to be grouped separately. This caused a discrepency between the tax amount computed in the frontend and the one computed in the backend. Steps to reproduce: ------------------- * Change the rounding method to globally * Create a 21% tax not included in price * Create a product with a price of 76.01 and the tax created above * Create a loyalty program with a 10% discount * Create a POS order with the product above and apply the loyalty program * Validate the order and generate the invoice > Observation: The invoice states that 0.01$ is still due Why the fix: ------------ In `round_tax_details_tax_amounts`, we try to detect any delta with the expeced tax amount. To do that we group the base lines based on a grouping key. The grouping key `is_refund` was incorrectly set to `true` for reward lines, which caused them to not be grouped. The delta was then not detected and tax amounts were not adjusted. We now make sure not to consider reward lines as refund. opw-6052112 Forward-Port-Of: odoo/odoo#275679 Forward-Port-Of: odoo/odoo#271577
### Steps to reproduce: - Download 'Sales' application - From 'Configuration' > 'Settings', enable 'Promotions, Loyalty & Gift Card' - From 'Products' > 'Gift cards & eWallet', Configure an eWallet program with a top-up product - Have a customer with an existing eWallet balance - Create a new sale order for that customer and add the eWallet top-up product - Pay the order using the customer's eWallet > The order gets discounted by the eWallet, effectively allowing the user to top up their
Original PR description
### Steps to reproduce: - Download 'Sales' application - From 'Configuration' > 'Settings', enable 'Promotions, Loyalty & Gift Card' - From 'Products' > 'Gift cards & eWallet', Configure an eWallet…
### Steps to reproduce:
- Download 'Sales' application
- From 'Configuration' > 'Settings', enable 'Promotions, Loyalty & Gift Card'
- From 'Products' > 'Gift cards & eWallet', Configure an eWallet program with a top-up product
- Have a customer with an existing eWallet balance
- Create a new sale order for that customer and add the eWallet top-up product
- Pay the order using the customer's eWallet
> The order gets discounted by the eWallet, effectively allowing the user to top up their balance
using the balance itself (infinite money glitch).
### Cause of Issue:
When computing the discountable amount for payment programs (like eWallets and gift cards), `_discountable_order` includes the total order amount. However, it did not exclude the program's own top-up products (`trigger_product_ids`) from the discountable lines.
### Fix:
If an order consists solely of top-up products, attempting to apply the eWallet now correctly raises a `UserError` ("There is nothing to discount").
opw-6341410
Forward-Port-Of: odoo/odoo#276141
Forward-Port-Of: odoo/odoo#273419**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa
Original PR description
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component…
**Steps to reproduce:** - Create a product A, tracked by lots - Create a kit product, include a component A - Change the UoM to 0.5 - Go to the PoS, order this kit product - Also order the component A, with a quantity of 2 - Pay for it, ask for an invoice - Go to the created picking - The Demand column is correctly computed and is 0.5 - The Quantity column is wrong and is 2 **Why the fix:** When getting the data from https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L283 we always get the component's line, as the move's product is the component, even if it used to be the kit product's move. This is because when exploding a kit's moves, it gets the kit's component as a product instead of keeping the kit product. This was introducing a weird behavior because we took the quantity from the component line, and not from the kit line, meaning the kit would always have the same quantity as the component. We now check if the move is actually a kit product's move, and if it is we adapt the qty to correct one by fetching the correct line's qty, and adapting it with the correct UoM. Changing the line in itself would not work, as the kit itself is not tracked by lots, so we would not enter https://github.com/odoo/odoo/blob/e0d84c7fbb270d0d1f82572daefa96c2978d3785/addons/point_of_sale/models/stock_picking.py#L284 and the move line would not be correctly created. opw-6153000 Forward-Port-Of: odoo/odoo#276098 Forward-Port-Of: odoo/odoo#262551
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be marked as paid. Steps to reproduce: - Enable cash rounding (e.g. 0.05, HALF-UP) with "Only for cash payment methods" - Create an order with a total of 15.28 - Add a cash payment of 10.00, then an online payment for the remainder The frontend requests 5.28 for the online payment, but as so
Original PR description
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be…
…nding When cash rounding is enabled with "Only for cash payment methods", an order partially paid in cash and completed with an online payment could neither request the correct online amount nor be marked as paid. Steps to reproduce: - Enable cash rounding (e.g. 0.05, HALF-UP) with "Only for cash payment methods" - Create an order with a total of 15.28 - Add a cash payment of 10.00, then an online payment for the remainder The frontend requests 5.28 for the online payment, but as soon as the order contained a cash payment the server rounded the whole order total: get_and_set_online_payments_data() returned an unpaid amount of 5.30 (15.30 - 10.00), so the validation failed with "Invalid online payments". Even once the online payment of 5.28 was processed, the order remained stuck in draft with the money captured: _is_pos_order_paid() compared the paid amount (15.28) against the rounded total (15.30). Only the part of the order actually settled in cash must be rounded: non-cash payments (card, online, ...) always pay their exact share. - get_amount_unpaid() now returns the exact residual of the order when the rounding only applies to cash payment methods. - _get_rounded_amount() now only rounds the amount not covered by non-cash payments, resolving its old TODO. Cash-only orders and orders where the cash payment settles the rounded remainder are unaffected. opw-6314690 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275472 Forward-Port-Of: odoo/odoo#275305
Features or functions removed from Odoo
The rarely used refresh button for the point-of-sale customer display has been removed from the debug window. This simplifies the debug interface and reduces clutter without affecting normal sales operations.
Original PR description
We remove the "refresh" button used to refresh the customer display screen from the debug windows as it's almost never used. see odoo/odoo#274986 task-6357970
21 changes
Enhancements to existing features
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
Resolved issues and error corrections
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 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
Timesheet 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
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
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
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
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
`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#27245314 changes
New functionality added to Odoo
This module adds extra functions on the point of sale for l10n_tw_edi_ecpay, passing values from pos order to invoice for creating Taiwan E-invoice task-5122414 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255980
Original PR description
This module adds extra functions on the point of sale for l10n_tw_edi_ecpay, passing values from pos order to invoice for creating Taiwan E-invoice task-5122414 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255980
Enhancements to existing features
Servers would return a 403 because we annoy them for downloading the WSDL/XSD at every call. opw-6237180 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#276345 Forward-Port-Of: odoo/odoo#267482
Original PR description
Servers would return a 403 because we annoy them for downloading the WSDL/XSD at every call. opw-6237180 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#276345 Forward-Port-Of: odoo/odoo#267482
Resolved issues and error corrections
This fixes a payroll report issue where eco voucher information was missing from Belgian payslips. Employees and payroll teams will now see the expected eco voucher line on the report, improving accuracy and clarity.
Original PR description
Since changing Eco vouchers to property input, the eco vouchers line on the report does not appear, this commit fixes it by calling the correct method in the template task-6370164
This fixes an issue that could interrupt the signing interface when debug-related page comments were present. The change helps ensure sign items are selected correctly so users can continue preparing or signing documents without unexpected errors.
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#122106
Fixed an issue where opening Studio from a project's task list added an incorrect extra ID to the URL. This also prevents errors when users go back in the browser or open the Studio URL directly, making Studio navigation more reliable.
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 fix ensures payroll rule parameter values stay consistent when reused by the system. It prevents one payroll calculation or process from unintentionally changing shared rule data, reducing the risk of hard-to-trace payroll issues.
Original PR description
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc. It could lead to very obscure bugs such as: ```python def…
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc.
It could lead to very obscure bugs such as:
```python
def some_innocent_code():
category_dict = self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')
incapacity_codes = category_dict['partial_incapacity']
incapacity_codes |= category_dict['total_incapacity']
# ... then use incapacity_codes
def print_rule_param():
print(self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')['partial_incapacity'])
print_rule_param() # OrderedSet(['LEAVE281'])
some_innocent_code()
print_rule_param() # OrderedSet(['LEAVE281', 'LEAVE264', 'LEAVE266', 'LEAVE217', 'LEAVE218', 'LEAVE219', 'MEDIC01'])
```
The solution was to either deepcopy the returned value each time, or to change all the rule parameters to their frozen equivalent. Since we don't have access to frozen objects in rule parameters's xml definitions, we opted for the deepcopy approach.
task-6329380
Forward-Port-Of: odoo/enterprise#124141
Forward-Port-Of: odoo/enterprise#123057Opening Studio from the Working Files view no longer triggers an error. This makes the accounting reports workflow more reliable when customizing or reviewing these records.
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 fix prevents Chile-specific certificate serial number requirements from blocking certificate creation for companies using other Latin American localizations, such as Peru. Businesses with multiple localizations installed can now configure certificates without encountering an unnecessary missing field error.
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
The Dutch reports module no longer shows an outdated website link in its module information. This avoids sending users to an unrelated external site and keeps the app 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
This fixes a configuration issue so the subtask button in project forecast task views points to the current subtask action. It helps keep task navigation reliable after related interface changes, with minimal impact for users.
Original PR description
Issue --- The inherited xpath still targets the old action-based subtask button Fix --- Update the inherited xpath to target action_open_subtasks. task-5966684 Forward-Port-Of: odoo/enterprise#123493 Forward-Port-Of: odoo/enterprise#123035
This fix ensures the field service sales task view correctly supports both older and newer versions of the subtask button. It prevents compatibility issues so users can continue accessing subtasks reliably after upgrades or forward ports.
Original PR description
Issue --- The inherited xpath only targets the new object-based subtask button, making it incompatible with versions where the action-based button is still present. Fix --- Update the inherited xpath to target both the action-based and object-based subtask buttons. Forward-Port-Of: odoo/enterprise#124249
The signing tool now uses PDF handling calls that work with the older PDF library version used on Python 3.10. This prevents automated build failures and helps keep document signing reliable across supported environments.
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
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
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#27245313 changes
Enhancements to existing features
The French balance sheet report now presents establishment costs before fixed assets and includes additional impairment accounts in the relevant tangible fixed asset lines. This improves report alignment with expected French accounting presentation and helps ensure these balances are reflected in the right sections.
Original PR description
Move establishment costs before fixed assets in the French balance sheet, and include the missing 2912, 2913, 2914, and 2915 impairment accounts in the relevant tangible fixed asset amortization/provision lines. task-6226138 Forward-Port-Of: odoo/enterprise#117689
Users can now move through the journal creation wizard using keyboard arrow keys. This makes setup faster and more accessible for people who prefer or rely on keyboard navigation.
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
Spreadsheet users can now use a new filter label function to display the readable name of a selected global filter value, such as a customer name, instead of only its internal ID. This makes spreadsheet reports easier to understand and share with business users.
Original PR description
Before this commit: If you use ODOO.FILTER.VALUE and have a customer set in the global filter, it returns the id of the customer. That can be useful in some cases but in others you might simply want the label. Task: 6167605 Forward-Port-Of: odoo/enterprise#115984
Resolved issues and error corrections
The field service sales task view now works with both older and newer versions of the subtask button. This prevents the related button customization from failing across supported versions, helping keep task workflows consistent.
Original PR description
Issue --- The inherited xpath only targets the new object-based subtask button, making it incompatible with versions where the action-based button is still present. Fix --- Update the inherited xpath to target both the action-based and object-based subtask buttons.
This fix ensures cached payroll rule parameters are safely copied before use, preventing one process from unintentionally changing values seen by another. It reduces the risk of hard-to-trace payroll calculation issues caused by shared temporary data.
Original PR description
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc. It could lead to very obscure bugs such as: ```python def…
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc.
It could lead to very obscure bugs such as:
```python
def some_innocent_code():
category_dict = self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')
incapacity_codes = category_dict['partial_incapacity']
incapacity_codes |= category_dict['total_incapacity']
# ... then use incapacity_codes
def print_rule_param():
print(self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')['partial_incapacity'])
print_rule_param() # OrderedSet(['LEAVE281'])
some_innocent_code()
print_rule_param() # OrderedSet(['LEAVE281', 'LEAVE264', 'LEAVE266', 'LEAVE217', 'LEAVE218', 'LEAVE219', 'MEDIC01'])
```
The solution was to either deepcopy the returned value each time, or to change all the rule parameters to their frozen equivalent. Since we don't have access to frozen objects in rule parameters's xml definitions, we opted for the deepcopy approach.
task-6329380
Forward-Port-Of: odoo/enterprise#124141
Forward-Port-Of: odoo/enterprise#123057This update fixes a compatibility problem that caused PDF processing in the Sign app to fail in Python 3.10 environments. It helps keep automated builds stable and ensures signed PDF handling continues to work as expected.
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
Users opening the template picker from a root audit report article will no longer see an error. If no parent article exists, the picker now shows that no template is available instead of crashing.
Original PR description
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to…
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to reproduce: 1. Install `accountant_knowledge`. 2. Create and open a new audit report. 3. Delete all content from article (the root). 4. Click the "Load a Template" button in the helper. => Crash with `AssertionError: Invalid falsy real id.` The issue occurs because the method responsible for loading the annex to display (see: `get_suggested_templates`) expects at least one record in the recordset. When the article has no parent, the recordset is empty, causing the method to fail. Before attempting to load a template, we will check whether the article has a parent article. If no parent exists, no template will be provided to the template picker. In that case, the picker will display a helper message indicating that no article template is available to load. Task [link](https://www.odoo.com/odoo/project.task/6333859) Task-6333859
New employee contracts created from a template now correctly inherit the template's analytic distribution. This helps payroll costs stay allocated to the right departments or accounts without manual re-entry.
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
The test setup now registers devices only for internal users, matching how Odoo Cloud Notifications are handled in real use. This prevents test data from including users who should not receive these notifications, improving test accuracy without changing customer-facing behavior.
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
The Norwegian SAF-T export now derives account grouping codes using the correct part of the account number. This prevents incorrect grouping values in exported general ledger reports, helping businesses produce compliant accounting files.
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
Uruguayan electronic invoices now avoid unnecessary blank lines when combining addenda text with terms and conditions. This helps keep short addenda content on the main document page instead of incorrectly pushing it onto a separate page, improving invoice presentation for customers.
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#119283Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the website logo in the editor, the size shown is always a constant number ~5.9kB, regardless of the actual size of the uploaded logo. The correct size is visible in the browser DOM. This gives users the wrong impression that their image is being heavily compressed or losing quality when it isn'
Original PR description
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the…
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the website logo in the editor, the size shown is always a constant number ~5.9kB, regardless of the actual size of the uploaded logo. The correct size is visible in the browser DOM. This gives users the wrong impression that their image is being heavily compressed or losing quality when it isn't. Reason: When clicking the logo, the editor tries to find the original, unprocessed version of the image so it can support cropping and other edits. It does this by asking the server to match the image's URL to a stored attachment. The website logo is served through a dynamic link (`/web/image/website/<id>/logo/<name>`) that isn't tied to a regular attachment record the way normal content images are, since it isn't uploaded through the usual media picker. Because of this, the server can't find a matching original, and the editor is left without a valid image source to work with. As a fallback, the editor tries to load a placeholder path instead of a real image. This request fails and silently resolves to Odoo's generic "image not found" placeholder. All further processing (and the size calculation) then happens on this small placeholder image instead of the actual logo, which is why the size shown never changes. Fix: When `get_image_info` does not return a usable `original`, `loadImageInfo` now falls back to using the image's own current src as `originalSrc`, instead of leaving it unset. This ensures `loadImage` always receives a valid, resolvable URL, so image processing (and the size shown) reflects the actual logo. opw-6260496 Forward-Port-Of: odoo/odoo#273542
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that certificate on the connection path of requests 2.31, but requests >= 2.32 changed that path (no longer calls get_connection()), so the step was skipped and the call crashed with: "TypeError: expected str, bytes or os.PathLike object, not certificate" Odoo pins requests 2.31.0 (max depending on Pyth
Original PR description
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that…
CertificateAdapter presents a client certificate stored in the database instead of on disk when opening an HTTPS connection (used by the l10n_es EDI modules verifactu, sii and tbai). It loaded that certificate on the connection path of requests 2.31, but requests >= 2.32 changed that path (no longer calls get_connection()), so the step was skipped and the call crashed with: "TypeError: expected str, bytes or os.PathLike object, not certificate" Odoo pins requests 2.31.0 (max depending on Python version), but online databases can use the version shipped by the OS (2.32.x on recent Ubuntu 26). Set the certificate up when the adapter is created instead of on that connection call. That step runs the same on every requests version, so the fix works both before and after 2.32. Steps to reproduce: - Spanish company with Veri*Factu and a certificate, on a server running requests >= 2.32 (saas-19.3 database for exemple on ubuntu 26) - Post a customer invoice and send it to Veri*Factu. => TypeError Reference: https://github.com/psf/requests/blob/f361ead047be5cb873174218582f7d8b9fcd9f49/HISTORY.md?plain=1#L146 Ticket [link](https://www.odoo.com/odoo/project.task/6366028) opw-6366028 Forward-Port-Of: odoo/odoo#275324
3 changes
Resolved issues and error corrections
This fixes an internal test issue in the Hong Kong payroll accounting area caused by an incorrect update between versions. It helps ensure payroll-related tests reflect the current date field behavior in this release, reducing the risk of unnoticed regressions.
Original PR description
Fixes a wrong forward port where the test were not updated to set the correct date field, which were changed in 18.4
This fixes how contract update templates are chosen during salary contract flows. The system now uses the current contract version when available, or falls back to the offer's contract template, helping ensure employees receive the right contract update document.
Original PR description
contract update template should come from current version if any or from the offer's contract template. Task-6094733
This fix removes unnecessary blank lines when combining invoice addenda with terms and conditions in Uruguayan electronic invoices. It helps prevent addenda text from being pushed onto a separate page when the content should fit on the same page.
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#1192832 changes
Enhancements to existing features
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#269465
Original PR description
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#269465
Resolved issues and error corrections
Fixes formatting of Uruguay electronic invoice addenda and terms so extra blank lines are not added. This helps keep short notes on the expected page and avoids unnecessary separate addenda pages 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#1192839 changes
Enhancements to existing features
The Sendcloud delivery setting previously called "Use Batch Shipping" is now named "Use Multicollo". This aligns the wording in Odoo with Sendcloud terminology, reducing customer confusion without changing the underlying functionality.
Original PR description
In order to avoid confusion for the customer, "Use Batch Shipping" was renamed to "Use Multicollo".This way it is consistent with the terminology used by Sendcloud. task-6048477 Forward-Port-Of: odoo/enterprise#123195 Forward-Port-Of: odoo/enterprise#122133
Resolved issues and error corrections
Live field service maps no longer recalculate routes when the current user's position changes, because those routes are based on worker pins instead. This reduces unnecessary processing and helps preserve routing service tokens without changing the visible route behavior.
Original PR description
The routing fetching in `updateUserPosition` of the `MapModel` should not be triggered for the live map. Right now, when changing user position, the routes are fetched again. However, for the live map, this should have no effect on routes, as they start from the user pins instead of the current user's position. This will avoid computing all over routes and preserve tokens. task-6307279 Forward-Port-Of: odoo/enterprise#123536 Forward-Port-Of: odoo/enterprise#123294
Subscription products that also allow one-time purchases now display the original price with a strikethrough next to the Buy Once price. This makes discounts or price comparisons clearer for shoppers and helps avoid confusion on product pages.
Original PR description
Version - saas-19.1 Steps to reproduce: - Enable 'Accept One Time Sale' on a subscription product - Open the product page on the website Issue: For subscription products with one time sale enabled, the original price was not shown as a strikethrough next to the Buy Once price. Fix: - Captured and exposed the original price to the template before it gets overwritten during subscription price processing - Added the missing strikethrough element to the Buy Once section of the product page Task ID - 6260207 Forward-Port-Of: odoo/enterprise#123977 Forward-Port-Of: odoo/enterprise#119488
The timesheet activity assistant no longer shows an empty Unmatched section when all items in that section are filtered out as away-from-keyboard events. This keeps the assistant cleaner and avoids confusing users with a header that has no visible content.
Original PR description
The Unmatched group's header renders even when its only entries are afk events, since those are filtered out at display time but still counted when checking if the group has content. With this PR, we first check if a group has visible content before displaying the header Task-6348666 Forward-Port-Of: odoo/enterprise#123286 Forward-Port-Of: odoo/enterprise#122858
Payroll rule parameters are now safely copied before use, preventing one process from accidentally altering cached values used by another. This reduces the risk of hard-to-trace payroll calculation issues caused by shared internal data.
Original PR description
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc. It could lead to very obscure bugs such as: ```python def…
Cached functions with `@ormcache` should not return immutable values, yet `_get_parameter_from_code()` could return dicts/sets/lists/etc.
It could lead to very obscure bugs such as:
```python
def some_innocent_code():
category_dict = self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')
incapacity_codes = category_dict['partial_incapacity']
incapacity_codes |= category_dict['total_incapacity']
# ... then use incapacity_codes
def print_rule_param():
print(self.env["hr.rule.parameter"]._get_parameter_from_code('l10n_be_work_entry_categories')['partial_incapacity'])
print_rule_param() # OrderedSet(['LEAVE281'])
some_innocent_code()
print_rule_param() # OrderedSet(['LEAVE281', 'LEAVE264', 'LEAVE266', 'LEAVE217', 'LEAVE218', 'LEAVE219', 'MEDIC01'])
```
The solution was to either deepcopy the returned value each time, or to change all the rule parameters to their frozen equivalent. Since we don't have access to frozen objects in rule parameters's xml definitions, we opted for the deepcopy approach.
task-6329380
Forward-Port-Of: odoo/enterprise#124134
Forward-Port-Of: odoo/enterprise#123058This fix prevents an error when users remove the start or end date from a Field Service planning shift. The system now checks that required date values are present before recalculating break time, making shift editing more reliable.
Original PR description
before: when removing the start and end date of a shift, a trace back happens in the `_onchange_break_time` cause: it depends on the start and end date values, so it breaks when they are falsy after: apply a guard to the `_onchange_break_time` function to check on those fields to avoid breaking it --- task-6361418 Forward-Port-Of: odoo/enterprise#123926 Forward-Port-Of: odoo/enterprise#123273
This change adjusts internal automated tests for Field Service planning with sales and timesheets so they run at the correct stage and avoid false warnings. It helps keep quality checks stable without changing day-to-day product behavior for users.
Original PR description
Before this commit, the `TestFsmFlowSaleAtInstall.test_fsm_flow` test throws a warning because of chart template in accounting, the reason is because all tests using accounting test class have to be executed in post_install to avoid having unexpected issue. This commit moves the test in post_install and skip the test is `planning_field_service_sale_stock` module is installed because the behavior tested is altered when that module is installed. runbot-error-240998 Forward-Port-Of: odoo/enterprise#123357 Forward-Port-Of: odoo/enterprise#122306
The Documents search panel was updated to stay compatible with a recent platform change. This prevents crashes when users interact with sortable items in the Documents search panel, improving reliability without changing business workflows.
Original PR description
The web `SearchPanel` root is migrated from the OWL2 compat `useRef` to an OWL3 `signal.ref()` (a function). `DocumentsSearchPanel` passes the inherited `this.root` to `useNestedSortable`, whose `draggable_hook_builder` validates `ref` as an object and reads `ref.el` — so the raw signal function crashed with:
```
Error in hook useNestedSortable: invalid type for property "ref" in parameters: expected { object } and got function.
```
Fix: pass an object ref whose `el` getter reads the signal, keeping `.el` reactive.
Companion of odoo/odoo#272029.Code cleanup and technical improvements
Several Point of Sale localization and payment screens were updated to stay compatible with Odoo's next web interface framework. This is an internal modernization that helps maintain stability during the Owl 3 migration without changing day-to-day business workflows.
Original PR description
`* = ["l10n_de_pos_cert", "l10n_mx_edi_pos", "l10n_pe_edi_pos", "pos_tyro"]` As part of the migration from `owl 2` to `owl 3`, this commit replaces uses of `t-custom-model` with `t-model` or `t-model.proxy`. Community: https://github.com/odoo/odoo/pull/271520
4 changes
Resolved issues and error corrections
This fixes a compatibility issue that caused PDF flattening in the Sign app to fail in Python 3.10 environments. It helps keep automated builds stable and ensures signed PDF handling continues to work as expected on the supported version.
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
The test setup for push notifications now only registers devices for internal users, matching how Odoo Cloud Notifications work in practice. This prevents misleading test coverage for users who should not receive these notifications and improves reliability of the mail enterprise test suite.
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
A typo in the payroll accounting code was fixed so the intended customization now matches the standard payroll method name. This helps ensure payroll-related accounting behavior runs as expected and avoids issues caused by the method not being properly recognized.
Original PR description
An incorrect underscore "_" was added when overriding the method, so we're removing it Correct name as it appears here: https://github.com/odoo/odoo/blob/1b8f6802832cfa4d146193a912af1f4445d09f0a/addons/hr/models/hr_version.py#L443
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 page breaks 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#1192831 change
Resolved issues and error corrections
Resetting certain accounting and Uruguay e-invoicing records to draft now returns a valid response when triggered remotely. This prevents an error that could interrupt automated actions or integrations using this workflow.
Original PR description
Calling the method in RPC causes an error: ``` TypeError: cannot marshal None unless allow_none is enabled ```