Wednesday, September 16, 2026
30 changes · saas-19.3
Enhancements to existing features
Partner lists now show each customer's reminder level and include filters for overdue accounts and reminder levels. This helps teams quickly identify customers needing follow-up and prioritize collection actions.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/enterprise#130440 task-6501327
Peppol-related errors are now shown as separate, readable items instead of one technical line. This helps users understand what went wrong and what action may be needed when electronic document validation fails.
Original PR description
Before this commit, Peppol error messages (e.g. Schematron errors) were logged in the chatter as a single unformatted line and without any humanization. The errors were too technical and the user could not easily know what action to take. This PR splits the raw error payload into individual entries, maps known error codes to human-readable explanations, and renders them as an HTML list in the chatter. task-6144909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283311 Forward-Port-Of: odoo/odoo#265253
Customer follow-up screens now show reminder levels directly in partner lists and make it easier to filter overdue accounts. This helps teams quickly prioritize collection actions and find customers by reminder stage without extra manual checks.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/odoo#286688 task-6501327
Point of Sale now remembers whether a cashier last used a percentage or fixed global discount and makes that the default for the next order. This reduces repeated selections for businesses that prefer fixed discounts, and the discount setting label is clearer because it now supports more than percentages.
Original PR description
The default global discount type was always `percent`, ignoring the type the cashier last picked. Some clients prefer `fixed` and had to reselect it on every order. Save the last global discount type used in local storage and use it as the default type for the next global discount. Since the discount value is no longer necessarily a percentage, rename the "Discount Percentage" field and its label to "Discount Value". --- Task: https://www.odoo.com/odoo/project/1737/tasks/6507075 Forward-Port-Of: odoo/odoo#285353
Resolved issues and error corrections
This fixes automated website builder checks so they continue to work with newer Chrome behavior when reading background image sizing. It helps keep release validation stable without changing the website experience for users.
Original PR description
In Chrome 152, single-value `background-size` properties may be serialized or expanded to include implicit dimensions (e.g., appending `auto` like `100px auto`), causing strict exact-string test assertions to fail. This commit updates `website` builder test expectations to use regex prefix matching or substring inclusion so tests remain reliable across different Chrome versions. Note: this is a followup of https://github.com/odoo/odoo/pull/285591 where I missed one occurence during forward-port. My bad. runbot-946570 Forward-Port-Of: odoo/odoo#288519
This fixes an automated point-of-sale test that could fail unpredictably when receipt printing produced an error on busy systems. The change prevents the screen from advancing too quickly, making test results more stable without changing the customer checkout experience.
Original PR description
The fast-payment tours with automatic receipt printing configure an unreachable printer to exercise the printing-failure path. Once the failed print settles, FeedbackScreen arms a 1500ms timer that auto-navigates to the next order (iface_print_auto). The tour needs to detect the resulting error dialog, confirm it, then click the validation button, all before that timer fires. On a fast machine this comfortably fits, but on a loaded CI runner the sequence can take longer than 1500ms, so the auto-navigation happens first, unmounting the feedback screen before the tour can click ".button.validation" and causing a step timeout that could not be reproduced locally. Click on the feedback screen background right after confirming the dialog to call stopAutomaticSkip() and cancel the pending timer, removing the race entirely. This mirrors the pattern already used in test_automatic_receipt_printing. runbot-946191 Forward-Port-Of: odoo/odoo#288403 Forward-Port-Of: odoo/odoo#284690
PINT electronic invoices are now generated through the newer UBL export instead of the older BIS 2.0 process. This helps simplify future maintenance and supports the gradual removal of the outdated BIS implementation without changing the business workflow.
Original PR description
Problem --------- Currently, PINT uses the old BIS2.0 export. Objective --------- Decouple the exports as an effort to remove the old BIS implementation. no-task --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288031 Forward-Port-Of: odoo/odoo#283585
This fixes how the website builder handles links entered without a prefix, such as website addresses, email addresses, and phone numbers. Users will now get the expected link type automatically, reducing broken or incorrect links on images and social media fields.
Original PR description
Steps to reproduce: - Add an image and add a link on that image. - Set the URL input to "odoo.com". - Click outside the image and click on the image again. => The "http" protocol was added instead of "https". - Set the URL input to "test@test.com". - Click outside the image and click on the image again. => The "http" protocol was added instead of "mailto". The issue also occurs for phone numbers. The builder URL picker did not normalize entered URL values like the editor link popover does. Actions using the picker could therefore receive raw values and would need to normalized the URL in a different way. This commit reuses the editor link input normalization helper in the builder URL picker so committed and previewed URL values are handled consistently. task-6384411 Forward-Port-Of: odoo/odoo#286189 Forward-Port-Of: odoo/odoo#275936
Asset depreciation models are now labeled by the time needed to fully depreciate an asset instead of by a rate percentage. This makes model names easier to understand and fixes broken wording in translated languages by removing an English-only plural ending.
Original PR description
This commit does 2 things: 1. Rate-based models were named after their rate, e.g. "2.78% per Month". Now they are named after the duration it takes to fully depreciate the asset. For example "2.78% per Month" now reads "36 Month". A helper function was added to handle the rounding of the method number. 2. Also the plural 's' that was appended to the duration was removed. It was added onto the end of the translated period label, so it produced broken names in every language other than English. task-6498452
Purchase receipts for subcontracted products now use the specific subcontractor location set on the vendor, rather than falling back to the company's generic subcontracting location. This improves inventory traceability and ensures stock movements reflect the correct subcontracting partner setup.
Original PR description
This is a backport from 19.4 commit: https://github.com/odoo-dev/odoo/commit/b3c592e27b37b4ac4357d1c4eb33d3afd51c16d4 **Issue** While confirming a PO for a subcontracted product, the receipt's move…
This is a backport from 19.4 commit: https://github.com/odoo-dev/odoo/commit/b3c592e27b37b4ac4357d1c4eb33d3afd51c16d4 **Issue** While confirming a PO for a subcontracted product, the receipt's move takes the company's generic subcontracting location as its source, instead of the location set on the subcontractor partner. **Steps to reproduce** - On a subcontractor partner, set a "Subcontractor Location" different from the company's default Subcontracting location. - Create a subcontracting BOM for a product with that partner as subcontractor. - Create and confirm a PO for that product with the partner as vendor. - Confirm the receipt and check the move history -> The incoming move's source location is the generic company "Subcontracting" location instead of the location set on the partner. **Cause** While confirming the PO, it creates the associated receipt (picking): https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/purchase/models/purchase_order.py#L719 https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/purchase_stock/models/purchase_order.py#L206 which creates and confirms the associated moves: https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/purchase_stock/models/purchase_order.py#L384-L385 which retrieves the subcontracting location from either: - the picking's partner, or - the company, if it can't get it from the picking: https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/mrp_subcontracting/models/stock_move.py#L154-L156 At that point the move takes it from the company, since the move has no picking yet. It only gets linked to a picking right after: https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/mrp_subcontracting/models/stock_move.py#L158 https://github.com/odoo/odoo/blob/d38e85c54ab25b23e4c3930a48b704a6d34b4ecf/addons/stock/models/stock_move.py#L1625-L1642 opw-6517624
Fixed a Point of Sale issue where selling multiple physical gift cards of the same value could combine them into one line, preventing entry of separate card codes. This helps cashiers complete gift card sales correctly and avoids failed or unsynced orders caused by duplicate loyalty card codes.
Original PR description
Steps to reproduce: - Create a gift card program (several programs sharing the same gift card product show the same issue) - In the PoS, sell a physical gift card: click the gift card product and set…
Steps to reproduce:
- Create a gift card program (several programs sharing the same gift card product show the same issue)
- In the PoS, sell a physical gift card: click the gift card product and set a code through "Sell physical gift card?"
- Click the gift card product again to sell a second physical card of the same value
- Validate the order
Issue:
The second unit is merged into the already coded orderline (one line, qty 2, one code), so the "Sell physical gift card?" link is no longer displayed and the second code cannot be entered. Validating the order then fails with "The operation cannot be completed: A coupon/loyalty card must have a unique code." and the order stays unsynced: the qty 2 line is split into two point entries both carrying the same gift_code, so two loyalty.card records are created with the same code.
Cause:
_setupGiftCardOptions() (and setupEWalletOptions()) pass merge=false so that a gift card line is never merged, and until 17.0 add_product() honored it ("options.merge !== false"). The 18.0 store refactoring dropped it: addLineToOrder() decides merging from a local variable that is only set to false when a price_unit is given in vals, and never reads opts.merge. The option became dead code, in point_of_sale's addLineToOrder as well as for the pos_discount caller.
Fix:
Honor opts.merge === false in addLineToOrder(). Callers that do not pass the option are unaffected, so the default merging behaviour is byte-for-byte the same; only the callers explicitly forbidding a merge (gift card, ewallet and discount lines) get their pre-18.0 behaviour back.
opw-6466324
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287486
Forward-Port-Of: odoo/odoo#286237Accounting users in Chilean companies can now upload XML files to create customer invoices without needing administrator rights. This prevents invoice imports from failing due to restricted access to the stored electronic document file.
Original PR description
Scenario: - be not an admin but have access to accounting - switch to chilean company - go to customer invoices - click on Upload and upload an XML file Result: an invoice is created, but the XML is not treated because of this permission error: 19.0/l10n_cl_edi/…/account_move.py", line 1078, in _l10n_cl_import_dte invoice.l10n_cl_dte_file = file_data['attachment'] odoo.exceptions.AccessError: You do not have enough rights to access the field "l10n_cl_dte_file" on Journal Entry (account.move). Please contact your system administrator Cause: l10n_cl_dte_file field is only accessible to administrator. opw-6509550 Forward-Port-Of: odoo/enterprise#131485 Forward-Port-Of: odoo/enterprise#130416
Sitemap creation now loads only the website page data it needs instead of pulling in large page design fields. This helps prevent out-of-memory failures on websites with many pages, making sitemap generation more reliable.
Original PR description
- Before this commit: All fields of ir.ui.view were prefetched, including arch_db and arch_prev. This caused an out-of-memory issue when dealing with many pages. - After this commit: Only required fields are fetched, avoiding unnecessary memory consumption from view architecture data. opw-6470593 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284945
The website configurator now handles temporary outages of Odoo's online website service instead of crashing during the color palette step. This helps users continue creating a new website even when the external service cannot be reached.
Original PR description
bug: configurator_missing_industry raised an uncaught RPC_ERROR when the IAP website API was unreachable, breaking the color palette step of the website configurator. steps: - Go in settings and set in the system parameters `website.website_api_endpoint` to anything that won't work - Create a new website - Traceback at the palette step fix: Wrap the IAP calls in a try/except for AccessError. task-6325919 Forward-Port-Of: odoo/odoo#280675
Users can now open the Attachments menu even when expenses with attachments belong to a company that is not currently active. The fix ensures the system only checks expense records the user can access, preventing unnecessary access errors in multi-company setups.
Original PR description
**Problem:** When any expense belonging to a given company has an attachment on it, an Access Error will occur when attempting to enter the Attachments Menu when that company is not active. **Cause:** In the `hr_expense` override of `_inaccessible_comodel_records`, all `hr.expense` records are browsed, then have their values read, even if they are not currently accessible. https://github.com/odoo/odoo/blob/f28aa8e7aa0e2c6fb97c841711800410a1da3b72/addons/hr_expense/models/ir_attachment.py#L22-L23 **Purpose:** Use `_filtered_access` to ensure that only currently accessible records are read. **Steps to Reproduce in Runbot:** 1. Add an attachment to an `hr.expense` record in the My Company (San Francisco) Company. 2. Activate a different company and disable My Company (San Francisco), then attempt to open the Attachments menu in the Settings App. opw-6517249
This change restores the previous way Mexican electronic payment complements report amounts, using the maximum allowed number of decimal places. It follows updated guidance from the certification provider after consultation with Mexican authorities, helping reduce rejected or non-compliant payment documents.
Original PR description
Quadrum reverted their changes because > Derived from a consultation with the government we reverted to our previous behavior, we recommend using the maximum number of decimals allowed Reverts commit https://github.com/odoo-dev/enterprise/commit/f13d204dacf9eae98c78de26e9f2e54387a0eaee as well opw-6561617 Forward-Port-Of: odoo/enterprise#131640 Forward-Port-Of: odoo/enterprise#131581
This fix prevents sales quotations from failing when a user saves immediately after rearranging order lines. Saving is briefly paused while the line reorder finishes, making the Sales workflow more reliable for users working quickly or with large quotations.
Original PR description
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has…
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has multiple sale order lines 3. Reorder a sale order line and immediately save (there's more chance to reproduce if you have a lot of sol and if you use the shortcut `ALT + S`) 4. An error is thrown Issue: It is possible that the save takes place during the execution of super.sortDrop. This reassigns the id of all the records in this.props.list.records Therefore, calling `_handleQuantityAdjustment` with the recordMap computed before the save uses an id that has disappeard from this.props.list.records so we cannot find it at https://github.com/odoo/odoo/blob/4973903252865a6a7a2da235bc3a01675dbbff4d/addons/sale_management/static/src/fields/sale_order_line_field/sale_order_line_field.js#L263 which eventually throws an error Solution: Suspend any save mechanism when sortDrop starts and resume it when sortDrop has finished opw-6483206 Forward-Port-Of: odoo/odoo#286787
The call settings menu now shows the correct name for the selected audio output device when microphone and speaker devices share the same system ID. This prevents confusion for users checking or changing their call audio settings, especially in Chrome setups with default devices.
Original PR description
Steps to reproduce: - Have an input audio device and an output audio device with different names but same device ID. Using Chrome, if the OS only sees one of each, both should have "default" as device ID. (alternatively, modify the code at `updateDevicesList` to simulate having devices of that kind). - Select those devices in the call settings UI, then open the settings UI dropdown again. => The "input" device is properly shown but the "output" device shows the "input" device names (while in fact this is the right one selected in the inner dropdown). This happens since [1]. Before that there were native `<select>` nodes that filtered devices by kind before rendering each option, and the "default" value of Chrome was not really handled. [1]: https://github.com/odoo/odoo/commit/93d0931fba1f467de265200b6a0463cf7edf9534 Related to task-6533808 Forward-Port-Of: odoo/odoo#288285
Creating reordering rules for manufactured products shared across multiple companies no longer shows an access error when other companies have their own bills of materials. Odoo now uses the appropriate company-specific bill of materials, reducing confusion and helping replenishment setup complete smoothly.
Original PR description
#### Issue: When creating a reordering rule for a shared manufactured product in a multi-company database, saving the rule may raise an `AccessError` on `mrp.bom`. The orderpoint is still created,…
#### Issue: When creating a reordering rule for a shared manufactured product in a multi-company database, saving the rule may raise an `AccessError` on `mrp.bom`. The orderpoint is still created, but the user sees a record-rule error if the product also has BoMs in companies that are not currently active. #### Example: A product is shared across multiple companies, and each company has its own BoM for that product. In the reproduced case, the active company has the correct variant BoM. However, the orderpoint computation first checks the broader product-template BoM relation, which may include BoMs from the other companies. As a result, Odoo can try to access a BoM from another company while the user is only working in the active company, causing an access error. #### Steps to reproduce: Use a multi-company database with MRP enabled. Create or use a shared product available to multiple companies. Create BoMs for that product in more than one company. Set the active company to the company where the reordering rule should be created. Create a reordering rule for the product. Save the reordering rule. Note the AccessError related to mrp.bom. #### Root Cause: The MRP orderpoint computations read `product_id.bom_ids` directly. This is the product-template BoM relation and can include BoMs from other companies for a shared product. Reading fields on those BoMs, such as `product_uom_id`, can hit the standard `mrp.bom` multi-company record rule. #### Fix: Prefer `product_id.variant_bom_ids` before falling back to `product_id.bom_ids` in the affected orderpoint computations. This avoids reading template-level BoMs from other companies when the product has a variant-specific BoM for the current reordering-rule use case. opw-6253743 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276627 Forward-Port-Of: odoo/odoo#267411
Fixes an issue where manually changing component lines during subcontracting production could leave behind invalid inventory records. This prevents later stock reservation or transfer validation errors, improving reliability of subcontracting receipts.
Original PR description
#### Issue When manually adding component lines in the subcontracting Record Production flow, extra stock move lines can remain in the database without a linked stock move. Those lines have no…
#### Issue When manually adding component lines in the subcontracting Record Production flow, extra stock move lines can remain in the database without a linked stock move. Those lines have no move_id, so their related state is empty. They can later be selected by stock reservation code and cause validation errors when processing the transfer. #### Steps to reproduce 1. Create a subcontracted product. 2. Confirm a subcontracting receipt/purchase flow for that product. 3. Open the subcontracting Record Production popup. 4. Remove an existing component line. 5. Add a new component line for another storable product available at the subcontractor location. 6. Save/record the production. 7. Check stock.move.line records , Inventory > History > Group By Status. An extra stock move line is left with no move_id. #### Root cause The inverse of `mrp.production.move_line_raw_ids` already collects and deletes move lines detached from existing raw moves. However, when the user adds a new component product, the inverse creates a new additional raw move. Creating that raw move can also create reserved move lines. The inverse then replaces `move.move_line_ids` with the user-entered lines, but it did not collect the newly created reserved lines before replacing them. Those reserved lines were detached from the move and left in the database as orphan stock move lines. #### Fix Apply the same cleanup logic to newly created additional raw moves: collect their auto-created move lines before replacing `move_line_ids`, then unlink those detached lines at the end of the inverse. opw-6304649 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281864 Forward-Port-Of: odoo/odoo#276235
Australian payroll users can now register super payments reliably, regardless of whether they open the super contribution from Pay Runs or from the reporting menu. This prevents a workflow-blocking error caused by pay run status information being incorrectly reused during payment creation.
Original PR description
Current behavior: -- Registering a super payment from a pay run opened out of the Pay Runs kanban fails with "Wrong value for account.payment.state. The same super contribution registers without…
Current behavior: -- Registering a super payment from a pay run opened out of the Pay Runs kanban fails with "Wrong value for account.payment.state. The same super contribution registers without error when opened from Payroll > Reporting > Australia > Super Contributions. Expected behavior: -- Register Super Payment should work regardless of how the super contribution record was reached. Steps to reproduce: -- - Take an Australian pay run through to Paid - Open Payroll > Payslips > Pay Runs, kanban grouped by Status (default) - Open the pay run from the Paid column, then Super Submissions - Lock the super contribution and click Register Super Payment Cause of the issue: -- The Pay Runs kanban is grouped by state, so the web client adds default_state to the context of records opened from a column. That context follows the smart button into action_register_super_payment, where account.payment is created without an explicit state. The ORM applies default_state from the context which is a hr.payslip.run value that account.payment.state does not accept. The same leak reaches account.move through action_post, which creates the payment journal entry. Fix: -- apply with_context(clean_context(self.env.context)) to the recordset to remove the default_state key opw-6478084 Forward-Port-Of: odoo/enterprise#128724
Fixed an issue where automation rules could perform the same action twice when a record was created and a monitored field was filled in automatically. This prevents duplicate emails, activities, or similar automated follow-ups, improving reliability for teams using workflow automation.
Original PR description
An automation rule triggered when a field reaches a specific value can run its actions twice when a record is created without that field provided explicitly. For example, a rule that sends one email…
An automation rule triggered when a field reaches a specific value can run its actions twice when a record is created without that field provided explicitly. For example, a rule that sends one email and schedules two activities can send two emails and schedule four activities for one record. ### Reproduction Steps Create an automation on `crm.lead` that triggers when the stage is set to "New", with one email action and one activity action. Create an opportunity through the website contact form, which leaves the stage unset. The lead is assigned to the "New" stage, but both actions run twice. ### Cause The lead stage is computed and read during creation. That computation can trigger the automation before the creation hook processes the same record, causing both paths to run the actions. The processing code tracks records already handled during nested computations. The feedback flag used for this tracking is attached to the records, but the code checked the automation context instead. When the computation was triggered during creation, the flag was therefore missed. ### Fix Read the feedback flag from the records so nested computations update the shared tracking state. The creation hook then skips records already processed by the computation. opw-6331134 Forward-Port-Of: odoo/odoo#288222
Product carousels set to single-item scrolling now slide correctly on websites using right-to-left languages such as Arabic. This prevents empty spaces and distorted product images, improving the browsing experience for those visitors.
Original PR description
**Description of the issue/feature this PR addresses:** When a dynamic product carousel is set to single-image scrolling mode in an RTL language (e.g., Arabic), sliding between items behaves…
**Description of the issue/feature this PR addresses:**
When a dynamic product carousel is set to single-image scrolling mode in an RTL language (e.g., Arabic), sliding between items behaves inconsistently, resulting in empty spots or temporarily distorted images.
This occurs because the DOM manipulation creating the infinite scroll was coded for LTR physical movements. In LTR, the first DOM element is visually on the left. In RTL, the visual layout is mirrored, with the first DOM element visually on the right. When we then trigger a "left" slide in RTL, the LTR-based JavaScript doesn't appropriately move the last element to the first index prior to the animation, resulting in an empty gap, among other issues.
This commit resolves the issue by checking the document's text direction. By swapping the target direction ("left" or "right") in onSlideSingleScroll and onSlidSingleScroll when RTL is active, the underlying array operations now correctly align with the animation.
**Steps to reproduce:**
- Website > Edit > add Product Carousel > Customize > change Scrolling Mode to Single
- Website > Edit > Theme > Website > Language > change to Arabic
- Click the arrows in the product carousel > observe inconsistent scrolling with visual glitches
**Current behavior before PR:**
- Visual glitches when sliding carousels in single-image scrolling mode for RTL languages
**Desired behavior after PR is merged:**
- No visual glitches when sliding carousels in single-image scrolling mode for RTL languages
opw-6490277
Forward-Port-Of: odoo/odoo#286042Corrects how Mexican electronic payment documents calculate fixed-rate taxes on partial payments, preventing mismatches that caused payment invoices to be rejected by certification providers or SAT. This improves reliability for companies issuing Mexican CFDI payment complements with IEPS Cuota taxes.
Original PR description
When generating a payment complement, tax base and importe coming from the related invoice are prorated by the percentage actually paid, each rounded independently to the currency precision. The…
When generating a payment complement, tax base and importe coming from the related invoice are prorated by the percentage actually paid, each rounded independently to the currency precision. The post-fix step that restores the SAT invariant uses a Tasa-only formula (`base = total / (1 + rate)`), so Cuota (fixed amount per unit) taxes keep mismatched values, ending up with `ImporteDR != round(BaseDR * TasaOCuotaDR)`. This leads to CFDIs rejected by the PAC/SAT. Steps to reproduce: - Create a customer invoice with a Cuota IEPS tax (e.g. 26.2569). - Register a partial payment whose amount is not an exact divisor of the invoice total (e.g. one third). - Send the payment CFDI: the resulting Cuota TrasladoDR has an ImporteDR that does not match BaseDR * TasaOCuotaDR, leading to a rejected CFDI. This commit recomputes `importe` from the prorated `base` for Cuota taxes (bypassing the Tasa post-fix) opw-6087564 Forward-Port-Of: odoo/enterprise#131436 Forward-Port-Of: odoo/enterprise#113395
Company-paid expenses created from a project now apply the project allocation only to the actual expense line. This prevents project reporting from being cancelled out by matching entries on payment-related lines, giving more accurate project cost visibility.
Original PR description
### Current behavior: Creating a company-paid expense from the Project overview posts a journal entry with the project analytic on both the expense and outstanding lines, so the analytic balance nets to zero ### Expected behavior: Analytic distribution should only be on the P&L (expense) line ### Steps to reproduce: 1. Open a project overview and create a company-paid expense 2. Submit, approve, and post it 3. Open the journal entry: analytic is on debit and credit lines ### Cause of the issue: `project_id` stays in the context after `clean_context` during `_create_company_paid_moves`. With `sale_project`, AML analytic compute then applies the project distribution to outstanding/tax lines as well ### Fix: Removed `project_id` from the context when creating company-paid moves opw-6368848 Forward-Port-Of: odoo/odoo#287299 Forward-Port-Of: odoo/odoo#280256
Instagram posts now allow more time to complete when Instagram retrieves images from Odoo servers. This reduces failed posts caused by timeouts and makes publishing to Instagram more dependable for affected customers.
Original PR description
Bug === On some database on the saas, timeout issue happen for Instagram. Because Instagram downloads the image on our server, it needs more timeout than other social media. Task-6547753 Forward-Port-Of: odoo/enterprise#131456 Forward-Port-Of: odoo/enterprise#131069
This fixes a form issue where Save and Discard buttons could remain visible even after there was nothing left to save. Users who retype an equivalent value, such as a differently formatted number, will now see the form return to a clean state correctly.
Original PR description
**Description of the issue/feature this PR addresses:** Fixes #287978 In `useInputField` (`input_field_hook.js`), `onChange` and `commitChanges` only trigger the `FIELD_IS_DIRTY` bus event with…
**Description of the issue/feature this PR addresses:** Fixes #287978 In `useInputField` (`input_field_hook.js`), `onChange` and `commitChanges` only trigger the `FIELD_IS_DIRTY` bus event with `false` when the newly typed value actually differs from the value already stored on the record. When the typed text parses to the *same* value as what is already on the record (e.g. retyping `6,250` after the field already holds `6,25`), the code takes the `else` branch, which only resets the displayed text and never notifies the bus that the field is no longer dirty. `form_status_indicator.js` keeps whatever `fieldIsDirty` state it last received from that bus event, and shows the Save/Discard buttons whenever `root.dirty || fieldIsDirty`. Since nothing ever fires `FIELD_IS_DIRTY(false)` in that `else` branch, `fieldIsDirty` stays stuck at `true`. `Discard` correctly resets `root.dirty`, but has no way to reset `fieldIsDirty`, so the Save/Discard buttons keep showing even though the record is clean. **Current behavior before PR:** 1. Edit a field to a new value, click away (value committed, indicator shows Save/Discard as expected). 2. Edit the same field again, this time typing different text that parses to the *same* value (e.g. `1.20` when the field already holds `1.2`), click away. 3. Click **Discard**. The record is reverted, but the Save/Discard buttons remain visible. They stay stuck until the page is reloaded. **Desired behavior after PR is merged:** Retyping different text that parses to the same value clears the field's dirty state like any other case where the field ends up unchanged, so Discard (or any other action) correctly hides the Save/Discard buttons once there is nothing left to save. Forward-Port-Of: odoo/odoo#288030
Salespeople now receive the expected upsell warning when several timesheet entries together pass the customer’s prepaid service threshold. This helps teams spot billable overages consistently, even when hours are recorded in smaller increments instead of all at once.
Original PR description
Steps to reproduce: -------------------------------------------- 1. Install `sale_timesheet` module 2. Create a product with: * Type: Service * Invoicing Policy: Prepaid/Fixed Price * Create on…
Steps to reproduce:
--------------------------------------------
1. Install `sale_timesheet` module
2. Create a product with:
* Type: Service
* Invoicing Policy: Prepaid/Fixed Price
* Create on Order: Project & Task
* Upsell Threshold: Set some value (e,g. 600%)
3. Create and confirm the sale order with that product
4. Create and confirm an invoice for the sale order
5. From Recorder Hours Smart button:
* Add two timesheets for hours 3 & 4 (Combined h > Threshold Percentage/100)
Observation:
--------------------------------------------
* When adding a single 7h timesheet, an upsell warning activity is correctly created for the salesperson in the chatter.
* When adding multiple incremental timesheets whose combined duration exceeds the threshold, no upsell warning activity is created.
Issue:
--------------------------------------------
* The `_compute_field_value` method filters on `invoice_status != 'upselling'` before recomputing the new invoice status.
* After the first incremental timesheet is added, the sales order already has the upselling status from the previous computation.
* As a result, subsequent computations skip the upsell activity logic, even though `has_displayed_warning_upsell` is still `False` and no warning activity has yet been created.
Solution:
--------------------------------------------
* Removes the `so.invoice_status != 'upselling'` condition from the filter.
* The order-level `invoice_status` was never the right place to control upsell activity creation. An order can have `invoice_status == 'upselling'` for various reasons (delivered > invoiced), but that doesn't mean all lines have already triggered their upsell warnings.
opw-6117754
Forward-Port-Of: odoo/odoo#287642
Forward-Port-Of: odoo/odoo#266275The QFPay point-of-sale payment test was updated to match the current checkout flow, where staff must press Send before the terminal payment starts. This prevents the automated test from timing out and helps keep payment terminal validation reliable.
Original PR description
Commit 7a208335fa84 ("[IMP] point_of_sale: avoid fast payments") removed the automatic sending of the transaction to the terminal, so the user now has to click on "Send" to trigger the payment request.
The tours of the other payment terminals (adyen, razorpay, safaricom, viva_com) were adapted accordingly, but pos_qfpay was missed. As a result, the payment line never reaches the 'waitingCard' status and the tour times out waiting for the "Waiting for card" step.
Add the missing PaymentScreen.clickSendButton() step to fix the tour.
runbot-940270
Forward-Port-Of: odoo/odoo#275204This fixes an error that occurred when users opened sale or purchase receipt records from Invoice Analysis. Businesses using receipts can now drill down from reporting views to the underlying documents without interruption.
Original PR description
When enabling Sale/Purchase Receipts and trying to open the form view from Invoice Analysis, an error occurs. The issue is caused by the `move_type` used in `_where()`, which includes a type that is not defined in the `move_type` field selection. Steps to reproduce: - Enable Sale/Purchase Receipts. - Create a Sale/Purchase Receipt for partner A and confirm it - Go to Invoice Analysis and open the Pivot view - Click on a cell to open partner A's receipt, then try to open the form view - Error Ticket [link](https://www.odoo.com/odoo/project.task/6462153) opw-6462153 Forward-Port-Of: odoo/odoo#288077 Forward-Port-Of: odoo/odoo#282922