Thursday, August 27, 2026
122 changes · master
Resolved issues and error corrections
Pending invitation buttons in General Settings now display with clear spacing instead of appearing stuck together. This makes the settings page easier to scan and interact with, especially when several invitations are pending.
Original PR description
Before this commit: the pending invitations buttons in General Settings render as one continuous run of pills touching edge to edge. This commits adds a flex-wrap container with a gap. task-6511840 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Email notifications for tracked changes now display the expected arrow and parentheses in the message body. This makes change summaries easier for recipients to read and understand without affecting the underlying notification behavior.
Original PR description
Bug === When notifying by email a tracking change, the arrow and parenthesis are not rendered in the email body. This commit cleans the fix we did in stable, and move the arrow from the python Markup to the template. Task-6424104
This fixes a duplicated manufacturing label-printing action that accidentally limited access to only one screen. Users can now print labels again from list, kanban, and form views, making the workflow consistent and easier to access.
Original PR description
Before adding the `action_print_labels` server action from [PR](https://github.com/odoo/odoo/pull/163289), the server action with same id and purpose had already been merged in [PR](https://github.com/odoo/odoo/pull/166495). As a result, the action was duplicated. The duplicate definition with `binding_view_types=form` was overriding the original `list,kanban,form` binding and restricting the action to the `form` view only. This commit removes the duplicate `action_print_labels` server action so the label printing flow is available from `list,kanban,form`, consistent with the `stock.picking` server action.
Website snippet links that define their own color style now keep that style instead of being overridden by the surrounding section. This improves visual contrast for elements like the Splash Intro scroll button, making them easier for visitors to see.
Original PR description
Steps to reproduce: - Drag and drop a "Splash Intro" snippet onto the page. - Inspect the scroll button. => The icon uses the `o_cc5` link color from the section. => There is not enough contrast between the arrow and the button background, making the arrow hard to see. Before this commit, color combination link rules still targeted links that were color combination roots themselves. Since [1] added `o_cc5` on the `s_splash_intro` section, its `a:not(.btn)` rule overrode the `o_cc1` scroll button color. After this commit, link color rules skip elements with `o_cc`, so a link using its own color combination keeps its own colors. [1]: https://github.com/odoo/odoo/commit/b7a4edb9fa3d task-6303725 Forward-Port-Of: odoo/odoo#273120
The recruitment job offer page now displays the quick assign button at the same size as the nearby avatar image. This small visual fix improves alignment and gives the page a cleaner, more consistent appearance when a company is linked to the job offer.
Original PR description
Before this PR, the o_quick_assign button was not the same size as the o_avatar img which makes it look like it's misaligned when there is a company associated with the job offer. task-6092395 | Before | After | |--------|--------| | <img width="1058" height="705" alt="Screenshot 2026-04-20 at 15 19 03" src="https://github.com/user-attachments/assets/31b38b78-591d-4c55-b3e8-b3888484f9a1" /> | <img width="1058" height="705" alt="Screenshot 2026-04-20 at 15 29 40" src="https://github.com/user-attachments/assets/6f78190a-be92-4eeb-9a9f-55e8f247bf1c" /> | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283038 Forward-Port-Of: odoo/odoo#260133
The dashboard list now uses each user's existing access rights to decide whether they can create new dashboards. This prevents the create option from being shown to users who should not have it, keeping permissions consistent and clearer.
Original PR description
The attribute to create a new dashboard was set to `true` for every user instead of defaulting to their access rights. Task-6345154
Belgian payroll will no longer show a minimum wage warning when an employee's wage already matches the required threshold. This prevents unnecessary alerts and avoids confusion after using the wage adjustment action.
Original PR description
The minimum wage warning for Belgian payroll was incorrectly triggering when an employee's wage was equal to the required minimum scale threshold. This occurred because floating-point precision issues in `_get_l10n_be_min_wage()` resulted in minute floating-point discrepancies (e.g., `4095.1200000000004`), causing the strict inequality check (`current_wage < min_wage`) to evaluate to `True`. As a consequence, clicking "Adjust Wage" applied the exact minimum wage but left the warning displayed. Fix this by rounding both `current_wage` and `min_wage` according to the wage type precision (2 decimals for monthly wages, 4 for hourly wages) before performing the inequality check. Task: 6488645
This update prevents a point-of-sale appointment page from failing to load due to a widget that relied on a removed component. By excluding the incompatible widget from the appointment assets, the module should load more reliably for users.
Original PR description
Purpose --- The new widget "Many2ManyAttendeeMail" depends on a removed asset in this module, messing the load order of the asset bundle. This commit fixes this by also removing the new widget from the assets. Task-6209447
A subscription pricing helper was moved into the core subscription module so it is always available when product configuration needs it. This prevents potential errors in subscription sales flows, including website-related setups, without changing the user experience.
Original PR description
Moved the function `_get_recurring_pricings` from website_sale_subscription to sale_subscription as it is called in _get_additional_configurator_data but may not be necessarily available. This is safe not only because it is in master but because website_sale_subscription depends on sale_subscription, and the function is kept on `product.template` so it should always be available still.
French VAT reports now only include a direct payment instruction when VAT is actually owed. This prevents refund requests from being rejected by the French tax authority due to an invalid payment block, while leaving normal VAT payment submissions unchanged.
Original PR description
`_prepare_edi_vals` always called `_get_formatted_payment_values()`, adding an EDI-Paiement (telereglement) block to the T-IDENTIF of the 3310CA3, regardless of whether the company owes VAT or is in…
`_prepare_edi_vals` always called `_get_formatted_payment_values()`, adding an EDI-Paiement (telereglement) block to the T-IDENTIF of the 3310CA3, regardless of whether the company owes VAT or is in a credit position. Steps to reproduce: - French company in a VAT credit position, requesting a refund. - Fill a bank account line, the account to receive the refund and send the VAT report to the DGFiP. Current behaviour: The DGFiP returns a negative acknowledgement on the CA3 interchange: "Telereglement 1 rejete: Montant telereglement absent ou invalide. Code erreur : 018", even though the declaration itself is accepted. The wizard's bank account lines are reused for two opposite purposes: the account to debit when VAT is due, and the account to credit when a refund is asked. `_get_formatted_payment_values()` builds a payment order from them unconditionally, so a telereglement for the credit amount is emitted in the refund case. A telereglement is invalid when no VAT is due, hence error 018. A return nets to either a payment or a credit, never both, so the two cases are mutually exclusive. This commit guards the call with `self.is_vat_due`, so the telereglement is only generated when the company actually owes VAT. The VAT-due flow is unchanged. opw-6275695 Forward-Port-Of: odoo/enterprise#124694 Forward-Port-Of: odoo/enterprise#120840
Kenyan POS refunds sent to eTIMS now reference the original sale's KRA invoice number instead of the refund's own order number. This prevents eTIMS from rejecting valid refunds because it was checking items and amounts against the wrong invoice.
Original PR description
Steps to reproduce: - Set up a company in Kenya with eTIMS. - Sell an order from the POS and send it to eTIMS. - Refund that order and send the refund to eTIMS. Cause of the issue: When we build the…
Steps to reproduce: - Set up a company in Kenya with eTIMS. - Sell an order from the POS and send it to eTIMS. - Refund that order and send the refund to eTIMS. Cause of the issue: When we build the JSON for eTIMS, the "orgInvcNo" field (the KRA invoice number of the order we are refunding) was always set to `self.sequence_number`, which is just the order's own number in its session. This is wrong for a refund: eTIMS then can't find the item on "the original invoice" (since it's looking at the wrong invoice), and also can't check the amounts, since it's comparing them to the wrong order. eTIMS then rejects the refund with a 910 error, like "item sequence ... does not exist on the original invoice" or "amount is incorrect for item ...". The invoice-based flow (account_move.py) already does this the right way, using `reversed_entry_id.l10n_ke_oscu_invoice_number`, but the POS order flow was not doing the same thing. Solution: For a refund order, use the KRA invoice number of the refunded order (`refunded_order_id.l10n_ke_oscu_order_number`) instead of the refund's own sequence number. Normal sales keep working like before. opw-6445174 Forward-Port-Of: odoo/enterprise#128387
Pricing rules added from a product variant now remain linked to that exact variant instead of being applied to the broader product template. This helps businesses avoid unintended pricing across multiple variants and ensures product-specific discounts or prices behave as expected.
Original PR description
Issue: --- When you apply pricing on product variant form, pricing is instead applied on product template. Steps to reproduce: 1- Open a product variant. 2- From prices tab, add a pricelist rule. Save the variant. 3- Re-open pricelist rule. As you see, the variant is not set. Cause & Fix: --- This is because `applied_on` is changed to `1_product` when `display_applied_on` is set to `1_product`. However, `display_applied_on` is also set to `1_product` when item is created from variant. We can check that case using `default_product_id`. opw-6421193 Forward-Port-Of: odoo/odoo#284167 Forward-Port-Of: odoo/odoo#280303
This fixes an issue where customers adding a new payment method through the portal with Authorize.Net would not have it saved. The payment method is now securely stored before the temporary authorization is cancelled, restoring the expected checkout and account-management experience.
Original PR description
Issue: --- Authorize payment tokenization doesn't work. Steps: 1- Setup authorize payment provider. 2- Using portal page, add a new payment method for the user. The created payment method is not…
Issue: --- Authorize payment tokenization doesn't work. Steps: 1- Setup authorize payment provider. 2- Using portal page, add a new payment method for the user. The created payment method is not saved. Cause: --- The issue was introduced in efc2788dfccd13ee6feb309430ff57e49664ff97. Before that, we were calling `_tokenize` before voiding the tx. In that PR, the `_tokenize` call was moved to `_process()`, after `_apply_updates()`. So now what happens is that we void the tx, then call `_tokenize()`. Inside tokenize we try to create a customer profile, which fails because the tx is already voided. Fix: --- We can fix it by calling `_tokenize()` once before voiding the tx. The redundant tokenize call inside the general payment tx `_process` is rendered ineffective by two safeguards: 1- There is a check for `tx.tokenize`, which neutralizes double tokenization: https://github.com/odoo/odoo/blob/fffd987cc98d1ea0cd04e24dda2ed8b64a219cdc/addons/payment/models/payment_transaction.py#L754-L755 https://github.com/odoo/odoo/blob/fffd987cc98d1ea0cd04e24dda2ed8b64a219cdc/addons/payment/models/payment_transaction.py#L893-L896 2- If `token_id` is already set, no token value is returned: https://github.com/odoo/odoo/blob/fffd987cc98d1ea0cd04e24dda2ed8b64a219cdc/addons/payment_authorize/models/payment_transaction.py#L237-L243 opw-6426847 Forward-Port-Of: odoo/odoo#283652 Forward-Port-Of: odoo/odoo#281014
Point of Sale receipts now include general customer notes that are added to an order but not tied to a specific product line. This restores expected receipt behavior and helps staff and customers see important order instructions on printed tickets.
Original PR description
## Steps to reproduce: - Go to the pos, click a product - Click the product again to unselect the line - Go to the 3 dots -> customer note - Enter a customer note, pay for the order - Try and print the receipt -> The customer note is not displayed ## Why the fix: Since the receipt REF, the general customer note was not displayed on the ticket anymore, but in was in earlier versions. When making a customer note without a selected line, we make a general one, which is not attached to a product line, so it was never displayed. We now display the general customer note if it exists, after having displayed all lines, as we did before 19.2. opw-6483621 Forward-Port-Of: odoo/odoo#284456
Installing eCommerce no longer fails if a user previously deleted default product attributes such as Brand. This prevents an avoidable setup error and makes the installation process more reliable for businesses that customized their product attributes.
Original PR description
Steps to produce: --- - Install sales module. - From settings, enable variants. - Go to Sales > Products > Attributes. - Delete one of the attributes created by default (such as the "Brand"…
Steps to produce: --- - Install sales module. - From settings, enable variants. - Go to Sales > Products > Attributes. - Delete one of the attributes created by default (such as the "Brand" attribute). - Try to install the eCommerce module. Traceback: --- - `Exception: Cannot update missing record 'product.pa_brand'` Root cause: --- - The `website_sale` module attempts to append the `external_identifier` field to the default demo attributes originally created by the `product` module. If a user deletes these attributes prior to installing `website_sale`, Odoo's XML parser encounters a missing foreign record. Solution: --- - Wrapped the records in `<odoo noupdate="1">` and added `forcecreate="0"` to each `<record>`. Combining `forcecreate="0"` and `noupdate="1"` safely instructs the XML parser to gracefully skip these specific records during installation or upgrades if they are missing, preventing the traceback while still applying the external identifiers if the attributes exist. opw-6480506 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283833
The salary offer page now avoids showing a large empty placeholder when no benefits are available. It also removes duplicate action buttons, making the review, feedback, and signing experience clearer for candidates or employees.
Original PR description
This commit fixes the issue of showing a big placeholder in the signing offer page when there was no benefits available, and also fix the duplicated buttons of "Review & Sign" and "Feedback" on the same page. taskid-6486370 Forward-Port-Of: odoo/enterprise#129008
This fixes an error that could block converting website contact form leads into opportunities when the visitor entered a new company name. Sales teams can now complete the lead conversion flow without the system applying an invalid customer type behind the scenes.
Original PR description
Steps to reproduce: 1. Have `website_crm` installed and CRM Leads enabled. 2. As a public visitor, go to the website's /contactus page. 3. Fill out the form, ensuring you type a new company in the…
Steps to reproduce:
1. Have `website_crm` installed and CRM Leads enabled.
2. As a public visitor, go to the website's /contactus page.
3. Fill out the form, ensuring you type a new company in the "Your Company" field, and submit.
4. As an internal user, go to CRM > Leads and open the newly created lead.
5. Click "Convert to Opportunity".
A ValueError is raised:
Wrong value for res.partner.type: 'lead'
The leads view (and lead actions) sets `default_type' as 'lead'` in the context. When converting a lead that has a `partner_name` (like those generated from the website contact form), `_create_customer` calls create method of partner model which triggers `_create_parent_from_name` to auto-create the parent company.
Since the parent company creation values don't include an explicit `type`, it falls back to `default_type` from the context, receiving 'lead', which is not a valid `res.partner.type` selection value.
Pop `default_type` from the context before creating the partner in `_create_customer`. The partner type is already explicitly set in `_prepare_customer_values` ('contact'), making context propagation unnecessary. If a specific type is needed for the parent company, `parent_additional_values` is the proper mechanism to use.
Task-6428783
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-prThe HTML editor now prevents table merge or unmerge actions from affecting cells in a different table. This avoids accidental changes when users work with multiple tables in the same document.
Original PR description
Steps to reproduce: - Insert two tables in the editor. - Merge cells in the first table. - Select the merged cell in the first table. - Open the table menu for the second table. - Observe that the…
Steps to reproduce: - Insert two tables in the editor. - Merge cells in the first table. - Select the merged cell in the first table. - Open the table menu for the second table. - Observe that the "Unmerge Cells" option is available even though the second table has no merged cells. - Click "Unmerge Cells". - The merged cell in the first table is unexpectedly unmerged. Description of the issue: - The "Unmerge Cells" option is shown for the second table when a merged cell from the first table is selected. - Clicking the option unmerges the selected cell from the first table. Cause: - In `getSelectedCellsMergeInfo`, `canUnmerge` was determined using `td.rowSpan > 1 || td.colSpan > 1` without checking whether the cell belonged to the target table. Solution: - Verify that the selected cells (`td`, `firstCell`, and `lastCell`) belong to the `targetTable` before allowing merge or unmerge operations. - Prevent merge and unmerge operations from being applied to cells in a different table. task-6475293 Forward-Port-Of: odoo/odoo#283222
The pickup location search no longer pre-fills an imprecise ZIP code based on GeoIP, helping customers avoid seeing irrelevant nearby pickup points. The search prompt is clearer and the country selector is simplified when there is only one country option.
Original PR description
GeoIP guesses a visitor's location is not precise resulting in showing pickup points that are not close to the customer. Drop the GeoIP zip prefill.
Also clarify the search placeholder ("Zip or City") and hide the country dropdown's caret when there's only one option to pick. Safely fallback on the first country in the selector.
Forward-Port-Of: odoo/odoo#284465
Forward-Port-Of: odoo/odoo#284392UAE companies can now create and save salary bank accounts directly from Payroll Settings. This helps payroll teams complete required UAE WPS configuration without needing a workaround.
Original PR description
Issue: UAE companies cannot create their salaries bank account directly from Payroll Settings. The bank account cannot be saved, leaving payroll configuration incomplete and preventing the UAE WPS…
Issue: UAE companies cannot create their salaries bank account directly from Payroll Settings. The bank account cannot be saved, leaving payroll configuration incomplete and preventing the UAE WPS process from being completed. Steps to reproduce: * Configure an Emirati company with the UAE Payroll localization. * Open Payroll > Configuration > Settings. * Create a new Salaries Bank Account from the settings field. * Fill in the bank details and try to save the account. Cause: Since saas-19.2, the bank account form hides the required account holder and expects the opening field to provide it through `default_partner_id`. The UAE salaries bank account setting only restricts selectable accounts through its domain and does not provide that creation default. Newly created accounts therefore have no owner and cannot be saved. Domains only filter selectable records and do not initialize fields on new records. Since the shared bank account form hides the required partner, accounts created from Payroll Settings have no owner and cannot be saved. Solution: We need to provide the current company partner as the account creation default while retaining the existing selection domain. This preserves the company and country restrictions and guarantees that newly created salaries accounts satisfy the required ownership invariant. opw-6441848 Forward-Port-Of: odoo/enterprise#127777
Invoice document recognition now compares bank account numbers in the same cleaned format used by OCR. This helps the system correctly match supplier IBANs even when saved bank details include spaces, dots, or dashes, reducing manual corrections.
Original PR description
When looking for a matching IBAN, we were searching on the `acc_number` field, which can contain spaces or special characters (dots, dashes, etc). But the OCR always returns the IBAN in a sanitized format, without any space or special characters, so it should be compared against the sanitized IBAN of the partners. task-none (issue found by chance) Forward-Port-Of: odoo/enterprise#128264 Forward-Port-Of: odoo/enterprise#127775
Deleting one project will no longer incorrectly move folders from archived projects to the trash. This protects documents linked to archived projects from accidental disruption while still cleaning up folders that are truly unused.
Original PR description
Deleting a project also sends the folders of every archived project to the trash. ### Steps to reproduce - Install `documents_project`, where each project has its own Documents folder linked through…
Deleting a project also sends the folders of every archived project to the trash.
### Steps to reproduce
- Install `documents_project`, where each project has its own Documents folder linked through `project.project.documents_folder_id`.
- Create `Project 1`, `Project 2`, and `Project 3`, then archive the first two.
- Delete `Project 3`.
- The folders of `Project 1` and `Project 2` are moved to the trash with their contents, although both projects still exist and still reference them.
### Cause
`_archive_folder_on_projects_unlinked` only archives folders that are no longer used by any project. This was checked through a `documents.document` domain on `project_ids`.
The domain mixed two conditions on the same relation:
- `('project_ids', '!=', False)` checks that a folder has users,
- `('project_ids', 'not any', [('id', 'not in', self.ids)])` checks that it has no users outside the projects being deleted.
Those conditions are not evaluated the same way by the ORM. The first one keeps archived projects visible by disabling `active_test` internally, while the second one searches `project.project` normally and hides archived projects.
An archived project can therefore be counted as a folder user by one condition and ignored by the other, causing its folder to be archived.
### Fix
Check remaining users directly on `project.project` with `active_test=False`, so archived projects are included. Since only folders of deleted projects can become unused, the search starts from those folders instead of scanning all Documents.
opw-6442976
Forward-Port-Of: odoo/enterprise#127217This fix prevents subscription invoicing from crashing when an automatic payment fails due to an invalid or faulty payment token. It helps recurring billing jobs continue handling failures cleanly instead of stopping with an error.
Original PR description
Step to reproduce: - create a faulty token that won't work and link it to a subscription - launch the recurring invoice cron - the following traceback occurs ``` last_tx_sudo = (self.transaction_ids…
Step to reproduce:
- create a faulty token that won't work and link it to a subscription
- launch the recurring invoice cron
- the following traceback occurs
```
last_tx_sudo = (self.transaction_ids - existing_transactions).sudo()
```
When the payment fails, the system rollback and we store the last_tx_sudo value in a dedicated variable. After rollback, the record does not exists anymore. Therefore, accessing the value fails.
```
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/sale_order.py", line 1703, in _handle_automatic_invoices
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1439, in __get__
self.compute_value(record)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1603, in compute_value
records._compute_field_value(self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/models.py", line 4575, in _compute_field_value
determine(field.compute, self)
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 69, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-18.2/sale_subscription/models/payment_transaction.py", line 25, in _compute_renewal_state
if tx.state in ['draft', 'pending']:
^^^^^^^^
File "/home/odoo/src/odoo/saas-18.2/odoo/orm/fields.py", line 1406, in __get__
raise MissingError("\n".join([
odoo.exceptions.MissingError: Record does not exist or has been deleted.
```
Moreover, since https://github.com/odoo/enterprise/pull/45236/files#diff-c36fd7952cc2bef40716419a668de41963d49e1aa4177d9319d503fc260da588R1678-R1682
```
if not last_tx_sudo or not last_tx_sudo.renewal_state not in ['pending', 'authorized']:
```
has become
```
if not last_tx_sudo or last_tx_sudo.renewal_state in ['pending', 'authorized']:
```
But it feels strange to unlink the invoice when the payment succeed.
This PR fixes it.
Forward-Port-Of: odoo/enterprise#129245
Forward-Port-Of: odoo/enterprise#83913This fixes how Odoo identifies extra email or message attachments that are not embedded directly in the message body. It helps ensure attachment lists are consistent and accurate for users viewing messages.
Original PR description
Before this commit, `extra_body_attachment_ids` is declared with `fields.Attr("ir.attachment", { compute() })`, while its compute returns the records of `attachment_ids` that the body does not inline. The model name is therefore the default of an attr field, and only a read inside an update cycle answers that string, as the compute runs on the first read outside one. No reader of the field does that today.
This commit declares the field as the `fields.Many("ir.attachment")` its compute returns, so that the declaration matches the value before the first compute as well as after.
Note that the added test asserts that a message inlining one of its two images lists only the other one, which nothing covered so far. It passes without this change.
Forward-Port-Of: odoo/odoo#284628
Forward-Port-Of: odoo/odoo#284445Automated onboarding tours now handle drag-and-drop steps more reliably, preventing tours from getting stuck during guided setup flows. This improves the reliability of Project and Helpdesk onboarding checks without changing everyday user workflows.
Original PR description
Robot mode (onboarding tours replayed with real actions instead of a human) got stuck on drag&drop steps: - tour_step_interactive.js's findTrigger() returned undefined for a "drag" event when no draggable ancestor was found, instead of falling back to the element itself. - tour_interactive.js's drop conditional only matched the exact pointerup/drop coordinates; clamp the point into the drop target's rect first, since it can land just outside due to rounding. - Reset tour.anchorEl when the pointer target disappears so a stale element isn't reused. Also mark project_tour's synchronization-only steps (waiting for a dirty form, a dropdown, ...) as isActive: ["auto"], since robot mode performs the real action and doesn't need them, and add project_tour and helpdesk_tour to the onboarding tours test coverage.
This fixes several issues when editing an Add to Cart button in the website builder, including action changes not applying, crashes after deleting the icon, and broken button content after copy/paste or text edits. This helps website editors reliably customize shopping buttons without creating broken storefront elements.
Original PR description
The commit c5a40a608280017ae9ea8f9e9e1c59f778d629ae updated to icons to use `data-icon` attribute instead of `fa-*` classes. This commit adapts the `addToCartAction` to correctly update the icon (by…
The commit c5a40a608280017ae9ea8f9e9e1c59f778d629ae updated to icons to use `data-icon` attribute instead of `fa-*` classes. This commit adapts the `addToCartAction` to correctly update the icon (by changing the attribute instead of the class). And fixes a few bugs related to that action as well. Steps to reproduce: - Open website builder - Drop a "Add to cart button" - Select a "Product" with no variants (for example "Chair protection") - Change the "Action" - Bug: the action is not changed (but a class with no effects is added) - - Select text in it - Copy - Paste - Bug: there is a `<button>` in a `<button>` - - Move caret just before the icon - Type text - Bug: the text goes outside the button - - Select a "Product" with no variants (for example "Chair protection") - Delete the icon - Change the "Action" - Bug: crash - - Select a "Product" with no variants (for example "Chair protection") - Select a few letter - Set their style to bold - Change the "Action" - Bug: only part of the text is changed task-6466422
Deleting an employee leave now triggers the related payslip information to be recalculated. This helps payroll stay accurate when time-off records are removed, reducing the risk of incorrect employee payments.
Original PR description
task-6510625 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
The app switcher has been adjusted to run more smoothly on mobile devices, particularly Android devices using Firefox. This improves day-to-day usability by reducing lag when moving between apps.
Original PR description
Prior to this commit, the app switcher was laggy and difficult to use on some mobile devices, especially on Android devices running Firefox. This commit removes and adjusts the CSS properties responsible for the performance issues.
When a leave entry is deleted, related payslips are now recalculated so payroll stays accurate. This helps prevent incorrect salary calculations caused by outdated leave information.
Original PR description
task-6510625
Corrects a setup error in the Turkish Nilvera e-invoicing module so the zero VAT warning works as intended. This prevents invoice processing errors when the system checks whether a sales invoice should display the zero VAT warning.
Original PR description
The `l10n_tr_zero_vat_warning` field is a boolean field but was incorrectly defined as [binary], causing the compute method to fail when assigning a boolean value. ```py File…
The `l10n_tr_zero_vat_warning` field is a boolean field but was incorrectly defined as [binary], causing the compute method to fail when assigning a boolean value.
```py
File "/home/odoo/src/odoo/saas-19.3/addons/l10n_tr_nilvera_einvoice/models/account_move.py", line 156, in _compute_l10n_tr_l10n_tr_zero_vat_warning
invoice.l10n_tr_zero_vat_warning = exempt_zero_tax and invoice.l10n_tr_gib_invoice_type == 'SATIS' and exempt_zero_tax in invoice.line_ids.tax_ids
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.3/odoo/orm/fields.py", line 1892, in __set__
self.write(protected_records, value)
File "/home/odoo/src/odoo/saas-19.3/odoo/orm/fields_binary.py", line 151, in write
super().write(records, value)
File "/home/odoo/src/odoo/saas-19.3/odoo/orm/fields.py", line 1583, in write
cache_value = self.convert_to_cache(value, records)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.3/odoo/orm/fields_binary.py", line 83, in convert_to_cache
raise TypeError(f'{self}: use BinaryValue instead of {value.__class__.__name__}')
TypeError: account.move.l10n_tr_zero_vat_warning: use BinaryValue instead of bool
```
upg-4608394
[binary]: https://github.com/odoo/odoo/pull/242043/changes#diff-d3cbb345d0a5855b7d7aa91e64a0ff480e3e5acfa3b2c71503a23ca7f3c0c511R132
Forward-Port-Of: odoo/odoo#284165Changing the project on timesheets in bulk or through automated updates now clears any task that does not belong to the new project. This prevents inaccurate timesheet links and helps keep reporting and project tracking consistent.
Original PR description
When modifying project_id on a timesheet through mass edit/rpc or anything that is not triggering `onChange`. The task_id would not be reset if it doesnt' belong to the new project set on the timesheet. Steps to reproduce: ------------------- * Install studio for easier reproducing of the issue * Open the timesheet list view * Open studio and activate the mass edit on the view * Modify the project_id on multiple records > Observation: The task_id stays the same even if they do not belong to the new set project Why the fix: ------------ Instead of relying only on the onChange we add an inverse to the project_id that will reset the task when needed. opw-6259149 Forward-Port-Of: odoo/odoo#283882
This fix makes an automated website test wait until the relevant page record is fully selected before deleting it. It reduces random test failures, helping keep website-related quality checks stable without changing user-facing behavior.
Original PR description
Fix the random tour failure by making sure the record is selected before trying to delete it. runbot-944542 Forward-Port-Of: odoo/odoo#280644
This fixes website page caching so pages are refreshed after a visitor changes cookie preferences, such as moving from denying to accepting cookies. It helps ensure visitors see the correct page behavior and consent-dependent content instead of an outdated cached version.
Original PR description
Initially with [commit 958b41c4], when cookies were denied (the page is cached a 1st time), then accepted (the page cache must be invalidated), cached pages would be computed again. This behavior was lost with [6c8a90ec], since which website pages are cached more aggressively. [commit 958b41c4]: https://github.com/odoo/odoo/commit/958b41c4acec7e1700ca4d6e0b25ee0ad2aac9f1 [6c8a90ec]: https://www.github.com/odoo/odoo/commit/6c8a90ecba45fb99addf1b86fe237fd626fba650 task-6471290 Forward-Port-Of: odoo/odoo#284477 Forward-Port-Of: odoo/odoo#282737
Changing the project on timesheet entries now automatically clears any task that does not belong to the new project, even when updates are made in bulk or through automated processes. This prevents timesheets from being linked to inconsistent project and task combinations, improving data accuracy for reporting and billing.
Original PR description
When modifying project_id on a timesheet through mass edit/rpc or anything that is not triggering `onChange`. The task_id would not be reset if it doesnt' belong to the new project set on the timesheet. Steps to reproduce: ------------------- * Install studio for easier reproducing of the issue * Open the timesheet list view * Open studio and activate the mass edit on the view * Modify the project_id on multiple records > Observation: The task_id stays the same even if they do not belong to the new set project Why the fix: ------------ Instead of relying only on the onChange we add an inverse to the project_id that will reset the task when needed. opw-6259149 Forward-Port-Of: odoo/enterprise#128799
Users can now update rental start or end dates on sales orders even if they do not have direct access to planning slots. The related planning entries are still updated in the background, reducing errors and keeping rental schedules aligned.
Original PR description
This commit prevents a potential access error, if a user changes the rental start date and/or end date of a sale order without the access rights to the 'planning.slot' model. In this case, we want the write to be executed and changes repercuted to the associated slots. Forward-Port-Of: odoo/enterprise#128778 Forward-Port-Of: odoo/enterprise#128365
The IoT display browser now starts only after the system has finished initializing. This prevents startup error pages and helps the browser open correctly in fullscreen, improving reliability for IoT device displays.
Original PR description
Before this commit, the display driver (and therefore browser) were being started too early, causing the following issues: - The browser initially displays an error page, as it tries to load the status page before Odoo has finished starting. - The browser window is not fullscreen. This may be because it is started before labwc is fully initialised, as the correct fullscreen command line arguments are used. The second issue can be fixed by restarting the Odoo service, the first happens every time. After this commit, we start the IoT interfaces in the main run method, instead of when the module is imported, meaning that everything else has time to finish initialising. This solves both problems. task-6469793 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284386
Very short timesheet assistant events that are ignored will no longer change the user's active project or task. This prevents small, accidental activity records from influencing future timesheet suggestions and keeps time tracking more accurate.
Original PR description
Before this Commit, small events (<60s) would be ignored but could still set or change the project and task of future events. After this Commit, if an event is small enough to be ignored by the assistant, it is also unable to change the current project or task of the user. For this commit to work correctly, it is expected that each event objects from the assistant has a duration value. task-[6486183](https://www.odoo.com/odoo/project/4105/tasks/6486183) Forward-Port-Of: odoo/enterprise#128700
Deleting multiple email templates at once now works correctly in Field Service Planning. This prevents an error that blocked users from cleaning up templates in bulk, while still protecting the customer ratings template from accidental deletion.
Original PR description
Steps to reproduce: - 1. Install `planning_field_service`. 2. Open Settings > Technical > Email Templates. 3. Select two templates and delete them. Issue: - The deletion crashes with `ValueError: Expected singleton: mail.template(290, 212)`, and several templates can no longer be deleted at once. Cause: - `_unlink_customer_ratings_mail_template` guards the template configured for intervention customer ratings, but it reads `self.id`. An `@api.ondelete` hook is called once with the whole recordset being unlinked, so it raises as soon as more than one template is deleted. Fix: - Look up the configured template id in `self.ids` instead. task-6488394 Forward-Port-Of: odoo/enterprise#128905
This fixes an intermittent failure in an automated test for German POS certification by ensuring the test waits for order synchronization before checking the table badge. It helps keep the validation pipeline stable and reduces false failures that can delay releases.
Original PR description
Sometimes, `test_fiskaly_basic_order` test fails with the following error: ``` AssertionError: FAILED: [55/68] Tour FiskalyTour -> Step body:has(.pos-leftheader .badge:contains(5)). Element (body:has(.pos-leftheader .badge:contains(5))) has not been found. ``` `FloorScreen.clickTable()` clicks on the table and waits for a badge to appear. The badge is rendered once the table order is synced to the server. If the order is still syncing when the click on the table lands, the badge will not be present and triggers the failure. runbot-940256 Forward-Port-Of: odoo/enterprise#128998
The message interface styling was simplified by removing an expensive visual rule that offered little visible benefit. This should help keep the mail experience responsive while preserving the overall look for users.
Original PR description
This PR cleans up a complex selector that is quite costly without providing any striking visual value. task-6481656 Forward-Port-Of: odoo/enterprise#128906
Point of Sale payments using eWallets or gift cards now apply the full redeemed balance even when the related discount tax is forced to be tax-excluded. This prevents one-cent mismatches where the card balance is fully consumed but the customer order receives a slightly smaller discount.
Original PR description
When paying with an eWallet or gift card in POS, the reward line could end up 0.01 short of the actual card balance if the discount product's tax is configured as tax-excluded through a per-tax…
When paying with an eWallet or gift card in POS, the reward line could end up 0.01 short of the actual card balance if the discount product's tax is configured as tax-excluded through a per-tax override, regardless of the tax's own default configuration. The card is still debited for the full balance, but the order is only discounted by one cent less, so the amount charged to the customer no longer matches the amount consumed from the card. Steps to reproduce: ------------------- * Top up an eWallet (or gift card) with a balance of 10.00 * On the eWallet/gift card program's discount product, set an 18% tax whose Tax Computation is overridden to "Excluded" (price_include_override = tax_excluded), independently of the company's default tax configuration * In POS, add a product to an order and pay (partly) with that eWallet/gift card > Observation: Only 9.99 is deducted from the order total, while the backend correctly shows 10 consumed on the wallet/gift card. Why the fix: ------------ The reward line's price_unit was reconstructed from a one-time backward tax computation, then kept only the tax amount for taxes whose price_include field was true, dropping it for any tax forced excluded. That price_unit was later re-taxed forward using the tax's real (excluded) configuration, and the two roundings don't agree for rates like 18%, losing a cent. We now force special_mode "total_included" whenever an eWallet/gift card reward line's taxes are computed, not just at creation, so its tax-included total always equals the exact redeemed amount regardless of how the tax is configured, and store price_unit as that target amount directly. opw-5819389 Forward-Port-Of: odoo/odoo#284397 Forward-Port-Of: odoo/odoo#278568
The website builder now shows custom gradient buttons without a border when the border is set to zero. This prevents editors from seeing a misleading preview and helps ensure the editing experience matches the final website result.
Original PR description
Steps to reproduce: 1. Create a button 2. Choose "custom" in type 3. Put a gradient for Fill option 4. Remove border (put 0) Problem: Since [this commit][1] support has been added to preview custom…
Steps to reproduce: 1. Create a button 2. Choose "custom" in type 3. Put a gradient for Fill option 4. Remove border (put 0) Problem: Since [this commit][1] support has been added to preview custom buttons with gradient backgrounds in the website builder. However, if a user sets the border to 0px it a false border is shown. This is inconsistent the changes that will be applied to the button on the website. The cause is how the preview border is set. In the same [commit][1], borders are previewed at 2px regardless of their actual size. This works for solid background buttons but causes a gradient pseudo-border to appear with custom gradient buttons. Solution: The solution is to set the preview button's border-width styling to 0px in the case when the border is being changed and its width is set to 0. This styling does not affect the classes applied to the actual button being edited and is removed if the border thickness is changed again. [1]: https://github.com/odoo/odoo/commit/291a77c50f19622f8083a5e3798c17b49f3b1c7e task-6296905 Forward-Port-Of: odoo/odoo#279165
This fix prevents multiple Point of Sale devices from accidentally reusing the same empty draft order. It avoids duplicated order identifiers and helps keep restaurant table assignments intact when staff work across shared terminals.
Original PR description
When using multiple devices sharing draft orders, a race condition can happen where one device reuses another device's empty synced draft order. This leads to duplicate UUIDs, which triggers automatic order merging in `sync_from_ui` on the server and clears the table association. To prevent this: - Filter out synced orders (`!order.isSynced`) in `getEmptyOrder()`, `createOrderIfNeeded()`, and `setTable()` when looking for reusable empty orders. - This ensures each terminal only reuses its own locally created, unsynced empty orders, guaranteeing unique UUIDs per device session. task-id: 6296661 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269551
The overtime rules screen now shows the related employee versions button only to HR managers. This prevents HR officers from seeing an access-rights error when opening overtime rule records, improving reliability during normal use and upgrades.
Original PR description
The button requires the group `hr.group_hr_user`, but the button uses `versions_count`, that in its computation uses fields like `contract_date_start` that require the group `hr.group_hr_manager`. To avoid the mismatch, the button is restricted to only managers. This error was found in upgrades failing. To reproduce: - Install `hr_attendance`. - Assign any employee the Default Ruleset to make the button not invisible. - Change the HR security of your user to Officer. - Go to Attendance->Configuration->Overtime Rulesets and try to see the record. - A message will display the following error: ``` You do not have enough rights to access the field "contract_date_start" on Employee Record (hr.version). Please contact your system administrator. Operation: read User: 2 Groups: allowed for groups 'Employees / Administrator' ``` Forward-Port-Of: odoo/odoo#284194
Email notifications for tracked record changes now correctly display the arrows and parentheses that show what changed. This makes update emails clearer for users while preserving how older messages are displayed.
Original PR description
Bug === When notifying by email a tracking change, the arrow and parentheses are not rendered in the email body. Technical and Constraints ========================= The class `o_track` is only set in…
Bug === When notifying by email a tracking change, the arrow and parentheses are not rendered in the email body. Technical and Constraints ========================= The class `o_track` is only set in the web client template (`mail.Message`). There's no class in the body of the email that is sent. It can be rendered with "notification templates" that we cannot change either (and they just do `t-out="message.body"`, so the body field of the mail message has to be properly rendered). We also need existing mail messages to be rendered correctly, and so we need a way to differentiate mail messages created before the fix from those created after it, to know when to disable the arrow and parentheses. Alternatives ============ We have tough about many solutions, this one is the best we found based on the constraints we have 1. Add a class in 19.3, use that class to not remove the arrow on previous mail message. That solution required a migration script that will change all tracking messages. Because the initial migration of the tracking was really slow, we wanted to avoid that. 2. Add a class, and keep it forever. But that solution makes the body of the mail messages bigger, which defeat one of the purpose of the initial refactoring 3. Change the outgoing email without changing the body of the mail message. That solution was really not reliable (regex change to add the arrow, and we have no clean way to target the tracking rows) 4. During the migration create a system parameter with the date, and compare with the create_date of the mail message to know if we should add the arrows or not (but we will need to keep that system parameter forever, and the code to support both to) Task-6424104 Forward-Port-Of: odoo/odoo#282210
This fixes an internal test issue in the website shop area by making sure inactive products are excluded during test runs. It helps keep automated quality checks stable without changing what customers see in the online store.
Original PR description
Description of the issue/feature this PR addresses: Addresses an issue causing test failures by ensuring that [inactive products](https://github.com/odoo-dev/odoo/blob/dbc917ddc263a330ff70f5edec716ccafe88d7a6/addons/website_sale/tests/test_product_filters.py#L93-L99) are filtered out rather than leaking from the environment into the test execution. I have verified that this issue does not allow [inactive records to leak to customers](https://www.odoo.com/mail/message/1151343506). runbot-242426 Forward-Port-Of: odoo/odoo#284326 Forward-Port-Of: odoo/odoo#283973
This fix corrects how Belgian payroll reports split severance periods for DMFA declarations, so severance pay is allocated across the right quarters. It also preserves valid manually entered departure dates, reducing reporting errors and avoiding unwanted overwrites of HR adjustments.
Original PR description
- previously, the termination period was split from notice period start to actual departure date, ignoring the theoretical notice duration. Now, it correctly splits from actual departure date to theoretical end date, ensuring proper multi-quarter severance (Code 003) allocation. - Preserve departure_date if after dismissal_date, else default to theoretical notice end. previously the compute always overwrote any user input, ignoring manual adjustments task: 5407737 Forward-Port-Of: odoo/enterprise#112279
Fixes a printing issue where customized sale order PDFs could show an unwanted blank column after a column such as Taxes or Discount was removed in Studio. This keeps section and combo rows aligned correctly, improving the appearance of customer-facing sales documents.
Original PR description
**Steps to reproduce:** 1. Open a Sale Order report in Studio 2. Delete the Taxes column 3. Save 4. Create a sale order with at least one section line and products that have taxes 5. Print the sale…
**Steps to reproduce:** 1. Open a Sale Order report in Studio 2. Delete the Taxes column 3. Save 4. Create a sale order with at least one section line and products that have taxes 5. Print the sale order PDF **Issue:** - A blank column is rendered in the PDF report on section (and combo) rows whenever a column such as Taxes or Discount is removed via Studio. **Why this happens:** - The section row's `colspan` and the combo row's `colspan` were computed using `3 + (1 if display_discount else 0) + (1 if display_taxes else 0)`. - `display_taxes` and `display_discount` are derived from order data (i.e. whether any line has taxes/discounts), not from which columns are actually rendered in the table. - When Studio removes a column it deletes the `<th>` and matching `<td>` elements via XPath, but these Python variables remain `True`. As a result, section/combo rows still accounted for the removed column in their `colspan`, producing one extra cell and a visible blank column. **Fix:** - Introduce a `colspan_count` variable which is incremented inside each `<th>` body - Use that counter for `td_section_name` and `td_combo_name` instead of the previous formula. - Because the increment occurs inside the `<th>` element, it is skipped whenever the element is not rendered, whether because `display_taxes`/`display_discount` is `False` or because Studio's XPath removed the element entirely. opw-6433679 Forward-Port-Of: odoo/odoo#283657 Forward-Port-Of: odoo/odoo#280719
Odoo now recognizes Stripe refunds that were already created after a manually captured payment, even when Stripe sends a refund notification later. This prevents duplicate refund records with the same Stripe reference, helping keep payment and accounting records accurate.
Original PR description
Steps to reproduce: - Configure Stripe with manual capture. - Authorize and capture an online payment. - Refund the captured payment from Odoo. - Let the `charge.refunded` webhook be processed. The refund initiated from Odoo is created as a child of the capture transaction, while the webhook resolves the charge to the source transaction. The webhook only checked direct refund children of that source transaction, so it missed the existing refund and created a second refund transaction with the same Stripe refund reference. Look up existing Stripe refund transactions in the child and grandchild transactions of the source transaction before creating webhook refund transactions, so the webhook recognizes refunds already created under capture children. opw-6359020 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283790 Forward-Port-Of: odoo/odoo#276154
The Point of Sale now automatically chooses a product option when it is the only available choice, as long as the option type is not multi-select. This removes an unnecessary step for cashiers and helps products with simple variants be added to an order smoothly.
Original PR description
Before this commit: ----------- - When a product attribute had only one available value, it was not automatically selected for display types other than multi. After this commit: ------------ - Automatically select the attribute value when an attribute has a single available value and its display type is not multi, allowing the product to be added without any additional user interaction. Task-6327371 Forward-Port-Of: odoo/odoo#282350 Forward-Port-Of: odoo/odoo#272437
The mail thread data request now returns only the information that is actually needed for the current user and conversation. This reduces unnecessary data handling and helps keep mail-related views more consistent across access scenarios, including multi-company cases.
Original PR description
This change cleans up the requested data from `/mail/thread/data` route, ensuring it aligns with what is actually needed depending on the user and thread. part of task-6452761 Forward-Port-Of: odoo/odoo#284452 Forward-Port-Of: odoo/odoo#280713
This fixes an intermittent issue in the Lunch app's automated order check by ensuring the test waits for the intended product to appear after changing location. It helps avoid false failures caused by outdated demo products still showing briefly, improving confidence in release testing without changing user-facing behavior.
Original PR description
The lunch order tour selects `Farm 1` before ordering a product. However, it only waits for the location input to be updated before clicking the first kanban record. With demo data installed, a product from the previous location can still be displayed while the product model is being reloaded. The tour can therefore order a demo product instead of the product created by the test. This notably fails during weekends when the corresponding demo vendor is unavailable. To fix we need to wait for the product created by the test before clicking it. Besides selecting the intended product, this also ensures that the product reload following the location change has completed. [error-181572 ](https://runbot.odoo.com/odoo/error/181572) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284626 Forward-Port-Of: odoo/odoo#281753
Argentinian accounting users can now create invoices for foreign customers even when export journals are unavailable or archived. Instead of stopping the workflow with an error, the system falls back to a standard invoice type so sales can continue without extra journal setup.
Original PR description
### Issue before this commit: Before this commit, users were completely blocked from creating an invoice for a foreign partner (e.g., "Cliente del Exterior") if all exportation journals were archived…
### Issue before this commit: Before this commit, users were completely blocked from creating an invoice for a foreign partner (e.g., "Cliente del Exterior") if all exportation journals were archived or unavailable, as the system would immediately trigger a RedirectWarning error. ### Steps to reproduce the issue: 1. Download Accounting and l10n_ar 2. Go to contacts and create a new one with: 1. Country as United States 2. VAT number ex. 55000002126 3. AFIP Responsibility Type as Cliente del Exterior 3. Go to Journals, filter for sales journals and archive: 1. Electronic Exportation Invoice (FEX) 2. Expo Sales Journal 4. Go to invoices and create a new one for the client you just created 5. As soon as you insert the client you will receive the error: You are trying to create an invoice for foreign partner but you don't have an exportation journal ### Cause of the issue: https://github.com/odoo/odoo/blob/014d58e3204d17db6dcba3c8ab7d8ad35003300e/addons/l10n_ar/models/account_move.py#L186-L189 The _onchange_partner_journal method rigidly enforced the use of an exportation journal for foreign AFIP responsibility types (codes 8, 9, and 10). If the query failed to find an active export journal, the code intentionally threw a hard error instead of providing a fallback mechanism. ### Reason to introduce the fix: This fix is introduced to prevent unnecessary workflow blocks. By catching the missing journal and defaulting the document type to "Invoice B" (code 6), the user can now successfully generate the invoice using a standard domestic sales journal without being forced to configure an exportation journal. opw-6442501 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284629 Forward-Port-Of: odoo/odoo#282971
Chat windows now keep their usual display priority by default while allowing other parts of Odoo to adjust how they appear on top of screens when needed. This prevents layout conflicts on mobile views and makes future customizations safer without disrupting existing behavior.
Original PR description
The z-index of chat windows on mobile views was previously fixed at `1020`, preventing other modules from adjusting their stacking order. This commit introduces a configurable z-index for chat windows, defaulting to `1020` while allowing other modules to override it when needed. task-6412411 Forward-Port-Of: odoo/odoo#283671 Forward-Port-Of: odoo/odoo#283178
Contacts now validate tax numbers using the country set on the partner record instead of guessing from the first two characters of the tax number. This prevents valid Mexican RFCs and similar identifiers from being incorrectly treated as foreign VAT numbers, reducing false validation errors when saving contacts.
Original PR description
**Issue**: If a partner has a tax number that begins with a different country’s code (for instance, a Mexican RFC number that begins with “RO”, matching Romania), when a user tries to add the tax…
**Issue**: If a partner has a tax number that begins with a different country’s code (for instance, a Mexican RFC number that begins with “RO”, matching Romania), when a user tries to add the tax number to the partner, there will be a validation error.
**Steps to reproduce** (on fresh database with Contacts app and l10n_mx module installed):
1. Make a new contact.
2. Give the contact a Mexican address.
3. Give the contact the RFC number (or `vat` field): ROS561231GR8.
4. Try to save this change. Observe the validation error.
**Explanation**:
The `get_all_identifiers` method uses the first two characters of `partner.vat` as a heuristic to detect the issuing country, since many VAT formats start with a country code (e.g. RO1234567897). This prefix was used unconditionally whenever it matched an item from `get_tin_metadata_of_country`. without checking whether the VAT actually belongs to that country. Some countries' identifier formats begin with letters which are not country codes. In Mexico, for instance, RFC numbers start with letters derived from the partner's name, so a partner named “Sofia Rodriguez” would get an RFC starting with “RO”. Therefore, this heuristic can produce false-positive matches against unrelated countries.
**Solution**:
We no longer use a partner's vat number to detect the issuing country. Instead, we use the partner's `country_code` field as the issuing country.
opw-6471006
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#283376This update makes an error message clearer when sending a credit note through French e-invoicing in demo mode. Users should better understand what went wrong during EDI document generation, reducing confusion and support effort.
Original PR description
Steps to reproduce: - Install `l10n_fr_pdp` module > Switch to `FR Company` - Activate `French e-invoicing` (Demo mode) - Create a New `Credit Note` with `FR Customer` > Send Issue: The system currently displays a confusing error message during EDI document generation. We are making the error message clearer and more user-friendly. opw-6412521 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284189
Sales quotation and pro forma email templates now use separate complete sentences for quotations and orders. This lets translators adapt grammar correctly in languages where the words require different articles or adjective forms, improving customer-facing email quality.
Original PR description
The quotation and pro forma email templates inserted either "quotation" or "order" into shared translatable text. In French, for example, "devis" is masculine while "commande" is feminine, so the surrounding articles and adjectives cannot agree with both terms. Define a complete sentence for each document state so translators can translate the surrounding grammar independently. opw-6445304 Forward-Port-Of: odoo/odoo#284596 Forward-Port-Of: odoo/odoo#283229
Odoo no longer lets users choose Peppol identifier codes that have been deprecated or removed from the official specification. This helps prevent invalid Peppol registrations and partner records, reducing errors in electronic invoicing setup.
Original PR description
Peppol EAS codes 0037, 0213, 9955, and 0193 are deprecated or removed from the Peppol specification but are still present in the selection field on stable branches, allowing users to register invalid identifiers. See: [eas codes](https://docs.peppol.eu/edelivery/codelists/v9.7/Peppol%20Code%20Lists%20-%20Participant%20identifier%20schemes%20v9.7.html) Before: - deprecated EAS codes were listed alongside valid ones in the partner's available Peppol EAS options, allowing users to select an outdated identifier for new or duplicated partners, or during Peppol registration. After: - Excluded deprecated EAS codes from the available Peppol EAS selection list on partners, preventing users from selecting them for new or duplicated partners, or during Peppol registration. Removed Deprecated codes in Master: odoo/odoo#271288 Task [link](https://www.odoo.com/odoo/project.task/6299691) task-6299691 Forward-Port-Of: odoo/odoo#284062 Forward-Port-Of: odoo/odoo#271435
A small configuration error meant two important accounting report records were not individually protected from deletion as intended. This fix corrects the list so both reports remain safely protected, reducing the risk of accidental removal.
Original PR description
On `ir.actions.report` we want to block the unlinking of specific reports in odoo. However, when the list was created a comma was missed between `action_account_original_vendor_bill` and `account_invoice_without_payment` which means we were actually protecting against people unlinking `action_account_original_vendor_billaccount_invoice_without_payment`. Adding in that comma will allow these two records to be properly protected. task-none Forward-Port-Of: odoo/odoo#283323
When multiple projects are duplicated at the same time, each copied project now receives only the milestones from its original project. This prevents copied projects from being cluttered with unrelated milestones from other selected projects, keeping project plans accurate.
Original PR description
Before this commit, duplicating several projects at once from the list view gave every copy the milestones of all the duplicated projects, because the copy loop assigned the milestones of the whole recordset instead of the ones of the project being copied. Duplicating a single project behaves correctly, which hid the issue. Steps to reproduce: - create two projects with milestones enabled, add a milestone to the first one and two others to the second one - select both projects in the list view and duplicate them Each copy contains the three milestones instead of only the milestones of its original project. Solution: Copy the milestones of the project being duplicated. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278520
Daily time off accruals based on worked time now respect the employee's local working calendar. This prevents employees on Monday-to-Friday schedules from incorrectly earning time off on Saturdays in certain time zones.
Original PR description
## Current behavior: On a Monday–Friday working schedule, a Daily accrual plan that is based on worked time grants accrued time on Saturday as well, even though Saturday is not a working day. The…
## Current behavior: On a Monday–Friday working schedule, a Daily accrual plan that is based on worked time grants accrued time on Saturday as well, even though Saturday is not a working day. The employee accrues on 6 days per week instead of 5 (Sunday is correctly skipped. Only Saturday is wrong). ## Expected behavior: The employee accrues only on the 5 working days (Mon–Fri) → 5 grants per week. Saturday and Sunday should add nothing. ## Setup: - Working schedule: Standard 40h/week, Monday–Friday, 08:00–17:00. - All timezones set to Australia/Brisbane (UTC+10) and matching: employee, working schedule, and user are all the same timezone. - Accrual plan milestone: accrue 5 Hours, Daily, "At the end of the accrual period", "Based on worked time = Yes". ## Steps to reproduce: - Create the working schedule and accrual plan above, with the calendar timezone set to Australia/Brisbane. - Assign the accrual allocation to an employee, Starting on a Monday. - On the Time Off dashboard, use "Balance at the (date)" to project the balance day by day across a weekend (Friday → Saturday → Sunday → Monday). ## Cause of the issue: Accrual period boundaries were built as naive UTC midnights instead of local calendar midnights. ## Fix: Localize accrual period boundaries in the employee/resource timezone before calling resource calendar APIs. This bug is reproducible in multiple versions. PRs for: - v19.0: https://github.com/odoo/odoo/pull/279029 - v18.0: https://github.com/odoo/odoo/pull/279036 opw-6316062 Forward-Port-Of: odoo/odoo#283583 Forward-Port-Of: odoo/odoo#279029
This fix ensures that country-specific mandatory customer fields remain visible when creating or editing customers in Point of Sale. It helps businesses in affected localizations complete invoicing and customer records correctly without missing legally or operationally required information.
Original PR description
*: l10n_{ar,co,in,pe,uy}_pos **Problem:** The POS "Edit/Create customer" Form was switched to a standalone, hardcoded form view (view_partner_form_pos_ui) that inherits nothing during [1], in the…
*: l10n_{ar,co,in,pe,uy}_pos
**Problem:**
The POS "Edit/Create customer" Form was switched to a standalone, hardcoded form view (view_partner_form_pos_ui) that inherits nothing during [1], in the attempt to simplify the view when accessed from the PoS.
Every field that localizations and other modules add to the partner form by inheriting base.view_partner_form therefore disappeared when accessed from PoS. Some of the fields are required, for example, to invoice.
**Solution:**
Keep the simplified view as the default, but route the view selection through an overridable hook that localization can tweak case by case. The override is applied to the affected POS bridges (see module list).
Add a test to prevent future regression.
**Note:**
Another possibility is to re-inherit for each localization the new
standalone view, but this fix would need to update the module to work,
while this one works with just a restart.
There are still ongoing discussion with PoS team to see if we really
want to go back to each localization needing to inherit backend views.
[1]: https://github.com/odoo/odoo/pull/230721/changes#diff-66cd201e7e8cfff5218a9fa93efd72f0bd77659b87359f2ca8763702462aaf92R26
opw-6244777 (many more)
Forward-Port-Of: odoo/odoo#268158Peruvian accounting reports now use the exchange rate already stored on each accounting entry instead of recalculating it during report generation. This reduces rounding discrepancies and helps businesses get more reliable report figures.
Original PR description
Previously, the `_get_ple_report_data` method computed the currency rate when called. Since the calculation was based on the entry totals, it was prone to rounding errors. This PR makes it use the rate stored in the entry itself. This should lead to more accurate results. opw-6411322 Forward-Port-Of: odoo/enterprise#128027 Forward-Port-Of: odoo/enterprise#126882
This update adjusts an internal automated test related to mobile mail notifications so it continues to match recent template tracking behavior. It helps keep quality checks reliable without changing the product experience for users.
Original PR description
Task-6424104 Forward-Port-Of: odoo/enterprise#127778
The French VAT report submission now treats notes containing only spaces as empty. This prevents incomplete files from being sent to ASPOne and avoids avoidable submission errors for users.
Original PR description
While sending the tax return to ASPOne, before adding the BC zone we are checking that BA zone won't be empty as if BC is completed there must be the BA zone in the xml file. The problem is that when we have only whitespaces, the condition will be respected but later on due to cleanup_xml_node(), the BA zone will not be rendered in the xml but BC will and it leads to an error This commit checks that express_mention_reason fields is not empty or not only whitespaces task-6476440 Forward-Port-Of: odoo/enterprise#128242
AI chat windows now appear in front of other chats and fullscreen editing screens on mobile. This prevents AI assistance and related popups from being hidden, making the feature usable in those views.
Original PR description
AI chats opened on mobile views could appear behind other chats. This was inconsistent with the expected stacking behavior, where newly opened chats should appear on top of existing ones. To reproduce: * Open the chatter of any module. * Open the message composer in fullscreen mode. * Click the AI button. This commit increases the z-index of AI chats on mobile views so they are displayed on top of other chats. task-6412411 Forward-Port-Of: odoo/enterprise#128649 Forward-Port-Of: odoo/enterprise#128346
Appointment invitation emails can now generate public calendar links without running into permission errors. This helps ensure invitees receive working calendar links and reduces failures when sending appointment-related emails.
Original PR description
Since calendar attendee access tokens are restricted to system users, appointment mail templates must sudo token reads when generating public calendar links. This follows the same pattern as the calendar mail templates and avoids an AccessError when rendering attendee invitation emails. ref: https://github.com/odoo/enterprise/commit/88a3cca752a5f726cd0260b485fc93f65a268cf8 Forward-Port-Of: odoo/enterprise#128959
Fixed a timing issue that could cause Point of Sale AvaTax orders to reload incorrectly after payment, sometimes making selected order lines disappear. The change makes order synchronization more reliable and improves automated test stability for AvaTax checkout flows.
Original PR description
The POS Avatax tour sporadically failed after returning from the payment screen. The race was reproduced locally by delaying the mocked Avatax response in mocked_request(). There's three somewhat…
The POS Avatax tour sporadically failed after returning from the payment screen. The race was reproduced locally by delaying the mocked Avatax response in mocked_request(). There's three somewhat related fixes. Firstly, get_order_tax_details() calls sync_from_ui(), which emits a SYNCHRONISATION notification. Unlike the normal POS sync path, the Avatax RPC did not pass the device context. The browser therefore treated its own notification as coming from another device and started an independent reload of open orders. That reload could replace the current order state after the tour returned to the product screen, causing the selected order line to disappear. We now pass the normal sync context so the browser can properly ignore its own notification. Secondly, we'll keep the complete sync_from_ui() response and replace its order, line, tax, and tax group data after the AvaTax calculation. We then simplify the processing client-side by moving towards the established pattern in the POS: missingRecursive() to load any other referenced records, and then pass that through loadConnectedData(). Lastly, clickPayButton() only waits for the payment screen element to be displayed. The AvaTax request starts from the screen's onMounted() callback, leaving a short window where the screen and its buttons exist but the request and UI blocker have not started yet. The next tour step can probably run during that window. To make sure this can't happen we explicitly waitRequest(). This first waits for requests to appear and then waits for them to complete. runbot-error-944281 Forward-Port-Of: odoo/enterprise#125944
Users can now open links included in spreadsheet cell comments with a normal click, as expected. This removes a frustrating interaction issue and makes shared references in comments easier to access.
Original PR description
Current behavior before PR: - Clicking a link in a cell comment did not work. A left click was blocked, while Ctrl+click (or Cmd+click) opened the link in a new tab. - This was caused by `t-on-click.prevent` on the comment thread and popover. It was originally added because the scroller service used the URL hash to scroll to anchors, which was removed in https://github.com/odoo/odoo/commit/711e9c9f24818714129f55283e2df64503d93605 Desired behavior after PR is merged: - `t-on-click.prevent` is removed and links in cell comments can be opened normally with both left click and Ctrl+click (Cmd+click on macOS). Task: [6448651](https://www.odoo.com/odoo/project/2328/tasks/6448651) Forward-Port-Of: odoo/enterprise#129188 Forward-Port-Of: odoo/enterprise#127473
This fix restores required customer information fields in the Point of Sale customer form for several country-specific invoicing flows. It prevents missing mandatory data from blocking invoices or compliance-related sales processes in affected localizations.
Original PR description
*: br,cl,ec,gt,it,ke,mx **Problem:** The POS "Edit/Create customer" Form was switched to a standalone, hardcoded form view (view_partner_form_pos_ui) that inherits nothing during [1], in the attempt…
*: br,cl,ec,gt,it,ke,mx **Problem:** The POS "Edit/Create customer" Form was switched to a standalone, hardcoded form view (view_partner_form_pos_ui) that inherits nothing during [1], in the attempt to simplify the view when accessed from the PoS. Every field that localizations and other modules add to the partner form by inheriting base.view_partner_form therefore disappeared when accessed from PoS. Some of the fields are required, for example, to invoice. **Solution:** Keep the simplified view as the default, but route the view selection through an overridable hook that localization can tweak case by case. The override is applied to the affected POS bridges (see module list). Add a test to prevent future regression. **Note:** Another possibility is to re-inherit for each localization the new standalone view, but this fix would need to update the module to work, while this one works with just a restart. There are still ongoing discussion with PoS team to see if we really want to go back to each localization needing to inherit backend views. [1]: https://github.com/odoo/odoo/pull/230721/changes#diff-66cd201e7e8cfff5218a9fa93efd72f0> opw-6244777 (many more) Forward-Port-Of: odoo/enterprise#119316
Cash in/out receipts in Point of Sale can now print even when a default printer has not been configured. The system now falls back to an available printer, reducing failed receipt printing during cash management operations.
Original PR description
## Description Fixes cash in/out receipt printing when no default printer is configured. ## Issue Previously, an early return in the printer selection logic prevented the fallback printer mechanism from being executed, causing receipt printing to fail when no default printer was configured. ## Fix Removed the early return so that the fallback printer selection logic can select an available printer before attempting to print the receipt. This ensures cash in/out receipts can be printed even when no default printer is configured. opw-6485495 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283789
A bug was fixed so planning sessions linked to quotations only use real sale order lines, not section or note rows from quotation templates. This prevents errors in a specific Field Service planning flow and helps keep quotation-to-planning links accurate.
Original PR description
This commit patches a niche bug involving creating a quotation via a quotation template containing a line section, then connecting it to an active planning session. The current architecture did not filter out `line_section` or `line_note` typed lines. This updated search domain resolves this issue. opw-6351484 Forward-Port-Of: odoo/enterprise#129228 Forward-Port-Of: odoo/enterprise#125056
This update fixes incorrect tax configuration details for Hungary in Odoo’s localization and electronic invoicing modules. It helps Hungarian companies apply and report taxes more accurately, reducing the risk of configuration-related accounting errors.
Original PR description
Adjusting incorrect tax configuration elements for Hungary. task-6397915 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284432 Forward-Port-Of: odoo/odoo#282697
This fixes an inventory forecast issue where subcontracted components could incorrectly appear as available before they were actually received or reserved. Businesses using subcontracting and make-to-order routes will see more accurate material availability, helping prevent premature production decisions.
Original PR description
### Steps to reproduce: - Enable Multi-Steps Routes, subcontracting and unarchive the MTO route - Create 3 products: Final Product (FP), Subcontracted Component (SB), Component (COMP) and put SB in…
### Steps to reproduce: - Enable Multi-Steps Routes, subcontracting and unarchive the MTO route - Create 3 products: Final Product (FP), Subcontracted Component (SB), Component (COMP) and put SB in MTO - Create a BOM for FP: 1 x SB - Create a subcontracted BOM for SB: 1 x COMP - Create and confirm an MO for 1 unit of FP > This generates a subcontracted MO for 1 unit of SB - Confrim the subcontracted PO and go back to the MO of FP #### > The component move forecast appears "Available" even if the SB unit is neither received nor 'pre-reserved' (the quantity of the move raw is still 0). ### Cause of the issue: The `forecast_widget` displays an available status in case the demand of the move is expected to be fulfilled and there is no `forecastExpectedDate`: https://github.com/odoo/odoo/blob/a46cdcd9d0b575eb668ed738565637f346bbdf7b/addons/stock/static/src/widgets/forecast_widget.xml#L1-L19 https://github.com/odoo/odoo/blob/4fbd88ad3ac2d92b47b024b96f1c40ed4b3f97e3/addons/stock/static/src/widgets/forecast_widget.js#L15-L26 Now, the issue is that this `forecastExpectedDate` is currently unreliable in this use case as the `forecast_expected_date` of the SB component move is incorrectly computed to be False rather than matching its subcontracted receipt counter part. To be more precise, the `forecast_expected_date` is computed based on the report lines: https://github.com/odoo/odoo/blob/8b8b99e371fcf214b9c55fb2fbfca20f2ee66f53/addons/stock/models/stock_move.py#L579-L581 https://github.com/odoo/odoo/blob/8b8b99e371fcf214b9c55fb2fbfca20f2ee66f53/addons/stock/models/stock_move.py#L2701 The component move is an out move of SB from Stock to Production and is linked to the finished subcontracted move of SB from Production to Subcontracting. In particular, this finished subcontracted move (which is assigned) contributes to the 'reserved' out qties on the get go and leads to an already reserved out quantity of 1.0 even thought the move is purely external and linked to the subcontractor process: https://github.com/odoo/odoo/blob/ef89bc530ffae93a553003559ca9078b7a9d0653/addons/stock/report/stock_forecasted.py#L241-L268 In turn, the `demand_out` matched its `reserved_out` (even thought this reserved_out should be 0) so that no `in_transit` move is provided to provide an `expected_date`: https://github.com/odoo/odoo/blob/ef89bc530ffae93a553003559ca9078b7a9d0653/addons/stock/report/stock_forecasted.py#L426-L435 opw-6445209 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283177
Opening the sales product configurator in debug mode no longer fails when a product has an empty custom attribute value. This keeps sales order editing reliable for configurable products and avoids interruptions for users or testers working with debug mode enabled.
Original PR description
This commit prevents a traceback when opening the product configurator in debug mode. Prop validation only happens in debug mode, which exposed an issue with products that allow entering custom attribute values (e.g. Acoustic Bloc Screen). When a custom value is left empty, it is read as `false` when custom attributes are retrieved from the frontend. As a result, the `custom_value` key in the `customPtavs` prop passed to the product configurator contains a boolean, whereas the prop expects a string. This commit ensures that an empty string is passed instead of `false` when opening the configurator for a line with an empty custom attribute value. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284482
This fixes how product names and descriptions appear in accounting line descriptions when space is limited. Product names will no longer be mistakenly shown in italic after wrapping, making accounting entries easier to read.
Original PR description
This commit removes the CSS hack used to make the description italic when a product is present in an AML. Instead, the product name and description are rendered separately using two spans in the readonly state. The previous `:first-line` approach did not handle line wrapping correctly: when the column was too narrow, part of the product name could wrap onto the next line and incorrectly appear italic. Rendering the two parts separately avoids this issue. Before | After -- | -- <img width="414" height="192" alt="image" src="https://github.com/user-attachments/assets/a178a35c-6d55-4982-a9c3-2fe727c626cc" /> | <img width="399" height="198" alt="image" src="https://github.com/user-attachments/assets/4716f93a-6d47-4631-a982-db43d9d38ef0" /> --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284232
This change makes an internal website editor test more reliable by waiting for the interface to finish updating before checking popup visibility. It helps reduce random CI failures, improving development stability without changing behavior for end users.
Original PR description
The test `undoing something on a target outside s_popup closes it` had a few fails in CI: the `fa-eye-slash` was not set as expected. This commit adds a `waitSidebarUpdated` call just before to ensure owl has no pending rendering when checking the eye. The fix is similar to aaf0f54d1feda60becb0bfbad578b366715c0172 which is about a similar failure in another test. runbot-938967 Forward-Port-Of: odoo/odoo#284381
Odoo now skips caption handling for unusual figure content, such as figures with no images or multiple images, instead of raising an error. This prevents Helpdesk tickets created from incoming emails from failing when the email contains valid but unexpected HTML.
Original PR description
**Steps to reproduce:** - Install Helpdesk - Create an email with a figure that has no image - Send it to Helpdesk email alias - Open up auto-created ticket from the email - `OwlError` is raised on `CaptionPlugin.addImageCaption` **Issue:** `CaptionPlugin` [1] was designed for `<figure>` elements with a single `<img>` and a single `<figcaption>` (mainly for editor direct interactions). But the HTML specifications also allow `<figure>` with 0 or more than 1 `<img>` element(s), in which case an error is raised (or some elements are removed). (see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/figure) **Fix:** Ignore such `<figure>` for now as it would require a rework of the plugin. [1] https://github.com/odoo/odoo/commit/b9d112a5800cfe11dc434caa0d335fa3f3db7178 opw-6413422 Forward-Port-Of: odoo/odoo#284139 Forward-Port-Of: odoo/odoo#279981
When translating content edited inside a related-record dialog, Odoo now saves those pending edits before opening the translation window. This prevents users from seeing outdated or missing text in the translation dialog, helping avoid incorrect translations.
Original PR description
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened…
Clicking the translate button saves the form's root record before opening the translation dialog, since https://github.com/odoo/odoo/commit/9da52919a03dbcee5209430918195158c0652099. A record opened in an x2many form dialog keeps its changes for itself until the dialog is saved, see https://github.com/odoo/odoo/blob/242f6d3cf7288853f163ac6986a3b7aa4279efaf/addons/web/static/src/model/relational_model/static_list.js#L193. Its pending changes are not part of the root record changes, so saving the root sends nothing to the server, and the translation dialog then shows the stored terms instead of the current content, or no terms at all when the stored value is empty. The fix changes openTranslationDialog in translation_button.js, the place that decides which record to save. When the record keeps its changes for itself (record._noUpdateParent), the record is saved directly, like the button did before the commit above. The root record is still saved in the other cases, so the editable list case that commit fixed keeps working. Steps to reproduce: 1. Activate a second language in Settings > Translations > Languages 2. Open the Surveys app, open a survey and click a question in the Questions tab 3. In the Description tab, change the description 4. Click the EN button on the description field => the translation dialog shows the terms of the previous description, not the current one Ticket [link](https://www.odoo.com/odoo/project.task/6237291) opw-6237291 Forward-Port-Of: odoo/odoo#283750 Forward-Port-Of: odoo/odoo#269507
This fixes an issue where editing a chatter message could break contact mentions when one contact's name or ID was contained inside another's. Users can now edit messages with multiple similar mentions without links being corrupted or moved.
Original PR description
# Introduction This PR fixes broken mention links linked to the fact that we replace strings without paying attention to the fact that some strings may contain others that we want to replace later.…
# Introduction
This PR fixes broken mention links linked to the fact that we replace strings
without paying attention to the fact that some strings may contain others
that we want to replace later. This affects both id's and names of records.
See commit messages for more details.
# How to reproduce
- Create Contact A and then Contact B and either :
- Contact B's id need to contain Contact A's id (e.g. Contact B id = 12; Contact A id = 1)
- Contact B's name need to contain Contact A's name (e.g. Contact B name = ABC; Contact A name = AB)
- In a chatter create a message mentionning first Contact B and then Contact A
> Depending on the version, you might need to reload the page here
- Edit the message and save
# The issue
We see a broken mention in the chatter
# Cause
When saving an edited message, we give the raw body of the message (without the mention links) and the mentionend partners to `generateMentionsLinks` : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L152
This method's purpose is to replace the text links ("@Contact A") with actual html links. It does so by enumerating each partner given as an argument and replace the text mention with a placeholder :
https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L158
It will then replace the placeholders with actual links : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L208-L218
The issue is that in both of those steps, we can try to replace a string that is contained
in another string we want to replace.
For exemple :
"string123 some text string12"
If we try to replace "string12" first, then we will select the wrong string :
"[string12]3 some text string12".
opw-6313748
Forward-Port-Of: odoo/odoo#284016
Forward-Port-Of: odoo/odoo#272549This fix adjusts restaurant appointment point-of-sale tests so they continue to work after POS data reloads clear browser storage. It keeps the production behavior unchanged while preventing test failures and improving confidence in future updates.
Original PR description
A recent PR in the community repository introduced a full clear of both `localStorage` and `sessionStorage` when reloading POS data. While this is the intended behavior in production, it breaks the test framework. This commit mocks the `clear` methods directly within the tour steps right before the reload action. This ensures the test survives the page reload and keeps its state, without polluting the core production code with test-specific logic. task-6456447 Forward-Port-Of: odoo/enterprise#129010 Forward-Port-Of: odoo/enterprise#128091
This fix prevents a manufacturing test cleanup from accidentally touching stock rules outside the intended route. It reduces the risk of test failures caused by unrelated demo or company data being removed while keeping the change internal to test behavior.
Original PR description
`test_check_update_qty_mto_chain` was removing `stock.rule` records from other companies using `mto_route.rule_ids.search()`. Calling `search()` on a recordset does not restrict the search to the records already present in that recordset, so the domain was effectively applied to all `stock.rule` records. With demo data, this could attempt to unlink an unrelated stock rule that is still referenced by an existing stock move, causing a `stock_move_rule_id_fkey` foreign key violation. This commit restricts the search explicitly to rules belonging to `mto_route` before unlinking them. [error-940031 ](https://runbot.odoo.com/odoo/error/940031) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282930
This fixes an issue where archiving or deleting one user could wrongly remove a shared contact from restricted discussion channels, even if another active user for that contact still had access. Contacts are now only unsubscribed when none of their remaining users qualifies for the channel, helping teams avoid accidental loss of communication access.
Original PR description
Before this commit, archiving or deleting a user removed its partner from every group restricted channel, even when another user of that partner was still active and in the group the channel requires. This happens because the members to unsubscribe are searched on partner_id alone, so the search cannot tell whether the partner keeps another user. This commit fixes the issue by unsubscribing a partner only when none of its remaining users has the group the channel requires. Forward-Port-Of: odoo/odoo#284354 Forward-Port-Of: odoo/odoo#283807
UPS shipping rate checks now follow UPS documentation by allowing phone numbers between 1 and 15 digits. This prevents valid customers in countries with shorter phone numbers, such as Luxembourg, from being blocked when requesting UPS shipping rates.
Original PR description
**Steps to reproduce:** - Create a contact with a phone number that has 9 characters - Setup an UPS carrier, a configuration that works is UPS Saver as Service Type and UPS Package/customer supplied…
**Steps to reproduce:** - Create a contact with a phone number that has 9 characters - Setup an UPS carrier, a configuration that works is UPS Saver as Service Type and UPS Package/customer supplied as a package type - Create a quotation, put the created contact as a client - Try adding a shipping and getting the rates - An User Error appears, the phone number is too short **Why the fix:** Before this commit, any phone number that was less than 10 characters would raise an User Error, but some countries, such as Luxembourg, use phone numbers that are nine characters long or even less. If we check the official UPS documentation (https://developer.ups.com/tag/Shipping?loc=en_EN#operation/Shipment), we can see in the Ship_to/Phone section, that the phone number should be a number between 1 and 15, not saying it should be 10 characters or more. <img width="495" height="473" alt="image" src="https://github.com/user-attachments/assets/fed82987-ffb8-4b84-b282-6c3d3b4f304e" /> After this commit, we adapt the way we prevent the user from inputing phone numbers to fit the official UPS documentation. opw-6307577 Forward-Port-Of: odoo/enterprise#128632 Forward-Port-Of: odoo/enterprise#122831
The German tax report XML now preserves cents for the Kz83 amount instead of rounding it down to a whole number. This helps ensure reported tax values remain accurate, for example keeping 26.40 as 26.40 rather than 26.00.
Original PR description
Description of the issue this commit addresses: The German tax report XML casts Kz83 to an integer before formatting it. This truncates decimal values, causing amounts such as 26.40 to become 26.00. --- Desired behavior after this commit is merged: This commit preserves the Kz83 decimal value and formats it with two decimal places in the German tax report XML. --- task-6414439 Forward-Port-Of: odoo/enterprise#125607
Appointment closing days are now refreshed immediately after being added, so teams can see schedule changes right away. The add closing day option is also limited to the appropriate appointment views and leave types, reducing confusion and preventing incorrect entries.
Original PR description
Fix some issues with the closing day feature rendering: - The closing day is not appearing in the gantt view after being created using the gantt "Add closing day" button. Re-fetching the gantt data after the closing day record creation to make sure the view is up-to-date. - The "Add closing day" button is visible from the calendar app but it should only be visible from appointment. As the calendar controller view is inherited in extension, the button was visible both from calendar and from appointment. Only displaying the button if we're in the appointment views. - In the appointment gantt, calendar and list views, making sure the "Add closing day" button only allows creating a leave of the same type as the currently opened views. In other word, hide the leave type 'resources' in the 'users' based views and the other way around. Task-6426018 Forward-Port-Of: odoo/enterprise#125854
Chilean electronic invoices that mention a foreign currency on invoice lines can now be imported even when the optional foreign-currency total is absent. This prevents mail-server invoice imports from failing and uses the standard total as a safe fallback.
Original PR description
When importing an incoming DTE through the fetchmail server, the total amount is read from the MntTotOtrMnda as soon as a Moneda node is present in the document. Steps to reproduce: - Set up a CL company with a DTE mail server - Fetch a DTE that includes the line-level Moneda node but does not include the header OtraMoneda block, so no MntTotOtrMnda - Run the fetchmail cron and check the logs Issue: The DTE fails to import Analysis: Occurs since https://github.com/odoo-dev/enterprise/commit/5805a92f91411846fdffa245cb047397cfc9b1f3 Moneda is defined at line level while MntTotOtrMnda in the optional header block Encabezado/OtraMoneda. Instead of assuming MntTotOtrMnda is always present whenever the document carries a foreign currency, fall back to the base-currency total MntTotal when it is missing. opw-6432612 Forward-Port-Of: odoo/enterprise#128766 Forward-Port-Of: odoo/enterprise#126869
UPS return shipments now include the commercial invoice in the delivery chatter, matching the behavior of outbound international shipments. US ZIP+4 postal codes are cleaned before being sent to UPS, preventing valid deliveries from being rejected.
Original PR description
Issues ----- 1. Commercial invoice is not forwarded to the user for the return delivery. 2. US postal codes of format 12345-6789 cause the delivery to be rejected. ----- Steps to reproduce issue 1…
Issues ----- 1. Commercial invoice is not forwarded to the user for the return delivery. 2. US postal codes of format 12345-6789 cause the delivery to be rejected. ----- Steps to reproduce issue 1 ----- - Set up UPS with return labels - Create an INTL delivery & confirm > OUT delivery has a commercial invoice in chatter, but the return doesn't Cause ----- The OUT and return call are not made using the same function. The OUT call is made via `ups_rest_send_shipping` which explicitly extracts the commercial invoice from the UPS response https://github.com/odoo/enterprise/blob/1a7c8ac34348ebc1ebe2da4100bdaec57484056f/delivery_ups_rest/models/delivery_ups.py#L204-L205 We should adapt `ups_rest_get_return_label` to match. ----- Steps to reproduce issue 2 ----- - Set up UPS - Create an american customer with a 9 digit zip (eg 20500-0003) - Create an delivery to the customer & confirm > Error: Invalid sold to postal code. Valid length is 0 to 9 alphanumeric Cause ----- The zip code is transmitted as-is, so we should sanitise it beforehand. https://github.com/odoo/enterprise/blob/c8c2f13b7fd17e215044fc62774f2b4a378aaf8c/delivery_ups_rest/models/ups_request.py#L368 Doc: https://github.com/UPS-API/api-documentation/blob/69e8a3cee7f9d3bf80735ae329aed0d8be156f97/Shipping.yaml#L5410-L5420 ----- Ticket: opw-6422500 Forward-Port-Of: odoo/enterprise#127375
Employees and managers can now mark multiple appraisals as done from the list view without encountering an error. The completion notification is now handled separately for each appraisal, making the batch action reliable.
Original PR description
Steps to reproduce: - select multiple appraisals and try to mark as done from list view. Issue: - The completion notification uses an appraisal variable assigned by a previous loop, raising an UnboundLocalError. Furthermore, message_notify() requires a singleton. Fix: - notify and post the completion message for each appraisal explicitly. task-6479018 Forward-Port-Of: odoo/enterprise#128207
This fixes an issue where companies that cannot receive Peppol invoices through Documents could lose their required incoming invoice journal setting. Incoming Peppol documents for affected companies, such as French companies using electronic invoicing rules, are now handled as vendor bills instead of being incorrectly stored in Documents.
Original PR description
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal…
Peppol documents can be received in a journal or in the Documents app (peppol_reception_mode). Some companies cannot use Documents: _peppol_allows_document_reception() returns False and the journal stays required. This is the case of French companies (via l10n_fr_pdp). The onchange of the settings and the import did not check this method. So on a French company with the mode set to 'documents': - the Settings cleared the journal on each opening, while it was still required - the incoming documents were saved in Documents instead of vendor bills Steps to reproduce: - Create a Belgian company, with a purchase journal, and register it on Peppol as receiver - Set the reception mode to "Receive in Documents" - Change the fiscal position to France, and install l10n_fr_pdp, the Peppol part is replaced by "French Electronic Invoicing", so the radio button is not visible anymore, but the company still has peppol_reception_mode == 'documents'. - Open the Settings again: the field "Incoming Invoices Journal" is empty. opw-6429691 Forward-Port-Of: odoo/enterprise#128441 Forward-Port-Of: odoo/enterprise#126462
Rental orders will now appear in Intrastat reports only when their duration is at least two years. This prevents short-term rentals from being reported incorrectly, improving compliance and report accuracy.
Original PR description
Problem: Some rental orders are showing in Intrastat reports when they should not be showing. Only rental orders with duration of 2 years or more should be shown in Intrastat reports. However, all rental orders are being shown. <img width="783" height="768" alt="intrastat_leasing" src="https://github.com/user-attachments/assets/7419e3dc-7b3e-4234-809f-6973fef93fc1" /> Cause: When querying the lines to show in the Intrastat report, there is no condition that checks for the duration of rental orders. opw-6351456 Forward-Port-Of: odoo/enterprise#125042
Partial receipts processed in the Barcode app no longer remove pending operation-level quality checks when users return to the transfer. This keeps required quality controls in place until the whole receipt is properly completed or cancelled, reducing the risk of missed inspections.
Original PR description
Steps to reproduce --- 1. Create a quality control point on the Receipts operation type with Control per set to Operation. 2. Confirm a receipt of 2 units of the product: one pending operation…
Steps to reproduce --- 1. Create a quality control point on the Receipts operation type with Control per set to Operation. 2. Confirm a receipt of 2 units of the product: one pending operation quality check is created. 3. In the Barcode app, receive 1 unit and go back to the transfer with the back button. 4. The pending operation quality check is gone. Issue --- Going back from the Barcode app calls `post_barcode_process`, which on a partial reception splits the picked move into a done move and a remaining move, then merges the transient duplicate back with `_merge_moves`. https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/stock_barcode/models/stock_move.py#L57-L60 `_merge_moves` cancels that transient duplicate through `_action_cancel` before unlinking it. https://github.com/odoo/odoo/blob/8f3100ca597559945cc42d9ef9517edbb40a900b/addons/stock/models/stock_move.py#L1400-L1401 The `quality_control` override of `_action_cancel`, picks the pending checks to drop from `is_product_canceled`, a `defaultdict(lambda: True)` keyed by `(picking, product_id)`. An operation check has no `product_id`, so its key is never computed by the loop and reads back the `True` default, so it is deleted even though the transfer still has a live move. Since an operation check covers the whole transfer, it must be dropped only when every move of its picking is cancelled. https://github.com/odoo/enterprise/blob/b89614661682ecbd131d940539aac8afdd9d7289/quality_control/models/stock_move.py#L68-L76 opw-6439179 Forward-Port-Of: odoo/enterprise#129051 Forward-Port-Of: odoo/enterprise#127427
Euro payments sent from bank journals in another currency can now be marked with the required SEPA values when the new setting is enabled. This helps businesses generate compliant payment files for SEPA-zone transfers without changing the journal currency, while leaving existing behavior unchanged by default.
Original PR description
Steps to reproduce: - Configure a bank journal whose currency isn't EUR (e.g. SEK, USD, GBP). - Use the generic ISO20022 payment method to send a payment in EUR to a SEPA-zone IBAN. - Generate the…
Steps to reproduce: - Configure a bank journal whose currency isn't EUR (e.g. SEK, USD, GBP). - Use the generic ISO20022 payment method to send a payment in EUR to a SEPA-zone IBAN. - Generate the pain.001 file: SvcLvl/Cd is NURG and ChrgBr is SHAR instead of the SEPA-mandated SEPA/SLEV. Cause of the issue: SvcLvl/Cd and ChrgBr are derived purely from the technical payment method code, not from whether the transaction actually qualifies as SEPA. The 'sepa_ct' payment method (which hardcodes SvcLvl=SEPA and ChrgBr=SLEV) is only ever offered on journals whose own currency is EUR. A journal in any other currency that occasionally sends a EUR payment therefore always falls back to the generic 'iso20022' payment method, which unconditionally reports NURG/SHAR. The same gap already exists, and is already solved, for Switzerland via the 'iso20022_ch_force_sepa' parameter, which dynamically remaps 'iso20022_ch' batches to 'sepa_ct' when their currency is EUR. No equivalent existed for any other country. Solution: Generalize that mechanism with a new opt-in parameter, 'account_iso20022.force_sepa_for_eur'. When set, a EUR-denominated batch generated through the generic 'iso20022' payment method is remapped to 'sepa_ct' for XML-generation purposes, so it correctly reports SvcLvl=SEPA and ChrgBr=SLEV. The parameter defaults to disabled, so the default behavior is unaffected unless explicitly turned on. opw-6006230 Forward-Port-Of: odoo/enterprise#127589
This fix allows users to enter and compare budget amounts on the Moroccan profit and loss report. It ensures the correct report column is used for budget comparisons and prevents entered budget values from disappearing.
Original PR description
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons: - The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of…
Before this commit, it was impossible to use budget on the Moroccan P&L, for the following reasons:
- The feature was designed for one-column reports. MA's P&L uses 3, one of which is the total of the two others.
=> We remove that requirement, and make sure to always select the 'balance' column as the reference for the budget comparison.
- When trying to input a budget amount in the report, the amount disappeared entirely.
=> This was because the total column of report was not using 'balance' as its expression label. We fix that by rewriting the expression labels of that report.
The fact we hardcode the use of 'balance' is arguable. It is however not possible here to rely on some custom handler to change a specific option key that would be used to generate the budget comparison data, since some of those data need to be generated in the get_options, before _custom_options_initializer even gets called. This is the simplest approach, and this case is rare enough for us to deem it acceptable.
opw-6385229
Forward-Port-Of: odoo/enterprise#129003
Forward-Port-Of: odoo/enterprise#128266Financial report snapshots are now paused when an open-ended fiscal or tax lock exception keeps a period editable. This prevents users from seeing outdated report amounts and removes snapshots that may have been created during the exception.
Original PR description
An open-ended fiscal or tax lock exception keeps the period editable, but snapshot generation did not consider it and could serve stale amounts. Prevent snapshots while a full exception is active and clear snapshots created during it. opw-6427776 Forward-Port-Of: odoo/enterprise#127525
Odoo now recognizes more modern search and AI crawlers so they can reach the correct website pages instead of getting stuck in repeated language redirects. This helps improve page inspection and indexing reliability while leaving normal visitor language behavior unchanged.
Original PR description
Modern crawlers now send an `Accept-Language` header (for example, `en-US,en;q=0.9`), whereas historically they did not. When that language differs from the website's default language,…
Modern crawlers now send an `Accept-Language` header (for example, `en-US,en;q=0.9`), whereas historically they did not. When that language differs from the website's default language, `ir.http._match()` issues a 303 redirect from `/page` to `/<lang>/page`. Since crawlers do not retain cookies, unrecognized agents are redirected on every request and never reach the default-language page. Customers reported that Google Search Console URL Inspection live tests only receive a redirect and that pages remain unindexed. Googlebot itself is not affected because it already matches the existing `bot` token. `_match()` already skips language redirects for recognized bots by serving the default-language page directly. Extend the `bots` user-agent list with modern crawler identifiers, each verified against vendor documentation: * `google-inspectiontool`: Search Console URL Inspection / Rich Results Test * `googleother`: Google generic crawler (`GoogleOther`, `GoogleOther-Image`, `GoogleOther-Video`) * `meta-external`: `meta-externalagent`, `meta-externalfetcher`, and `meta-externalads`, successors to the already-listed `facebookexternalhit` * `meta-webindexer`: Meta AI search indexer * `chatgpt-user`: OpenAI user-request fetcher (currently matched only through the `bot` substring in its info URL, which is fragile) * `claude-user`: Anthropic user-request fetcher * `perplexity-user`: Perplexity user-request fetcher The redirect behavior remains unchanged for human visitors. Localized pages continue to be crawlable through their own URLs (for example, `/fr/page`) via `hreflang` alternates. As a side effect, `link_tracker` and `mass_mailing_sms` no longer count clicks from these crawlers, and website visitor tracking skips them. task-6213245 Forward-Port-Of: odoo/odoo#275571
Duplicating project tasks, using task templates, or generating recurring tasks now preserves the correct dependency chain between sub-tasks. This prevents copied tasks from showing reversed or mismatched dependencies, helping teams keep project workflows accurate.
Original PR description
**Problem:** Duplicating a task, creating a task from a task template, or generating the next occurrence of a recurring task scrambles the dependencies between its sub-tasks: each copied sub-task…
**Problem:** Duplicating a task, creating a task from a task template, or generating the next occurrence of a recurring task scrambles the dependencies between its sub-tasks: each copied sub-task carries the dependencies of a different sub-task instead of its own. **Steps to reproduce:** 1. Enable Task Dependencies on a project. 2. Create a task with three sub-tasks and chain them: the second depends on the first, the third depends on the second. 3. Duplicate the task, or use "Create from template" if the task is a template. 4. Open the sub-tasks of the new task and look at their dependencies. **Current behavior:** The dependencies of the copied sub-tasks are shifted: the chain runs in the reverse order of the original one. **Expected behavior:** Each copied sub-task depends on the copy of the sub-task its original depended on, so the new task reproduces the original chain. **Cause of the issue:** `_create_task_mapping` builds the original to copy mapping by pairing `original_task.child_ids` with `copied_task.child_ids` positionally, on the assumption stated in its docstring that both recordsets share the same index order. They do not. `project.task._order` ends with `id desc`, so `child_ids` is read newest-first, while the copies are created by iterating the original `child_ids` in that same order. The copies' ids therefore ascend along the original list, and reading them back through `child_ids` returns them in the exact reverse order. `zip` then pairs each original with the copy of the sub-task at the mirrored position, and `_resolve_copied_dependencies` writes every `depend_on_ids` and `dependent_ids` onto the wrong copy. This affects every caller of that method: `copy`, the task template action, and the creation of the next occurrences of a recurring task. **Fix:** Sorting the copied children by id restores the correspondence because id order is the order in which the copies were created from the original list, an invariant that holds whatever `_order` does, whereas the previous code silently depended on `_order` producing the same sequence on both sides. `test_duplicate_project_with_subtask_dependencies` and `test_recurrence_copy_task_dependency` were reading the copies by `child_ids` index too, which the mirrored mapping happened to satisfy, so they passed on a wrong result; they now index them in creation order as well. opw-6386578 Forward-Port-Of: odoo/odoo#284548 Forward-Port-Of: odoo/odoo#280893
Zero-demand stock transfers are now included when calculating past forecasted inventory, preventing incorrect negative quantities from appearing historically. This helps businesses rely on more accurate stock forecasts after unplanned physical movements, though the stock report view must be updated for the fix to take effect.
Original PR description
**Problem:** When creating a transfer that moves out a product with zero demand quantity, it will change the forecasted quantity of that product in the past. **Cause:** The query filtered out the stock move with zero demand quantity, which preventing the system from accounting for unplanned physical transfers when retroactively calculating past inventory balances **Steps to reproduce the issue:** 1. Create a stock picking with 0 demand quantity that moves a product from an internal location to a virtual location or production location. 2. The forecasted quantity of the product becomes negative in the past. **Fix:** Add another check in the query to include stock moves with zero demand quantity. **Notes:** Since the forecast report is made from a SQL view, this will require a -u to update the report. opw-6462883 Forward-Port-Of: odoo/odoo#284000 Forward-Port-Of: odoo/odoo#283577
Odoo now correctly excludes temporary wizard screens from reference selections used by sales and marketing tracking. This prevents irrelevant internal options from appearing to users and keeps selections cleaner and less error-prone.
Original PR description
Various places mistakenly used `model.is_transient()` to filter the transient models, where the model is `ir.model` record itself, which always returns False since `ir.model` is a regular persistent model. As a result, transient models (wizards) were never filtered out and allowed into the `utm_reference` Reference field selection. This commit fixes it by using `self.env[model.model].is_transient()` to call `is_transient` on the actual model. Task-6458883 Forward-Port-Of: odoo/odoo#283824 Forward-Port-Of: odoo/odoo#282711
The sales flow now verifies whether a sales order requires a customer signature before allowing payment to proceed. This helps prevent orders from being paid or completed without required approval, improving compliance with business sales policies.
Original PR description
See also: - https://github.com/odoo/enterprise/pull/127041 Forward-Port-Of: odoo/odoo#283579 Forward-Port-Of: odoo/odoo#280403
Draft point-of-sale bills no longer show the QR code that lets customers create an invoice before the order is finalized. This prevents premature self-invoicing and avoids payment/order inconsistencies that could confuse staff and customers.
Original PR description
Step to reproduce: - install point_of_sale - have a pos, with `Early Receipt Printing` and `Self-service invoicing` enabled - open a pos ,select a product - from action button, click on "Bill"…
Step to reproduce: - install point_of_sale - have a pos, with `Early Receipt Printing` and `Self-service invoicing` enabled - open a pos ,select a product - from action button, click on "Bill" Observation: - We can see QR code in bill, using which a person can invoice itself, even when order is in draft state. - This cause a lot of anomoly like payment line not visible in pos order, even after successful payment Cause: - Prior to this version, `Qr` related data is shown only when `order.finalized` i.e. `status != draft` . https://github.com/odoo/odoo/blob/6f64942cbbbf2355f7328394a6d484f6828a80f1/addons/point_of_sale/static/src/app/components/receipt/order_receipt.xml#L76 - After commit https://github.com/odoo/odoo/commit/aeaca097ae39b293bff47458ae8af019585f9224 we removed this condition Fix: - The condition is brought back. opw-6427152 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284014 Forward-Port-Of: odoo/odoo#279382
This fix prevents errors when users filter sales orders using custom fields linked to project tasks. It makes sales and project reporting more reliable for teams that use related task information in their sales order views.
Original PR description
step to reproduce : 1. Create a related field on `sale.order`, for example: x_studio_production_stage = tasks_ids.stage_id.name 2. Use this field in a filter: [('x_studio_production_stage', 'ilike',…
step to reproduce :
1. Create a related field on `sale.order`, for example:
x_studio_production_stage = tasks_ids.stage_id.name
2. Use this field in a filter:
[('x_studio_production_stage', 'ilike', 'Dispatch')]
3. Applying the filter raises:
```python
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/addons/web/controllers/dataset.py", line 32, in call_kw
return call_kw(request.env[model], method, args, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 97, in call_kw
result = method(recs, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/addons/web/models/models.py", line 67, in web_search_read
records = self.search_fetch(domain, specification.keys(), offset=offset, limit=limit, order=order)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 1408, in search_fetch
query = self._search(domain, offset=offset, limit=limit, order=order or self._order)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5366, in _search
domain = domain.optimize_full(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 446, in optimize_full
return self._optimize(model, OptimizationLevel.FULL)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 460, in _optimize
previous, domain = domain, domain._optimize_step(model, next_level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 654, in _optimize_step
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 609, in _flatten
for child in children:
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 654, in <genexpr>
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 460, in _optimize
previous, domain = domain, domain._optimize_step(model, next_level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 654, in _optimize_step
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 609, in _flatten
for child in children:
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 654, in <genexpr>
children = self._flatten(child._optimize(model, level) for child in self.children)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 460, in _optimize
previous, domain = domain, domain._optimize_step(model, next_level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 962, in _optimize_step
domain = self._optimize_field_search_method(model)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 1008, in _optimize_field_search_method
computed_domain = field.determine_domain(model, operator, value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1928, in determine_domain
return determine(self.search, records, operator, value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 81, in determine
return needle(*args)
^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/addons/sale_project/models/sale_order.py", line 76, in _search_tasks_ids
query = self.env['project.task']._search(task_domain)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5355, in _search
domain = Domain(domain)
^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/19.0/odoo/orm/domains.py", line 259, in __new__
raise ValueError(f"Domain() invalid item in domain: {item!r}")
ValueError: Domain() invalid item in domain: ('id', 'any!', [('id', 'any!', <odoo.tools.query.Query object at 0x7aca184f4170>)])
```
Cause:
When searching on the related field, [_search_related()](https://github.com/odoo/odoo/blob/19.0/odoo/orm/fields.py#L768) converts the related path into an `any!` domain:
('tasks_ids', 'any!',
[('stage_id', 'any!', [('name', 'ilike', 'Dispatch')])]
)
During [Domain.optimize_full()](https://github.com/odoo/odoo/blob/19.0/odoo/orm/domains.py?utm_source=chatgpt.com#L436), [_optimize_field_search_method()](https://github.com/odoo/odoo/blob/19.0/odoo/orm/domains.py?utm_source=chatgpt.com#L1008) calls the field's search method, which invokes `_search_tasks_ids()` with `operator='any!'` and the related domain as `value`.
The existing [_search_tasks_ids()](https://github.com/odoo/odoo/blob/57c7c9938725d392a6f2cd6c89a861d2a8385c44/addons/sale_project/models/sale_order.py#L76) expects a normal search value and therefore generates an invalid nested domain.
Fix :
`_search_tasks_ids()` to directly pass the domain to `project.task._search()` when the operator is `any` or `any!`.
upg - 4584778
opw - 6475804
[here]: https://github.com/odoo/odoo/blob/19.0/odoo/orm/fields.py#L768
[here]: https://github.com/odoo/odoo/blob/19.0/odoo/orm/models.py#L5366
[here]: https://github.com/odoo/odoo/blob/19.0/odoo/orm/domains.py?utm_source=chatgpt.com#L436
[here]: https://github.com/odoo/odoo/blob/57c7c9938725d392a6f2cd6c89a861d2a8385c44/addons/sale_project/models/sale_order.py#L76
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#284101The accounting dashboard now shows the full invoice or bill amount for documents marked "To Check," instead of only the remaining unpaid balance. This avoids understating the value of documents that still need review after partial payments.
Original PR description
Currently, the "To Check" links on the dashboard display the residual amount of invoices and bills. Since the entire document needs to be checked regardless of partial payments, showing the remaining balance is misleading. This commit updates the `selects` list in `_get_to_check_payment_query` to use `amount_total` instead of `amount_residual`, ensuring the dashboard reflects the full value of the documents. Task-6478415 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284171
Customers who complete self-order purchases now receive receipt emails with the requested receipt image attached. This fixes missing receipt attachments for paid orders, improving proof-of-purchase delivery and customer communication.
Original PR description
Before this commit: ======================== * Receipt emails were sent without attachments for both paid and draft orders. * `fullTicketImage` and `basicTicketImage` were hardcoded to `false`. * As a result, paid orders were also sent without a receipt attachment. After this commit: ====================== * Receipt emails for paid orders now include the generated receipt image. * `fullTicketImage` and `basicTicketImage` are correctly handled to generate and attach the requested receipt image. Task-5353350 Forward-Port-Of: odoo/odoo#283947 Forward-Port-Of: odoo/odoo#237688
Employees who dismiss the attendance location warning will now see the pop-up close as expected. This prevents confusion when location access is blocked and users choose not to continue with check-in or check-out.
Original PR description
Steps to reproduce: -------------------------------------------- 1. Install Attendance module. 2. Enable `Device & Location Tracking` & `Attendance from Backend` in settings. 3. Block location access…
Steps to reproduce: -------------------------------------------- 1. Install Attendance module. 2. Enable `Device & Location Tracking` & `Attendance from Backend` in settings. 3. Block location access from the browser for this site (Site settings) 4. Try to checkIn/checkOut from the Dot in the systray 5. We'll have one confirmation pop-up asking to Proceed Anyway OR Discard Observation: -------------------------------------------- On clicking the discard button, Nothing happens. Issue: -------------------------------------------- In `confirmChecking()`, the `cancel` callback was defined as an arrow function using an expression body. In JavaScript, an assignment expression returns the assigned value. Since `this._attendanceInProgress` is set to `false`, the callback implicitly returns `false`. `ConfirmationDialog.execButton()` treats a `false` return value as a signal to keep the dialog open (used intentionally to block closing on validation failure) This caused the dialog to never call `this.props.close()`, leaving it permanently open when Discard was clicked. https://github.com/odoo/odoo/blob/5e84fdd99e34836a15cadc4fdf4b6bc449727e58/addons/web/static/src/core/confirmation_dialog/confirmation_dialog.js#L75-L89 Solution: -------------------------------------------- Change the `cancel` callback from an expression body to a block body, A block body arrow function returns `undefined` by default. This ensures `execButton` does not interpret the return value as a 'keep dialog open' signal, and correctly calls `this.props.close()` to dismiss the dialog. opw-6462439 Forward-Port-Of: odoo/odoo#284093 Forward-Port-Of: odoo/odoo#281702
This fixes signup link generation so the required signup purpose is always provided when creating access tokens. It helps prevent invite or portal access flows from failing when users need to sign up or access shared project content.
Original PR description
A `signup_type` is required to generate a token. Task-6452339 Forward-Port-Of: odoo/odoo#283417 Forward-Port-Of: odoo/odoo#280891
Deleting a draft invoice for timesheet-based services no longer changes which sales order line the timesheet hours belong to. This prevents sold hours from disappearing from the original order or being moved to another order when an invoice is removed and recreated.
Original PR description
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the…
Deleting a draft customer invoice linked to timesheets resets their timesheet_invoice_id so the hours become invoiceable again. This write also marks the timesheets' so_line for recompute, and the re-derivation runs while the lines are no longer protected by the invoice link. When the task or project no longer resolves to a sale order item (e.g. it was unlinked after invoicing), the timesheets lose their sale order item or get reassigned to another one, so the delivered hours silently disappear from the original order line. Protect so_line during the write and drop the pending recompute: deleting an invoice must only make the hours invoiceable again, not change their allocation. Steps to reproduce: - Install Sales and Timesheets - Create a service product with invoice policy "Based on Timesheets" and "Create a task in a new project" - Create and confirm a sale order with this product - Log a timesheet on the generated task - Create the invoice (keep it in draft) - Remove the Sales Order Item from the task and from the project settings (or point them to a sale order item of another order) - Delete the draft invoice - Open the timesheet: its Sales Order Item is emptied (or replaced by the other order's item, whose delivered quantity now includes the hours sold on the original order), and the original line's delivered quantity is reset --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283831 Forward-Port-Of: odoo/odoo#279552
API documentation pages now display bullet points and lists with proper styling. This makes generated documentation easier to read and helps users understand reference information more clearly.
Original PR description
Bullet points and lists coming from the generated html by docutils were not properly styled. This commit fixes those cases. task-6484990 Forward-Port-Of: odoo/odoo#283835
This fixes invoice tax calculations when one tax increases the base amount used by a following tax on the same line. Businesses get more accurate tax breakdowns and totals in accounting documents, reducing reporting and reconciliation errors.
Original PR description
**Steps to reproduce:** - Create a tax that affects the base of the subsequent ones - Create an invoice with this tax and another one on the same line **Issue:** In "_aggregate_base_line_tax_details", the tax amount from the first tax should be included in the following values of the second tax: - raw_total_excluded - raw_total_excluded_currency - target_total_excluded - target_total_excluded_currency - total_excluded - total_excluded_currency But it is not. opw-6235909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284066 Forward-Port-Of: odoo/odoo#279335
This change updates internal subscription-related tests so they stay aligned with recent changes in the related Odoo codebase. It helps maintain confidence that subscription flows continue to work as expected, without changing customer-facing functionality.
Original PR description
See also: - https://github.com/odoo/odoo/pull/280403 Forward-Port-Of: odoo/enterprise#128610 Forward-Port-Of: odoo/enterprise#127041
This fixes an issue where social media users saw an access error when liking a stream post. Likes are now processed safely in the background, improving the experience for users managing social streams.
Original PR description
Bug === When a social user like a stream post, an access error is raised because he has no write access on it. Task-6425391 Forward-Port-Of: odoo/enterprise#128993 Forward-Port-Of: odoo/enterprise#125973
The timesheet timer now excludes archived projects from its project dropdown, even when those projects were used in past timesheets. This prevents users from accidentally selecting inactive projects and keeps time tracking choices aligned with current project availability.
Original PR description
Steps to reproduce: ---------------------------------- 1. Install the `timesheet_grid` module. 2. Create a project and add any timesheet to it. 3. Archive the project. 5. From the systray timer,…
Steps to reproduce:
----------------------------------
1. Install the `timesheet_grid` module.
2. Create a project and add any timesheet to it.
3. Archive the project.
5. From the systray timer, click on the Project field.
Observation:
----------------------------------
The archived project is visible in the dropdown.
Issue:
----------------------------------
In Odoo, standard search views and `name_search` calls on `project.project` automatically respect `active_test=True`. When you open the timer, the frontend passes `{'timesheet_timer_search': True}` in the context to `name_search` with an empty query string. `name_search` overrides standard searching to retrieve recently used projects first by querying `account.analytic.line` via `_get_recently_used_records ('project_id', ...)`. `account.analytic.line` stores past timesheet logs. Even after a project is archived, historical timesheet records for that project still exist in `account.analytic.line`. Because `_get_recently_used_records` runs a `_read_group` query on `account.analytic.line` (which has no active field of its own), it fetched the `project_id` from historical timesheet entries without checking if the referenced project was active.
Solution:
----------------------------------
In `name_search`, explicitly append `[('active', '=', True)]` to the `project_domain` used when querying `_get_recently_used_records`. Standard form/list views using `_domain_project_id` already benefit from Odoo's default ORM `active_test=True` mechanism during standard `project.project` searches.
Note:
----------------------------------
Another solution was to add `active = true` in `getTimesheetTimerFieldInfo` https://github.com/odoo/enterprise/blob/22eb84cdc94ba334d42bad32fb491d35c8147c94/timesheet_grid/static/src/services/static_timesheet_timer_service.js#L322-L328
Fixing it in Python ensures that any call passing `timesheet_timer_search` in context (e.g. mobile widgets, custom RPCs, or python wizards) will benefit from the fix, rather than only patching a single OWL JS service.
opw-6445528
Forward-Port-Of: odoo/enterprise#127374A payroll-related test now uses the correct wage value depending on how employee pay is stored. This helps prevent false test failures and keeps pay gap reporting checks reliable across payroll configurations.
Original PR description
Without `hr_payroll`, the contract wage is stored in `wage`. With `hr_payroll`, hourly employees use `hourly_wage` instead. This commit uses `_get_contract_wage_field()` so the test sets the correct field in both cases. [error-237750](https://runbot.odoo.com/odoo/error/237750) Forward-Port-Of: odoo/enterprise#127398
This fix ensures timer and timesheet screens react correctly after an underlying framework change. Users should see active timers and timesheet status restored reliably instead of intervals refreshing unnecessarily or active timesheets being missed.
Original PR description
OWL3's useEffect takes one argument, so the OWL2 deps callback is dropped: timer_start_field re-arms its interval on every render instead of on a timer_start it compares by value, and timesheet_systray never binds its `loaded` parameter, so it never restores the active timesheet. Came in with odoo/enterprise#128151 and odoo/enterprise#125368. Effects kept: they arm an interval and call into the timer service, not a derivation, so useOnChange restores both declared dependency lists verbatim. see https://odoo.github.io/owl/documentation/v3/owl/reference/hooks.html#useeffect community: https://github.com/odoo/odoo/pull/283883 Forward-Port-Of: odoo/enterprise#128751
This update prevents an error during accounting reconciliation when users work with a parent company and branch company at the same time. It ensures the currency conversion uses the correct company context, allowing journal items across selected companies to be reconciled smoothly.
Original PR description
When having multiple companies selected at the same time, _get_conversion_rate returns: File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in get raise ValueError("Expected singleton: %s" %…
When having multiple companies selected at the same time, _get_conversion_rate returns:
File "/data/build/odoo/odoo/orm/fields_misc.py", line 114, in get
raise ValueError("Expected singleton: %s" % record)
1 - Create a new company with currency EUR.
2 - Create a branch company underneath the main company.
3 - In Accounting, install fiscal localization, e.g. Belgian Companies on the company configuration settings.
4 - Select an account like 600000 Raw Materials, and enable Allow Reconciliation on this account. The exact account isn't important, only that we can make credits / debits to it to be reconciled.
5 - With only the top level company selected, make a debit of 100 USD, e.g. Vendor Bill, set in currency USD to the account 600000.
6 - Now with only the branch level company selected, make a credit of 100EUR, e.g. Customers Invoices, set in currency EUR to the same account with an amount equal to the credit in step 5. (if 1USD == 1EUR, 1-1), so that there is no residual amount, i.e. credit == debit.
7 - Now select both the top level company and the sub branch company in the company context.
8 - In Journal Items, reconcile the unreconciled journal items for the Account 600000.
With this commit we select the first company of the aml instead of every companies on the amls.
opw-6290703
Forward-Port-Of: odoo/enterprise#123774This fixes an issue where a user who was explicitly added as an editor on a Documents folder could not update access rights for internal users. It ensures folder editors can manage the sharing permissions they are allowed to edit, reducing blocked collaboration workflows.
Original PR description
1. Create a non-company root folder 2. Edit rights as follows: * add Marc Demo as editor member * access for internal users and link to None 3. As Marc Demo, try updating Internal users access to "editor" ⮕ You can't. Task-6410610 Forward-Port-Of: odoo/enterprise#129054 Forward-Port-Of: odoo/enterprise#125191
Shopee shops can now be reauthorized with a different account, and Odoo will correctly update the shop connection. This prevents errors when businesses change API credentials or reconnect a shop under another Shopee account.
Original PR description
Context: when a user re-authenticate a shop, they might use different shopee.account (API key). Currently Odoo will not change the shopee.account when they re-auth with another shopee.account. Enable a shopee.shop switches to another shopee.account when we run `create_or_update_shop` function. Forward-Port-Of: odoo/enterprise#129143 Forward-Port-Of: odoo/enterprise#92446
Corrects a rounding mismatch in Peruvian electronic invoice XML that could cause invoices, especially down payment invoices, to be rejected by the tax validation service. This helps ensure taxable amounts match line totals and improves successful submission of Peruvian UBL 2.1 documents.
Original PR description
**Steps to reproduce:** - Install Accounting, Sales and l10n_pe_edi - Switch to a Peruvian company (e.g. PE Company) - Create a SO: * Customer: [a Peruvian customer] * Order Lines: | Product |…
**Steps to reproduce:**
- Install Accounting, Sales and l10n_pe_edi
- Switch to a Peruvian company (e.g. PE Company)
- Create a SO:
* Customer: [a Peruvian customer]
* Order Lines:
| Product | Quantity | Unit Price | Taxes |
| ------- | -------- | ---------- | ------- |
| any | 3.00 | 123.50 | VAT 18% |
| any | 2.00 | 27.544216 | 0% Ina |
| any | 1.00 | 43.490867 | 0% Exo |
- Confirm the SO
- Create a 40% down payment
- Confirm the down payment
- Process it to sent it to Peru UBL 2.1
**Issue:**
The following error message is returned by the OSE:
`3272|La base imponible a nivel de línea difiere de lainformación consignada en el comprobante - Detalle: xxx.xxx.xxx ticket : 20260000000000221633458 error: Error en la Linea Nro. :1. : 3272 (nodo: "cac:TaxSubtotal/cbc:TaxableAmount" valor: "148.20")`
**Cause:**
In the XML, one line has 148.19 for "cbc:LineExtensionAmount", but 148.20 for "cac:TaxSubtotal/cbc:TaxableAmount".
The issue is coming from the fact that "base_amount_currency" is used instead of "total_excluded_currency" for the computation of "cac:TaxSubtotal/cbc:TaxableAmount".
**Issue 2:**
When a tax is impacting the base amount of a following tax, its tax amount is not taken into account in "total_excluded_currency".
opw-6235909
Forward-Port-Of: odoo/enterprise#128886
Forward-Port-Of: odoo/enterprise#122310Users can now decline a signature request with a reason without triggering an error screen. The signing workflow now closes the decline window before showing the confirmation message, making the process smoother and more reliable.
Original PR description
Version: 19.4 Steps to reproduce: - Create a sign request with a signature and send it to a user - Decline the document as administrator with a reason Issue: Opening the thank you dialog before closing the decline dialog caused both actions to be processed together. This made the thank you dialog get built twice and both attempts were already destroyed before orm.call, raising a traceback. Fix: Close the decline signature dialog first, then open the thank you dialog. Task id - 6471923 Forward-Port-Of: odoo/enterprise#128345
This fixes seven mislabeled entries in the Mexican chart of accounts so their names match the official SAT catalogue. The correction helps ensure electronic accounting exports show the proper account descriptions, reducing confusion and compliance risk for Mexican companies.
Original PR description
Seven entries of the Mexican chart of accounts template carry a name belonging to a **different** group, copied from a neighbouring entry. Each record's XML ID still states the intended name, which…
Seven entries of the Mexican chart of accounts template carry a name belonging
to a **different** group, copied from a neighbouring entry. Each record's XML ID
still states the intended name, which is what this restores.
| Code | Field | Before | After |
|---|---|---|---|
| `6` | `name@es` | Gastos generales | Gastos |
| `252.07` | `name@es` | `account_subgroup_hipotecas_por_pagar_a_largo_plazo_nacional` | Hipotecas por pagar a largo plazo nacional |
| `602` | `name`, `name@es` | Cost of sales / Costo de venta | Selling expenses / Gastos de venta |
| `613` | `name@es` | Amortización contable | Depreciación contable |
| `614` | `name` | Accounting depreciation | Accounting amortisation |
| `701.06` | `name`, `name@es` | Interest on foreign bank charges / Intereses a cargo bancario extranjero | Interest payable by national natural persons / Intereses a cargo de personas físicas nacional |
| `702` | `name@es` | Utilidad cambiaria | Productos financieros |
### Why it is not cosmetic
The electronic accounting Chart of Accounts XML takes the `Desc` attribute of
every `<Ctas>` element from the *account group name* — `cfdicoa.xml`
(`t-att-Desc="account.get('name')"`), fed by `trial_balance.py`
`_l10n_mx_get_coa_values()`. Any `es_*` database therefore declares:
```xml
<catalogocuentas:Ctas CodAgrup="702" NumCta="702" Desc="Utilidad cambiaria" Nivel="1" Natur="A"/>
```
whereas the SAT catalogue (Anexo 24) publishes `702` as *Productos financieros*,
with `702.01 Utilidad cambiaria` … `702.10 Otros productos financieros` beneath
it. `CodAgrup` comes from `code_prefix_start` and stays correct, so the file
still validates against the XSD, but the declared description does not match the
official nomenclature. Trial Balance and Pólizas are unaffected — neither
exports group names.
### Evidence
- `252.07` contains its own XML ID as the Spanish name.
- `602` duplicates `501.01`, yet its children are `Sueldos y Salarios`,
`Compensaciones`, `Tiempos extras`.
- `613` and `614` are swapped in one language each: `613`'s children are
depreciations, `614`'s are amortisations.
- `701.06` duplicates `701.05` in both languages; the correct name is symmetric
to `701.07` and to `702.06`.
- `6` is the only single-digit root group whose Spanish name does not match its
XML ID (`account_group_gastos`).
### Notes
Introduced in d782b8b92557; correct in 15.0, where the names lived in
`account.account.tag.csv`. Still present in 18.0, 19.0 and master, hence
targeting 17.0. Template data only — existing databases are unaffected until the
chart is (re)installed, and renaming a group moves no balance.
Forward-Port-Of: odoo/odoo#277426
Forward-Port-Of: odoo/odoo#278328
Forward-Port-Of: odoo/odoo#277891The Belgian salary configurator now handles cases where no company bike is available. This prevents an error when users select the company bike option and keeps the offer setup process running smoothly.
Original PR description
Steps to Reproduce: - install l10n_be_hr_contract_salary module. - make sure that there is no model with vehicle type bike in fleet. - create an offer in recruitment. - open salary configurator. - click on company bike checkbox. Issue: - traceback occurs when enabling the company bike option without a configured bike. Reason: - the company bike depreciated cost value is empty when no bike is available, but the code tries to split it into bike options and vehicle ID resulting in a traceback. Solution: - Use the condition to check if the company bike depreciated cost is available before spliting the value. - Set the depreciated cost to 0 when no bike is selected. task-6468987 Forward-Port-Of: odoo/enterprise#127898
Cancelling and resetting a payslip now correctly returns related time off to be included in payroll calculations. This prevents approved leave from being missed when payroll teams revise payslips for the same period.
Original PR description
How to reproduce: - Create a payslip for an employee and validate it - Create a new time off for said employee during the same period as the payslip and validate it - Go back to the payslip, cancel it and reset it to draft - The new time off is not included in the payslip Reason: When a payslip is cancelled, if there are time off during the same period as the payslip, their state is not reset to "to compute in next payslip" and instead stays in "to defer to next payslip", causing the issue How it was fixed: Now, when a payslip is cancelled, the new function "return_time_off_to_normal" will catch all leaves that are in the same time frame as the payslip to reset their state to "to compute in next payslip". Task ID: 6431576 Forward-Port-Of: odoo/enterprise#128958 Forward-Port-Of: odoo/enterprise#126868