Thursday, August 27, 2026
90 changes · master
Enhancements to existing features
New Google fonts added to a website are now hosted locally instead of being served by Google. This improves page performance scores and helps reduce privacy and GDPR compliance concerns, while also improving how fonts load for visitors.
Original PR description
Currently, Google fonts are served by Google, which lowers the Lighthouse score and may impact GDPR compliance. Solution: By default, when a user add a new Google font, it is hosted locally. Task-6009931
The calendar attendee invitation flow is now simpler and easier to use, with clearer labels, better field ordering, and a smaller dialog. Users can also add attendees more smoothly from quick event creation, including by entering an email address for a new contact.
Original PR description
# Purpose Simplify the attendee invitation wizard a bit & make the attendee pill display in the quickcreates clickable # Specs Event form: - Align invitation buttons with guest count - Clearer attendee placeholder Invitation wizard: - Reorder fields - Move default focus to email - Remove contact image Quickcreate event: - Display attendee pill - Opens invitation wizard for new partners --------------------------------- Task-6209447
Website managers can now edit common HTTP error pages directly in the website builder, including changing layout, content, and whether the default error message appears. The update also improves how website content routes are listed, helping modules expose dynamic pages more clearly for discovery and editing.
Original PR description
This PR improves website content discoverability and website customisation by: - Extending list_as_website_content to support dynamic, structured routes. - Making HTTP error pages editable through…
This PR improves website content discoverability and website customisation by: - Extending list_as_website_content to support dynamic, structured routes. - Making HTTP error pages editable through the website builder. # Dynamic structured routes for `list_as_website_content` Previously, `list_as_website_content` only supported returning a static title, with route URLs inferred automatically from the endpoint. This PR extends the API to also accept callables returning structured route definitions (`route_title` and `route_url`), enabling explicit and dynamic website content entries. This provides greater flexibility for modules exposing multiple or computed website routes. # Editable HTTP error pages HTTP error pages (400, 403, 404, 415, and 422) are now editable from the website builder via: `/website/http_error/<error_code>` ### Users can: - Fully customise error pages using snippets and inner blocks. - Toggle the visibility of the default error message. - Preview and edit pages using a demo error card in edit mode instead of triggering a real HTTP error. task-4714826 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Odoo now lets users and business workflows filter uploaded binary files by their stored filename or file size. This preserves file-name based filtering even as separate filename fields are replaced by the binary field itself.
Original PR description
Add `filename` and `size` as properties that can be used when searching binary fields. Since file name fields can be replaced by just using the binary field, we provide this to be able to search by the name of the file.
`[('bin_field.filename', 'ilike' 'test')]` searches for binary values with file name containing "test".
----
Since we remove the fields (https://github.com/odoo/odoo/pull/284302) and may want to continue support filtering on the file name.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThe employee records timeline now displays contract periods more clearly and avoids confusing visual states. This makes it easier for users to understand which version is selected and how contract versions connect over time.
Original PR description
Adapting the misc widgets to the new design versions_timeline.scss as unused styling, but that file owned the timeline's own geometry, and the new markup kept the item wrapper class the statusbar had…
Adapting the misc widgets to the new design versions_timeline.scss as unused styling, but that file owned the timeline's own geometry, and the new markup kept the item wrapper class the statusbar had renamed in the meantime The employee records bar ended up with one contract line per version, drawn inside the bar, instead of a single line spanning the versions of a contract; with a selected version painted in the very grey the statusbar paints on hover, so a hovered version looked selected; and with items never folding into the dropdowns, since the statusbar looks them up by ".o_arrow_button_wrap": the bar wrapped onto a second row and showed a stray duplicate of the selected version. Give the bar a stylesheet again, scoped to a class shared with the other versioned records bars: only the displayed version is highlighted, and the contract line bridges the gap between two versions of the same contract so it reads as one line under the bar. task-6510183 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
After installing VoIP, users are now taken directly to the phone number list so they can continue setup more easily. The update also clarifies that phone numbers still being processed will refresh automatically, reducing confusion during provisioning.
Saudi payroll now lets companies choose how service duration is calculated for end-of-service benefits: 360 days, 365 days, or actual days in the year. This supports different company policies and improves payroll configuration accuracy for Saudi employees.
Original PR description
Replace the Saudi EOS actual-days boolean with a selection allowing companies to compute service duration using 360 days, 365 days, or the actual number of days in the year. Updated the employee payroll view and EOS tests accordingly. task-6413322
Businesses can now set a maximum bookable capacity for each appointment time slot, helping avoid too many reservations arriving at the same time. This is useful for capacity-sensitive services such as restaurants, and the limit is also reflected in Google Reserve availability.
Original PR description
Allow businesses to configure a max bookable capacity, applied for every slot computed from availabilities. This prevents accepting too many reservations at once and allows some pacing. This is…
Allow businesses to configure a max bookable capacity, applied for every slot computed from availabilities. This prevents accepting too many reservations at once and allows some pacing. This is different from the availability of resources, and is for instance designed for restaurants to avoid clogging the kitchen. The feature is also supported in appointment_google_reserve, and code is adapted to make sure we compute this availability even when selecting manually a given resource (e.g. for resource_time) The heuristic is the following: --- 0. Only for resources, and when managing capacity, for a recurring appointment... 1. For every appointment.slot... 2. 'Subslots' will be created, as usual, every slot_creation_interval. We are using this as the duration of the interval over which we limit the bookings to slot_interval_capacity. The last subslot will also use this value, no matter the appointment_duration. 3. To count how much capacity is currently used on a subslot, we count reserved capacity on booking lines of meetings STARTING in the interval [start, start + slot_creation_interval[. We include the start, as this is the most common use case when booking and matches booked time on the front-end, and exclude the ending as next subslot would start there. Example --- Let us have an appointment configured as follows to illustrate: - slot_interval_capacity = 10 - slot_creation_interval = 15 min - appointment_duration = 1h - 1 appointment.slot: 19.00 to 20.30 - 22 total capacity available on available resources -> 'subslots' are 15 min long and created at 19.00 19.15 19.30 Now, let's add bookings one by one: - From the backend, 2 people at 19.25 - 6 people at 19.00 - 8 people at 19.00. Not enough capacity. They book at 19.15. The subslot [19.15, 19.30[ is now full, as the 19.25 booking also counts. - 4 people at 19.00. Subslot [19.00, 19.15[ is now full. - 4 people. They cannot book because total capacity is already used up to 20/22. They can only book for two, and end up booking the 19.30 subslot. While that subslot is not full in the sense of slot_interval_capacity, the day is now full. Algorithm --- In order to lower the time complexity of the check on booking lines, we implement a sliding window of subslots over booking lines grouped by start. This should end up in O(nlogn) (due to sorting, see below) instead of O(n^2). As this only works with ordered data, we order booking line data (after grouping, more efficient) and slots (only if the computation is needed, a few explicit checks are made for this) in an ascending order of start datetime. Test --- A test is added for both appointment and appointment_google_reserve. In the appointment test, we also include a complex configuration as we want the sliding window algorithm to be robust and work in any configuration. Other change --- To complete this new feature, we enable the total row in the gantt view for appointments. We use the total_capacity_reserved instead of the usual count. We only compute the total row(s) when on a single appointment to avoid clash of definition when manage_capacity is true / false. Task-6191497
Category header display settings are now managed at the website level instead of separately on each product category. This helps keep the shop experience consistent across all categories on the same website and reduces configuration confusion.
Original PR description
Before this commit, category header settings were stored on each product category, leading to inconsistent behavior across categories within the same website. This commit stores these settings on the website instead, making them consistent for all categories of a website. Upgrade PR:https://github.com/odoo/upgrade/pull/10604 task-6325882
Property fields can now decide individually whether their changes are recorded in the chatter. This gives businesses more control over which property updates are visible in communication history, helping reduce noise while preserving important audit trails.
Original PR description
Allow to track properties. Properties fields are all "tracked" but the property have their own tracking attribute that will define if the change are tracked or not in the chatter. TASK-5131127 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Belgian payroll now shows more complementary benefit information on payslips and improves how insurance contributions are configured and validated. This helps payroll teams reduce manual inputs, catch invalid insurance amounts earlier, and provide employees with clearer salary details.
Original PR description
Refactor existing Belgian salary rules to support the complementary information display section on payslips and enhance employee insurance configuration. - Add employee-level ambulatory insurance contribution and setting defaults. - Compute employer share for meal vouchers via new `MEAL_VOUCHER_EMPLOYER` rule. - Refactor termination rules (Hospital, Ambulatory, Group Insurance) to compute directly from contract version history instead of manual inputs. - Enable `display_in_pdf_extra_info` across relevant insurance rules. - Rename `F_SOCIAL_CONTRIB` and configure for extra info display. - Add payroll warnings to catch invalid employee insurance contributions. Task: 6327364
The accounting dashboard KPI cards have been refreshed to stay visible and easier to use. On mobile devices, the cards now appear in a horizontally scrollable row, improving access without taking up excessive screen space.
Original PR description
This commit improves the UI of the KPI cards in the account dashboard. There's no longer a close button on the cards, they're meant to always be visible on the dashboard. Also, the cards are now in a scrollable row on mobile, instead of being stacked vertically. Task ID: 6498831
Belgian payroll can now identify when an employee's salary is paid to a bank account owned or managed by someone else. This helps companies capture the required beneficiary details, especially for non-European accounts, improving compliance in exceptional payment situations.
Original PR description
Under specific circumstances, you can have your salary sent to another account, for example, you're under heavy fines and drawbacks, and your salary is managed by a legal advisor. In that case, you're not the proprietary of the account. Moreover, if you're not the proprietary and the account where money is sent is not european, we need to know the beneficiary city + country. Adding 2 new fields on `res.partner.bank` (displayed in form only for BE companies) : - `is_third_party`: computed (editable) boolean checking if `holder_name` and `partner_id.name` are matching or not. - `third_party_beneficiary_id`: res_partner managing the third party account. Task: 6431562
The quotation template form now separates subscription-specific settings into their own column. This makes the form easier to scan and helps users distinguish subscription configuration from general template details when setting up quotations.
Original PR description
Before this change: The quotation template form view displayed subscription specific fields together with the template's general fields in a single column. This made it hard to visually distinguish which fields belonged to the subscription configuration or the general template settings. After this change: The subscription fields are now separated into their own column. This groups related fields visually and makes the distinction between general template info and subscription specific settings clear. Impact: Improves the user experience by making subscription fields easier to locate and distinguish from other template fields, reducing the chance of confusion when configuring a subscription quotation template. task id: 6410262
Datetime fields can now automatically fill in a preset value when a user clicks them. This supports workflows like Social scheduling, where the planned time can default to one hour from now to speed up data entry.
Original PR description
Purpose ======= I social, we would like to automatically set the scheduled date to "now + 1 hour" when we click in the Datetime field. Task-6323897
The spreadsheet dashboard settings panel is now hidden from users who do not have administrator rights. This helps prevent non-admin users from seeing configuration options they cannot or should not manage, making the dashboard experience clearer and more controlled.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: task-6345154 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Businesses can now turn an active asset set to no depreciation into a depreciable asset without manually recreating it. The system transfers the current book value to the selected asset account, closes the original asset, creates and validates the new depreciable asset, and makes navigation between the linked records easier.
Original PR description
Allow users to convert a running asset that uses the "No Depreciation" method into a depreciable asset. A new "Activate Depreciation" action is added to the asset modification wizard: - A transfer journal entry moves the book value from the current fixed asset account to a newly selected one. - The old asset is closed and a new asset is created on the target account, inheriting the book value, salvage value, and the depreciation parameters configured on the new account. - The new asset is validated immediately, computing its depreciation board from the activation date. task-6357855
Project sharing now separates collaborators from message followers, so businesses can grant limited task editing access without automatically changing notification followers. This improves control over external or portal user access while keeping project communication settings cleaner.
Original PR description
#TODO --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
HR now skips an unnecessary calendar lookup when employee contract or work versions follow each other without a gap. This improves performance for related HR validations, reducing wait times and database load without changing business behavior.
Original PR description
Optimize `has_work_hours_between_versions` by adding a fast path for back-to-back versions. If a new version starts the exact day after the previous one ends, there is no time gap between them (the old version ends at midnight and the new one begins immediately). In this scenario, we can safely return `False` and bypass the expensive calendar lookup entirely. `._get_l10n_be_min_wage_invalid_employees` on next.odoo.com: | | time | SQL queries | |--------|--------|--------| | before | ~7.8s | 6744 | | after | ~2.4s | 491 | <img width="1874" height="995" alt="image" src="https://github.com/user-attachments/assets/2b61179b-10c5-46a6-a3ac-4a0cad14f6d6" /> <img width="1874" height="995" alt="image" src="https://github.com/user-attachments/assets/7a70fb39-ece9-4090-ba2e-f3c4acca2657" /> task-6472490 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282703
The timesheet ActivityWatch assistant now shows the name of the relevant Odoo record when it can identify one from a visited URL, instead of falling back to a broad app name. This makes time suggestions clearer and helps users recognize what they were working on more quickly.
Original PR description
Before this commit, when the ActivityWatch integration encountered unmatched Odoo URLs, it would fallback to displaying the general application name (e.g., "Working on Sales"). With this commit, if the URL path ends with a valid record ID (e.g., /odoo/departments/1) and the corresponding model can be identified, the assistant will attempt to fetch and display the actual record name (e.g., "Working on Research & Development"). task: 6365568 Forward-Port-Of: odoo/enterprise#127981 Forward-Port-Of: odoo/enterprise#124138
Invoice QR code settings and payment method labels now use the correct local payment scheme names, such as Pix, FPS, PayNow, PromptPay, VietQR, MMQR, KHQR, and SEPA where relevant. This makes setup clearer for businesses in each country and avoids showing SEPA-specific wording where it does not apply.
Original PR description
The QR-code setting on invoices was labelled "SEPA QR Codes" for every country, and the Payment QR-code selection offered "SEPA Credit Transfer QR" everywhere, even where SEPA does not exist. The bank account fields driving those codes were named "Proxy Type" and "Proxy Value", which said little to users. Each localization now names the setting and its QR method after the local payment scheme (Pix, FPS, QRIS, PayNow, PromptPay, VietQR, MMQR, KHQR) and offers it only to companies of that country. SEPA naming is kept for the SEPA zone, other countries get a generic "Payment QR Codes" label, and the bank fields become "Account Identifier Type" and "Identifier Value". task-6471076
Point of Sale manual data reloads now clear browser-stored local and session data in addition to the main offline database. This helps prevent outdated or mismatched information from causing inconsistent behavior after a reload.
Original PR description
Manual data reloads reset IndexDB but leave local and session storage intact. The goal is to clear them to avoid inconsistent data. task-6456447 Forward-Port-Of: odoo/odoo#284238 Forward-Port-Of: odoo/odoo#281456
Point of Sale now lets cashiers move on immediately after validating a paid order instead of waiting for receipt printing to finish. This should make checkout feel faster and reduce delays at the register, while automated checks were updated to match the new flow.
Original PR description
- Stop awaiting the receipt print in the POS after order payment validation - Adapt tours to this behavior change task-id: 6425204 enterprise PR: https://github.com/odoo/enterprise/pull/127818 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283823 Forward-Port-Of: odoo/odoo#280002
Bank journals now receive the right outstanding payment accounts automatically for Moroccan and Indian accounting setups. This helps ensure payments are reflected correctly in cash-basis tax reporting, even when bank synchronization or reconciliation is not available.
Original PR description
Reason: - Moroccan companies usually use cash basis accounting. And the cash basis entries are only made when the invoices are reconciled with the bank transactions. However, in Morocco, there is no…
Reason: - Moroccan companies usually use cash basis accounting. And the cash basis entries are only made when the invoices are reconciled with the bank transactions. However, in Morocco, there is no Moroccan bank available for bank synchronization. Before this commit: - We are not assigning the outstanding accounts on the bank journals by default. - Which is causing the issues when the user creates a payment without an entry and without having any bank transactions to reconcile it with. Therefore, the tax report won't show the moves and taxes that occurred in the period. After this commit: - Introduced a method for updating the accounts on the payment method lines of the bank journal in the account module, as we need the same functionalities in l10n_in as well. - For Moroccan localization, from now on, we are setting the outstanding accounts automatically on the bank journals. - The payment accounts are applied by default during CoA loading and whenever payment method lines are recomputed, ensuring accounts remain consistent. Task-6041119 Forward-Port-Of: odoo/odoo#254642
Module descriptions, summaries, and short labels are now exported and loaded with each individual module instead of being bundled into the core translation file. This makes translations work better for custom modules and avoids extra translation export work when modules are added or updated.
Original PR description
Currently a module's `description`, `shortdesc`, and `summary` that are defined in the manifest file are exported in the `base.pot` file. This means that in order to get them translated (in case of an update or a new module), the `base.pot` file needed to be re-exported again with all the possible modules in the addons paths. For custom modules, this also means that their manifest terms are not translated at all, because their terms are not in the `base.pot` file. This commit changes this by exporting the manifest terms in their own modules' POT file, and loading them from there as well. This way, the manifest terms of custom modules can be translated, and there is no need to re-export the `base.pot` file when a new module is added or updated. We also add a test to check that the custom reader implementation is faster than using `polib`.
Payroll users can now tap a full employee row to select it when reviewing payroll runs on mobile. Tapping the employee avatar opens the employee record, making navigation clearer and reducing selection mistakes on smaller screens.
Original PR description
this commit improves the employee selection in mobile view of hr_version_payrun_list making the click event on the record row to select the record and when the avater is clicked the user will be redirected to the employee form view. task-6469653
Resolved issues and error corrections
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
Code cleanup and technical improvements
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
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
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 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
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 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
Changing 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 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
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
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
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
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
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
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
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#268158Fixed 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
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
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
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#272549Appointment 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
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
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#284101Customers 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
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
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 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
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#122310This 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#277891Cancelling 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
Point of Sale data loading has been redesigned so stores can sync only the records that changed instead of reloading everything each time. This should improve startup speed and scalability, especially for businesses with large product, customer, loyalty, restaurant, self-order, or localization datasets.
Original PR description
pos*: l10n_ar_pos, l10n_es_edi_verifactu_pos, l10n_in_pos, l10n_pe_pos, l10n_sa_edi_pos, point_of_sale, pos_discount, pos_event, pos_glory_cash, pos_hr, pos_loyalty, pos_online_payment_self_order,…
pos*: l10n_ar_pos, l10n_es_edi_verifactu_pos, l10n_in_pos, l10n_pe_pos,
l10n_sa_edi_pos, point_of_sale, pos_discount, pos_event, pos_glory_cash,
pos_hr, pos_loyalty, pos_online_payment_self_order, pos_qfpay,
pos_restaurant, pos_sale, pos_self_order, pos_self_order_event,
pos_self_order_qfpay
Rework the POS data loading mechanism to improve scalability and support
incremental syncing based on `write_date` timestamps.
- Move field/relation metadata computation into `PosLoadMixin` via new
`_load_data_relations()` and `_load_pos_data_domain_and_dependencies()`
methods, removing the centralized logic from `pos_session.py`
- Refactor `load_data()` to accept a `local_data` dict (model → {id:
write_date}) so the frontend can request only records newer than what
is already cached in IndexedDB
- Add `_load_pos_metadata()` and `_read_pos_data_from_metadata()` to
clearly separate metadata collection from record reading
- Add `load_pos_data_force_loading()` hook for models that always require
a full reload regardless of local cache
- Automatically append `write_date` to loaded fields for cache comparison
- Add template as record in the indexedDB cache
- Refactor `data_service.js`: extract `syncInitialData`, `cleanLocalData`,
`cleanOldModels`, and `handleLoadingDataError`; handle stale model
cleanup in IndexedDB when modules are uninstalled
- Compress multiple calls (load_data, load_data_params,
filter_local_data, ..) into a single load_data call.
- Change some loading method to use load_data instead of a custom method
for each model.
task-id: 4982785
Enterprise PR: https://github.com/odoo/enterprise/pull/93803
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prEnterprise Point of Sale modules were aligned with Odoo's newer data loading approach, making setup and screen data retrieval more consistent across countries and POS features. This mainly supports reliability and maintainability, with added tests for preparation screen loading to reduce regression risk.
Original PR description
Align all enterprise POS modules with the refactored data loading mechanism introduced in community. pos_enterprise: - Add `PosLoadMixin` override with `_load_prep_data_domain_and_dependencies()`,…
Align all enterprise POS modules with the refactored data loading mechanism introduced in community. pos_enterprise: - Add `PosLoadMixin` override with `_load_prep_data_domain_and_dependencies()`, `_load_prep_metadata()`, and `_read_prep_data_from_metadata()` to mirror the new metadata-based loading pattern for preparation display data - Refactor `pos_prep_display.py`: replace `load_data_params()` and `load_preparation_data()` with `_load_metadata()` using the new mixin; remove the centralized `_load_pos_data_relations()` call on `pos.session` - Update `_load_pos_data_domain()` signature to remove the `config` parameter and resolve it from `data['pos.config']` directly - Adapt `data_service.js` patch: merge `loadFieldsAndRelations()` into `loadInitialData()` using `getFieldsAndRelations()`/`initFieldsAndRelations()`; simplify `initData()` pos_settle_due: - Remove `ir_ui_view.py` override that manually exposed two view IDs — these are now handled by the generic `ir.ui.view` loading in community - Inject the two view IDs (`customer_due_pos_order_list_view`, `due_account_move_list_view`) directly into `pos.config` read data via `_load_pos_data_read()` instead l10n_br_edi_pos, l10n_cl_edi_pos, l10n_ec_edi_pos, pos_appointment, pos_blackbox_be, pos_enterprise, pos_event_iot, pos_iot, pos_planning, pos_settle_due, pos_self_order_restaurant_appointment: - Update `_load_pos_data_domain()` signatures to drop the `config` argument in line with the new mixin interface task-id: 4982785 Community PR: https://github.com/odoo/odoo/pull/225341