Daily updates from Odoo
Monday, June 22, 2026
150 changes
26 changes
Resolved issues and error corrections
This update resolves an issue where leave schedules incorrectly blocked resource allocation, now only applying to resources with matching calendars. Additionally, tests have been reorganized and corrected to ensure accurate coverage of rental planning features, improving overall system stability and reliability.
Original PR description
## [FIX] sale_renting_planning: check global leaves working schedule Before this commit: any `resource.calendar.leaves` with no `resource_id` created would prevent all resources from being allocated…
## [FIX] sale_renting_planning: check global leaves working schedule
Before this commit: any `resource.calendar.leaves` with no `resource_id` created would prevent all resources from being allocated during the leave date.
After this commit: any `resource.calendar.leaves` with `no resource_id` would be applied only to resources with the same `calendar_id` as the leave.
if the leave has no `calendar_id` then the leave applies to all `resource.calendars`
if a resource has no `calendar_id` then leaves with no `calendar_id` apply to it as well
## [IMP] {website_}sale_renting_planning: move tests from industry and fix existing ones
This commit moves the tests from [odoo/industry#1980](vscode-file://vscode-app/snap/code/237/usr/share/code/resources/app/out/vs/code/electron-browser/workbench/workbench.html) to their respective standard modules.
It also fixes the logic behind some tests as they weren't testing a `planning.role` with `sync_shift_rental` enabled.
task-6179505
Forward-Port-Of: odoo/enterprise#120865
Forward-Port-Of: odoo/enterprise#116430This update enhances the Timesheet Assistant by streamlining suggestions, adding shortcuts, and correcting inaccuracies in hour calculations. Specifically, it removes distracting highlights, excludes leave time from totals, and improves the relevance of suggestions for timesheet creation.
Original PR description
## Expected Behavior After Commit - Remove the green highlight when selecting a suggestion. - Add shortcuts for timesheet creation buttons. - Allow calendar events to be considered side activities - Exclude leave time from total hours, as leave time is already counted in the timesheet. - Do not show to‑do tasks (tasks without a project) in suggestions. - Restore previous suggestions for to‑do tasks when they later become linked to a project. - Add a default name for suggestions that do not have one. - Add hotkeys to Timesheet Assistant task-[6191451](https://www.odoo.com/odoo/project/4105/tasks/6191451) Forward-Port-Of: odoo/enterprise#120057
This update corrects an error that prevented users from sending SMS messages to website visitors. The issue stemmed from a change in how visitor data was accessed. This fix ensures that the correct phone number is used when sending SMS messages, improving the functionality of the website CRM SMS feature.
Original PR description
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another…
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another `Form` block. - Configure the form action to `Create an Opportunity` and `save`. - Fill in the required fields, including phone number and `Submit` the form. - Go to `Website` > `Reporting` > `Visitors` and click the `SMS` button on the visitor record. `AttributeError: 'website.visitor' object has no attribute 'phone'` After [this commit], which removed the mobile field from res.partner along with all related views, then it was updated to access the phone number from the website visitor. When a user creates an opportunity through the website and then tries to send an sms from the corresponding visitor record, it raises an error [1] because it attempts to access the phone field on website.visitor. when an anonymous (non-logged-in) user creates an opportunity, clicking the sms button on the corresponding visitor record triggers error here [2]. This commit ensures that the correct mobile field is accessed from the website visitor. [this commit]: https://github.com/odoo/odoo/commit/6b820eb6fc6f782ba6a83d605d87b4a1dd2a87be [1]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L13 [2]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L20 sentry-7550340909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270118
This update resolves an issue impacting payroll calculations in both the UAE (AE) and Saudi Arabia (SA) localizations. The fix ensures accurate net cost calculations by standardizing how salary rules are aggregated, preventing negative values from incorrectly affecting payroll totals.
Original PR description
Steps: - Add a new salary category with the parent_id of company contribution (COMP) in AE - Create a dummy salary rule of that category - Compute a payslip and see the net cost unchanged Or - Create and compute a payslip in SA - Company contributions will be subtracted from each other Issue: - In AE localization, the issue with the rule was dropping salary rules that have a parent of company contribution category - In SA localization, the issue with the NETCOST was the aggregation of individual rules could include negative values which is not the intended flow. Solution: A standardized approach was adopted in both localizations in order to match the calculation of the NETCOST across. This approach will account for the categories with company contribution parent as well as the positive values for the individual salary rules. Forward-Port-Of: odoo/enterprise#115499
This update resolves an issue impacting how sickness pay is calculated for employees transitioning between long and partial periods of absence. The fix ensures accurate DPV (disabled person's verification) computations, particularly when moving from a long sickness to a partial incapacity. This improves the reliability of payroll processing.
Original PR description
Forward-Port-Of: odoo/enterprise#120868
This update fixes a limitation in how subscription products are priced, allowing users to accurately set one-time prices for hybrid subscriptions. Now, the system requires a plan to be selected for subscription products and ensures users can correctly configure pricing for products that allow both recurring and one-time sales. This enhances the flexibility and accuracy of subscription pricing.
Original PR description
Before this commit:
1. Users could save a pricelist rule for a pure subscription product without assigning a plan.
2. Hybrid subscription products (where 'Allow One-Time Sale' is True) were filtered out of the pricelist item form when no plan was selected, preventing users from setting a one-time price.
After this commit:
- The `plan_id` field on the product template form is now mandatory if the product is a subscription and does not allow one-time sales.
- The `product_tmpl_id` domain on the pricelist item form is updated to `['|', ('recurring_invoice', '=', bool(plan_id)), ('allow_one_time_sale', '=', True)]`, allowing users to select hybrid products for one-time pricing.
task: 6164232
Forward-Port-Of: odoo/enterprise#115271This update fixes a bug that prevented customers from removing free shipping rewards once they were applied to their cart. The fix removes a restriction in the system that only handled discount rewards, now allowing free shipping and free product rewards to be removed as intended. This improves the customer experience and ensures accurate order totals.
Original PR description
Steps to produce: --- - Install `website_sale_loyalty`. - Go to `Website > ecommerece > Loyalty > DIscount & Loyalty`. - Create a new discount & loyalty program > set program type as `promotions`. -…
Steps to produce: --- - Install `website_sale_loyalty`. - Go to `Website > ecommerece > Loyalty > DIscount & Loyalty`. - Create a new discount & loyalty program > set program type as `promotions`. - Under Rewards, select `Free Shipping` as the reward type. - Create a product with a price of 1000 and publish it. - Add the product to the cart from the website. - Observe that free shipping is automatically applied on cart. - Attempt to remove the free shipping reward from the cart. Issue: --- - Free shipping (and similarly, free product rewards) cannot be removed from the cart once applied. Root cause: --- - At [1], the `website_sale_loyalty_delete` context is only passed when the reward type is `discount`. As a result, for free shipping and free product rewards, the context is not set. At [2], the order line is removed, but the reward is not added to `disabled_auto_rewards`. The `_auto_apply_rewards` method runs immediately afterward, detects the missing reward, and re-applies it automatically. Fix: --- - Since there are three reward types (discount, free shipping, and free product), the condition restricting the context to only discount rewards should be removed. [1]https://github.com/odoo/odoo/blob/5e90858fa91348f6aa33b4f8a246e77fbb8ea63f/addons/website_sale_loyalty/models/sale_order.py#L179 [2]https://github.com/odoo/odoo/blob/5e90858fa91348f6aa33b4f8a246e77fbb8ea63f/addons/website_sale_loyalty/models/sale_order_line.py#L15-L23 opw-6159288 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261732
This update resolves an issue where milestone deadline dates disappeared from task views after navigating back or refreshing. The fix ensures that milestone deadlines are consistently displayed in the task Kanban view and task form views, improving project tracking accuracy.
Original PR description
Steps to reproduce: 1. Open the Project application and open any project. 2. Filter the tasks by milestone (milestone deadlines appear as expected in the kanban view). 3. Open any task form view. 4.…
Steps to reproduce: 1. Open the Project application and open any project. 2. Filter the tasks by milestone (milestone deadlines appear as expected in the kanban view). 3. Open any task form view. 4. Click the browser's back button (or simply refresh the page while on the task kanban view). Issue: Milestone deadline dates disappear from the task Kanban cards and headers after navigating back or reloading. Why this happens: When hitting the browser back button or refreshing, the web client's router state recovery workflow executes (`loadRouterState` -> `loadState` -> `doAction` -> `_executeActWindowAction`). During this flow, `_getActionParams` checks if it can reuse the cached `lastAction`. However, due to a safety condition introduced in commit ab26f95893 to prevent embedded action showing across different projects, the router falls back to generating a fresh action request via `state.action`. This forces `_loadAction` to fetch the action definition from the database. Because the original base action window `act_project_project_2_project_task_all` lacks the `display_milestone_deadline` key inside its default context dictionary, the reloaded view is rendered without the flags required by the frontend to display milestone deadlines. opw-6283514 Forward-Port-Of: odoo/odoo#269781
This update fixes a visual issue in the CRM's Kanban view where the progress bar wasn't accurately displaying the number of opportunities in each stage. The team removed a counter that was previously showing the 'Other' opportunity count, and this change restores that functionality, providing a clearer picture of pipeline activity. This ensures sales teams have a more accurate view of their progress.
Original PR description
# How to reproduce - Go to the CRM kanban view - Add an oppurtinity where the salesperson is yourself & add another one where it is not in Stage X - Enable the "My pipeline" filter - Hover the progress bar of Stage X # The problem The green part of the progress bar displays "X Planned" while the grey one displays "No activities scheduled" even if there are # Cause This commit introduced the change from "X Other" to "No activities scheduled" : https://github.com/odoo/odoo/commit/ec52375d3b99f42e712b8a44afee43d82ffdf239 But it removed the counter, which the PO wishes to add back opw-6229549 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265523
This update corrects a problem in how Odoo generates UBL BIS3 files for Debit Notes. Previously, the system incorrectly used 'LegalMonetaryTotal' instead of the required 'RequestedMonetaryTotal' node. This change ensures compliance with UBL BIS3 standards, improving the accuracy of our financial document exports.
Original PR description
Problem --------- Debit note should have the node `RequestedMonetaryTotal` instead of `LegalMonetaryTotal`. Solution --------- Add a conditional depending on the document type. opw-6295897 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270238
This update fixes an issue where stock valuation calculations were incorrect when a product had no recorded value. The fix ensures all products are considered during valuation replays, resulting in accurate inventory accounting. This improves the reliability of financial reporting.
Original PR description
Usecase to reproduce: - Create two average product A and B - Delele all the product.value for B - Receipt both units at 10$ - Set the price unit of A to 20$ - Receipt both units at 20$ Check the value at date to trigger a replay of valuation Expected behavior: - Product A -> 20 units at 20$ -> 400$ - Product B -> 20 units at 15$ -> 300$ Current behavior: - Correct for A but B is 200$ It happens because when we replay the history, we check for the minimal product.value and we replay valuation from this date (with moves). However in our case, the product B has no product value and thus we replay from A product.value. However it arrives after the first receipt of B and thus we only consider the second receipt for B. This is fixed by ensuring we have a product.value for all products in order to add a date domain on the moves. Forward-Port-Of: odoo/odoo#255909 Forward-Port-Of: odoo/odoo#255787
This update fixes an issue where preparation prints weren't showing all items after transferring a restaurant order to a shared table. The fix ensures that all products from both orders are accurately reflected on the preparation reprint, improving kitchen efficiency and order accuracy. It addresses a technical problem related to how order history is managed during merging.
Original PR description
When moving an order (Order A) to a table that already has an order (Order B), the merged order only reprints Order B's products. The products from Order A are missing from the preparation reprint.…
When moving an order (Order A) to a table that already has an order (Order B), the merged order only reprints Order B's products. The products from Order A are missing from the preparation reprint. Steps to reproduce: ------------------- * Open a POS session on a Restaurant POS * Create an order (Order A) for Table 1 * Create a second order (Order B) for Table 2 * Transfer/Merge Order A to Table 2 * Reprint the preparation order > Observation: Only the products that were already on Table 2 (Order B) appear on the reprint. Products from Order A are missing. Why the fix: ------------ mergeOrders correctly transfers kitchen history (last_order_preparation_change.lines) via handlePreparationHistory, but does not update uiState.lastPrints on the destination order. The reprint button uses lastPrints.at(-1) when there are no pending changes, so it only shows the destination order's last print batch — ignoring the merged lines entirely. Implementation: After the merge loop, build a consolidated lastPrints entry from the destination order's last_order_preparation_change.lines (which now contains lines from both orders) and push it onto destOrder.uiState.lastPrints so that reprint reflects the full merged state. opw-6060684 Forward-Port-Of: odoo/odoo#270311 Forward-Port-Of: odoo/odoo#256309
This update fixes inaccurate Cost of Goods Sold (COGS) calculations for kit products in Odoo. The change ensures kits are correctly valued, addressing issues with multiple steps, multiple kits in a BOM, and FIFO inventory accounting. The fix simplifies the calculation and improves test coverage.
Original PR description
There's a few problems with kits and cogs This PR fixes them and unskips most tests of the test class. **Problems:** - Problem 1 multiple steps delivery - steps to reproduce: - activate 3 steps…
There's a few problems with kits and cogs
This PR fixes them and unskips most tests
of the test class.
**Problems:**
- Problem 1 multiple steps delivery
- steps to reproduce:
- activate 3 steps delivery
- create 2 storable products 'comp A' and 'comp B'
with category standard perpetual
- for both : set a cost of 10 and on on hand quantity
- create a storable kit product with category standard perpetual
- create a kit bom for the kit product with 1 comp A and 1 comp B
- confirm a SO for 1 quantity of the kit prod
- validate only first delivery
- confirm invoice
- Current behaviour:
No cogs line
- expected behaviour :
There should be cogs for 20$
- Problem 2 multiple kits in Bom :
- steps to reproduce:
- (multiple steps delivery not needed)
- use same products as for problem 1 but, in the Bom, set
the number of kit products produced to 2
- confirm a SO for 2 quantity of the kit prod
- validate all pickings
- confirm invoice
- Current behaviour:
Cogs have a value of 10$
- expected behaviour :
There should be cogs for 20$
- Problem 3: fifo comp
- steps to reproduce:
- with 1 step delivery
- create a storable product 'comp A' with fifo perpetual
category
- Confirm a PO and validate receipt for 1 comp A at 10
- Confirm a PO and validate receipt for 1 comp A at 20
- create a storable product 'kit' with fifo perpetual categ
- create a kit bom for the kit product with 1 comp A
- confirm SO for 2 kit
- deliver 1 quantity and create backorder
- confirm invoice for 1
- COGS line are created for 10$ (as expected)
- deliver the backorder
- confirm invoice for 1
- Current Behaviour:
Cogs are created for 15$
- Expected Behaviour:
Cogs should be created for 20$
**Cause of the issues:**
To compute the price_unit used for the cogs we call
_get_cogs_value()
https://github.com/odoo/odoo/blob/f8741728294a5147c7d2427d9997738096386262/addons/stock_account/models/account_move.py#L122
What we want is the price unit for 1 unit of the kit product
So we want :
sum(unit price of each comp * quantity of comp in bom)/ quantity of kit in bom
What is done for now :
Inside the sale_mrp override, for each component of
the bom we call _get_price_unit() on its move and
add the value to 'average_price_unit' and then divide
by the quantity of the kit product in the bom
https://github.com/odoo/odoo/blob/f8741728294a5147c7d2427d9997738096386262/addons/sale_mrp/models/account_move.py#L38-L42
Inside the sale_mrp override of _get_price_unit()
we return _get_kit_price_unit() called on the move,
https://github.com/odoo/odoo/blob/f8741728294a5147c7d2427d9997738096386262/addons/sale_mrp/models/stock_move.py#L15
Inside _get_kit_price_unit(), the variable 'component_qty_per_kit',
contains the quantity of each component as recorded in the bom
times the valued quantity (sale order line quantity).
For each comp :
- we store the return value of _get_price_unit
called on its moves in 'price_unit'.
- we add to 'total_price_unit':
price_unit * component_qty_per_kit/ the kit qty in the bom
we then return total_price_unit / valued quantity
So we actually return:
sum(unit price of each comp * quantity of comp in bom*
valued quantity)/ (quantity of kit in bom * valued quantity)
which is equal to:
sum(unit price of each comp *quantity of comp in bom)
/ quantity of kit in bom
https://github.com/odoo/odoo/blob/38c737c2a4cc29b48235a100cfa9d6152af73826/addons/mrp_account/models/stock_move.py#L40-L44
Problem 1 is caused by the fact that _get_price_unit()
will return 0 if there's only internal moves because
they have a value of 0.
(The problem does not happen with a single component
cause then the fallback on the super method is correct, but
with multiple comp the super method also returns 0
because _get_cogs_price_unit returns 0 when more than
one product).
Problem 2 is caused by the fact that we divide by the
quantity of the kit in the bom (kit_bom.product_qty) here
(inside _get_kit_price_unit) and again inside _get_cogs_value
as mentionned before.
Problem 3 happens because there is no mechanism
to account for already posted cogs inside the sale_mrp
override of _get_cogs_value(), as qty_invoiced
is computed but never used
https://github.com/odoo/odoo/blob/38c737c2a4cc29b48235a100cfa9d6152af73826/addons/sale_mrp/models/account_move.py#L31
**Fix**
As regards to the super methods (so non kit scenario),
_get_cogs_value() is used to :
- use original invoice if needed
- use standard price of the product if no moves
- deduct already posted cogs
- calls get _get_cogs_price_unit() to compute price_unit
based on the moves
All of this is also wanted for kits and don't need adaptation,
therefore the override should be on the _get_cogs_price_unit
where we do need a different behaviour when the product is a kit
Doing this we benefit from the 'already posted mechanism'
from _get_cogs_value which solves problem 3
Additionally, instead of calling get_price_unit we can directly
call the super method _get_cogs_price_unit as we have
already computed all the components quantities needed
for our computation and therefore don't need
_get_kit_price_unit to recompute all of this.
Also, _get_cogs_price_unit will fall back on the product
standard price if the move has no value which solves
problem 1.
That will also prevent dividing twice by the quantity
of kit product in the bom (bom.product_qty)
which solves problem2.
**Tests:**
Out of the 9 existing tests of the class (that were skipped
before this PR) and after adapation to v19 valuation :
- 2 succeeded before and after the fix : this PR unskips them
- 5 failed before the fix and now suceed with the fix : this PR
unskips them
- 2 failed before the fix and after the fix, they were let
skipped
In addition, 2 tests were added to cover problem 1 and 3
(problem 2 is covered in test test_sale_mrp_kit_bom_cogs)
Forward-Port-Of: odoo/odoo#270675
Forward-Port-Of: odoo/odoo#270075This update resolves an issue where users couldn't complete delivery preset orders in self-ordering mode if the Google Places API key wasn't set up. Now, the system automatically fills in addresses via the API if the key is present, or accepts manual address entry if it's not. This ensures a smoother ordering experience for all users.
Original PR description
Before this commit: =================== If the Google Places Autocomplete API key was not configured for the company, users could not proceed with delivery preset orders in self-ordering mode because the complete address could not be retrieved from the API. After this commit: ================== - If the API key is configured: The address is fetched using the Google Places Autocomplete API. - If the API key is not configured: The system accepts the address entered manually by the user. task-6213436 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an error in the Luxembourg eCDF XML export that was causing incorrect financial year data. Specifically, an issue with account 142 was resolved by removing it from the export mapping, ensuring accurate reporting on the Odoo Profit and Loss visualization. A new test has been added to verify the fix.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update resolves an issue where the inventory valuation closing entry incorrectly calculated accounting balances for companies with multiple stock locations. The fix ensures the closing entry accurately reflects the stock valuation for each company, preventing discrepancies in accounting balances. This ensures accurate financial reporting across all company setups.
Original PR description
**Steps to reproduce on a new db:** (bug also reproducable on runbot but the impact is less easy to compute because of influence of other existing companies) - create a new company as company 2 and…
**Steps to reproduce on a new db:** (bug also reproducable on runbot but the impact is less easy to compute because of influence of other existing companies) - create a new company as company 2 and use the existing default company as company 1. - create a warehouse for both company - for both comp, in settings for the 'fiscal localization' set Package : Generic Chart of account, if not already set (to have account journals). - for both comp, in settings for inventory valuation set 'periodic' and for periodic valuation set 'daily' From company 1 : - create a storable product with standard price method and set a cost of 30 - set an onhand quantity of 1 if you navigate to 'inventory valuation' you'll see that : - initial balance is 0 - ending stock is 30 - the variation lines have a balance of 30 - all of this is expected From company 2 : - change the cost of the product to 10 - set an onhand quantity of 1 if you navigate to 'inventory valuation' you'll see that : - initial balance is 0 - ending stock is 10 - the variation lines have a balance of 10 - all of this is expected From any company : - navigate to 'scheduled actions' and select the action 'Stock Account: Inventory Valuation Closing' - click on 'Run Manually' - navigate to 'inventory valuation' **Current behavior:** with company 1 selected : - the initial balance is now 30 - ending stock still 30 - no variation lines - the initial balance was correctly increased by the closing entry with company 2 selected: - the initial balance is now 40 - the ending stock is still 10 - the variation lines credit 30 in stock valuation In company 2 the closing entry debitted 40 in stock valuation instead of 10 which increased the initial balance to 40 instead of 10 If you open the journal items you'll find the closing amls have a balance of 40 instead of 10 **Cause of the issue:** The _cron_post_stock_valuation() method calls action_close_stock_valuation() on both companies https://github.com/odoo/odoo/blob/bfa39854e56da4bf23295d62f63d66973ad0d78e/addons/stock_account/models/res_company.py#L143-L144 This methods calls _action_close_stock_valuation with a context modified with only self.env.company.ids in 'allowed_company_ids' https://github.com/odoo/odoo/blob/bfa39854e56da4bf23295d62f63d66973ad0d78e/addons/stock_account/models/res_company.py#L56 This is needed because inside stock_value() we use the total value of the product https://github.com/odoo/odoo/blob/bfa39854e56da4bf23295d62f63d66973ad0d78e/addons/stock_account/models/res_company.py#L92 which will be the sum of the values of the product for each company inside allowed_company_id https://github.com/odoo/odoo/blob/bfa39854e56da4bf23295d62f63d66973ad0d78e/addons/stock_account/models/product.py#L274 So in case action_close_stock_valuation() was called from the 'generate entry' button from the inventory valuation view we need only the main company selected to be in the 'allowed_company_ids' so that the inventory value is computed based only on this company (as is the accounting value). The problem is that this does not work when calling the method from _cron_post_stock_valuation because then there is no 'allowed_company_ids' in the context (because it was called from _process_job() with a new env). so self.env.company will be the company of the user which will be company 1. https://github.com/odoo/odoo/blob/bfa39854e56da4bf23295d62f63d66973ad0d78e/odoo/orm/environments.py#L243 Therefore when _action_close_stock_valuation will be called on company 2, in the context, allowed_company_ids will be company 1. Then, when computing 'products', with_company() will add self (company 2) to the context. https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/models/res_company.py#L151-L152 So stock_value will return the sum of the total_value of each product for company 1 and company 2 which is 40 (instead of 10 for just company 2) https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/models/res_company.py#L242 We then create the closing accounting entry to match the accounting value with the stock value, which explains why the new initial accounting balance of company 2 is 40. **fix:** We set the context using self instead of self.env.companies This makes more sense as both in the cron use case and the generate entry use case the stock value we want is the one of the company in self. - In cron use case, it's obvious as the method is called in a for loop on each company - In the generate entry use case, self will also be the main company, because it's called, in actionGenerateEntry, on this.companyId https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/static/src/stock_valuation/controller.js#L75 which is computed based on the get_report_values https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/static/src/stock_valuation/controller.js#L21 https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/static/src/stock_valuation/controller.js#L28-L30 Which returns the main company https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/report/stock_valuation_report.py#L29 Most importantly, this is also aligned with how the accounting values are computed. https://github.com/odoo/odoo/blob/616e82d7b3a53b1facf481e783baed3e99393d3c/addons/stock_account/models/res_company.py#L103-L105 opw-6237402 Forward-Port-Of: odoo/odoo#269152 Forward-Port-Of: odoo/odoo#266932
This update optimizes how Odoo searches for documents, specifically addressing a slow and complex query when filtering by 'SHARED'. The change aligns with the production database's approach, resulting in faster search speeds and a more efficient system. This improves the overall user experience when searching for documents.
Original PR description
Searching for "not 'SHARED'" results in a very complex query. Our own production DB prefers this implementation, also easier to read. credit: https://github.com/odoo/enterprise/pull/105915#discussion_r2745148099 Task-5893183 Forward-Port-Of: odoo/enterprise#120870
This update ensures the 'pdp_identifier' field is correctly populated during company partner registration, particularly when using the PEPPOL EAS standard. Previously, the field would be left blank, causing issues with registration. Now, a user error message will appear if an invalid identifier is entered, preventing incorrect data and improving the registration process.
Original PR description
Currently when the company partner uses non 0225 peppol EAS the `pdp_identifier` field is `False`. Thus the (related) identifier field on the registration wizard is left empty. Also add a UserError when writing an invalid identifier to the `pdp_identifier` field instead of just silently failing. That way an error ill pop up in the registration wizard when trying to register with an invalid identifier. task-6307489 Forward-Port-Of: odoo/odoo#270915 Forward-Port-Of: odoo/odoo#270330
This update resolves an issue where DIAN XML files for Point of Sale (PoS) payments were being rejected due to incorrect calculations of prepaid amounts. The fix combines payment amounts into a single tag, ensuring accurate data transmission to the DIAN and preventing errors related to negative payment lines.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update resolves an issue where invoices with reverse charge VAT in Poland (fa3) were generating incorrect XML files for transmission to the tax authorities (KSEF). Specifically, the XML lacked the necessary information to accurately reflect the reverse charge amount and total sale value. This ensures proper tax reporting and compliance.
Original PR description
**STEP TO REPRODUCE** 1. Create an invoice with a tax with reverse charge (0% EU G for example). 2. Send the invoice to ksef. 3. Open the generated xml, and notice field P_18 is 2 while it should be 1 (because there is reverse charge). Also, there is not P13_10 indicated the total value of sale to which the reverse charge applies. opw-6041836 Forward-Port-Of: odoo/odoo#263764
This update fixes an issue preventing the 'Send to SII' option from appearing on newly generated vendor bills in Chile. The fix adjusts internal code to correctly display this option when a document is generated and awaiting electronic filing, ensuring compliance with Chilean tax regulations. This ensures users can properly submit their electronic invoices.
Original PR description
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF…
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF [XML](https://www.odoo.com/mail/message/1097235975) file. * Create a new **Purchase Journal** with **Use Documents** enabled. * Create a vendor bill using this journal. * Set the **Document Type** to **46 - Liquidación-Factura Electrónica**. * Confirm the vendor bill. **Observed behavior:** * The Send button is not visible on the confirmed vendor bill despite the DTE being generated and `l10n_cl_dte_status` being set to `not_sent`. **Cause:** * `_compute_display_send_button` in `account` only returns `True` for sale documents (`is_sale_document()`), so the "Send" button — which opens the Send & Print dialog containing the "Send to SII" option — was never shown on vendor bills. * `_get_move_constraints` in `account.move.send` unconditionally adds a `not_sale_document` constraint for non-sale documents, blocking the Send & Print dialog from processing vendor bills even if the button were visible. * The cron's `cron_run_sii_workflow` only processes moves with `l10n_cl_dte_status = 'ask_for_status'`, skipping moves still in `not_sent` state. **Fix:** * Override `_compute_display_send_button` in `l10n_cl_edi` to also show the "Send" button on posted moves with `l10n_cl_dte_status == 'not_sent'`, matching the pattern used by `l10n_br_edi`. * Override `_get_move_constraints` in `l10n_cl_edi` to remove the `not_sale_document` constraint for Chilean purchase documents with `not_sent` status, matching the pattern used by `l10n_br_edi`. **REF** During this [refactor](https://github.com/odoo/enterprise/pull/103427/changes/f5617ecf7584cf019897408df94b002622f48d9d), these two methods were inadvertently missed and were not overridden opw-6300571 Forward-Port-Of: odoo/enterprise#120818
This update fixes an error in the delivery note pricing calculation for products tracked by multiple lots. Previously, the price was incorrectly based on only the first lot, leading to inaccurate DDT costs. This change ensures that the total sale price across all lots is correctly reflected on the delivery note, improving financial accuracy.
Original PR description
Steps to reproduce: 1. Install Italian localization and l10n_it_stock_ddt 2. Create a product tracked by lots with a price of 100 3. Create two lots for that product, each with 5 in stock 4. Create a sale order for a quantity of 8 5. Confirm the sale order and validate the delivery 6. Print the delivery note Issue: Only the first lot's sale price is used in the DDT cost calculation (price = 500 instead of 800) Why this happens: The QWeb template used `move.move_line_ids[0].sale_price`, which only reads the sale_price of the first move line. When a delivery is split across multiple lots, each lot produces its own move line, so only the first is considered in the price calculation. opw-6244076 Forward-Port-Of: odoo/odoo#270743 Forward-Port-Of: odoo/odoo#267757
This update resolves an error that occurred when reloading chart templates after removing account codes. The recent change allowed for optional account codes, but the reload process incorrectly attempted to match them, leading to a 'bool' type error. This fix ensures that accounts without codes are handled correctly during the reload process, preventing the error and maintaining chart template functionality.
Original PR description
Currently, an error occurs when reloading a chart template. Steps to Reproduce: - Install the `Accounting` module with demo data. - Go to `Accounting` > `Configuration` > `Accounting` > `Chart of…
Currently, an error occurs when reloading a chart template. Steps to Reproduce: - Install the `Accounting` module with demo data. - Go to `Accounting` > `Configuration` > `Accounting` > `Chart of Accounts`. - Open the `Current Assets` account and remove its account code. - Go to `Settings` and click the `Reload` button under `Fiscal Localization`. `TypeError: expected string or bytes-like object, got 'bool'` After this [recent commit], account codes became optional and can be removed. As a result, when the code is removed from the Fixed Assets account and the chart template is reloaded, the reload process tries to match template accounts [1] with existing accounts [2]. It retrieves the existing account using its id [3] and correctly finds the account record, but its code field is False. Later, when the reload logic compares the existing account code with the template account code, it attempts to use account code in a regular expression, which raises error [4]. This commit ensures that accounts without a code are treated as having a non-matching code. [recent commit]: https://github.com/odoo/odoo/commit/c3313b336b9f1305c363097745926f2bdf61e277 [1]- https://github.com/odoo/odoo/blob/b04678b130438d5ee1605fadb9ca567930691fdf/addons/account/models/chart_template.py#L1153-L1155 [2]: https://github.com/odoo/odoo/blob/b04678b130438d5ee1605fadb9ca567930691fdf/addons/account/models/chart_template.py#L332 [3]: https://github.com/odoo/odoo/blob/b04678b130438d5ee1605fadb9ca567930691fdf/addons/account/models/chart_template.py#L435 [4]- https://github.com/odoo/odoo/blob/b04678b130438d5ee1605fadb9ca567930691fdf/addons/account/models/chart_template.py#L439 sentry-7556472625 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update resolves an issue where POS orders with tracked products and GS1 barcodes would fail to validate due to an incorrect search for existing lots. The fix ensures that lot names, even when partially matching GS1 barcodes, are correctly identified, preventing duplicate lot creation errors. This improves the reliability of POS order processing.
Original PR description
When validating a POS order containing a product tracked by lots, an error about duplicate lot numbers is raised if the lot name can be read as a GS1 barcode (e.g. "10156": "10" is the GS1…
When validating a POS order containing a product tracked by lots, an error about duplicate lot numbers is raised if the lot name can be read as a GS1 barcode (e.g. "10156": "10" is the GS1 Application Identifier for Batch/Lot) while the company uses a GS1 nomenclature and the Barcode app is installed.
Steps to reproduce:
-------------------
* Install Barcode, POS and Inventory, enable lots & serial numbers
* Set the barcode nomenclature to "Default GS1 Nomenclature"
* Create a product tracked by lots and a lot named "10156" (any name starting with "10"), set an on-hand quantity for it with this lot
* On the "PoS Orders" operation type, enable both "Create New" and "Use Existing ones" for lots/serial numbers
* In POS, sell the product with lot "10156" and validate the order
> Observation:
The order fails to validate with a duplicate lot number error: the search for existing lots does not find lot "10156", so the POS tries to create it again and hits the unique constraint on stock.lot.
Why the fix:
------------
With stock_barcode installed, `stock.lot._search` preprocesses any domain on `name` with `_preprocess_gs1_search_args` so that scanned GS1 barcodes can match lot records. The lot names sent at order validation by `_create_production_lots_for_pos_order` are real lot names coming from the order lines, not scanned barcodes, but "10156" is decomposable as a valid GS1 lot ("10" + "156"), so the search domain became `('name', '=', '156')` and missed the existing lot. Skip the GS1 preprocessing in that search with the existing `skip_preprocess_gs1` context key, as already done in `product` and `stock`.
opw-6274744
Forward-Port-Of: odoo/odoo#269787This update resolves an issue where the system incorrectly forecasted expiring perishable products, leading to unnecessary reordering recommendations. The fix clarifies how the system handles expiration dates, ensuring accurate stock availability and preventing an endless replenishment cycle. This improves inventory management and reduces potential waste.
Original PR description
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install…
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install Inventory - Go to settings > Enable 'Lots and Serial Numbers' and 'Expiration Dates' - Create a product 'Vegetable Oil' that is tracked by lots. - Enable Expiration date in the 'Inventory' Section and set Removal date to 2. - Create a receipt for 'Vegetable oil' with Demand 10 and mark it as todo - Details > Set 'Expiration Date' into the future > Set a Lot Number and Save - Validate the receipt - Open Product Form for Vegetable oil > Reordering Rules. - Create a new Reordering rule with Min 5 and Max 10 and save. ## Observed Behavior: The forecasted quantity is calculated as zero, resulting in a quantity to order of 10, even though no replenishment is actually required. The product already satisfies the maximum quantity defined on the reordering rule, and there is no existing demand, as there are no delivery orders or sales order reservations for the product. **Why this is an issue:** When the user navigates from the product form to the On Hand Quantity view to verify the stock situation, the forecasted quantity is shown as 10. This is inconsistent with the value displayed on the reordering rule, creating confusion and making it difficult to understand the actual inventory status. In addition, if the product is configured with a Buy route, running the `Procurement: Run Scheduler` action repeatedly generates new purchase orders for the perishable product. Even if the generated purchase orders are cancelled or completed, since newly purchased stock will also have an expiration date. As a result, the same incorrect forecast calculation occurs again, causing the scheduler to continuously create new purchase orders and leading to an endless replenishment cycle if they expire within horizon days. ## Root cause: When an orderpoint is created or updated, `_compute_qty_to_order` [1] is triggered. This method calls `_compute_qty_to_order_computed` [2] which accesses the forecasted quantity, causing its compute method to called. The forecast computation retrieves context from `_get_product_context` as seen in [3], where the lead horizon date (route lead time + horizon days configured in settings) is passed as `to_date` at [4]. It then reads the product's `virtual_available` quantity using the orderpoint context at [5]. This ultimately invokes `_compute_quantities`, which delegates the calculation of `virtual_available` to `_compute_quantities_dict`, as shown in [6]. As a result, `max_date` is set to the lead horizon date at [7] (for example, one year in the future). Since the product expires before that date, it is included in `expired_unreserved_quant_res`, causing `virtual_available` to be reduced to zero at [8]. Consequently, the forecasted quantity also becomes zero at [9]. Because the forecasted quantity falls below the orderpoint's minimum quantity, a replenishment is incorrectly triggered. **Why did this issue not occur in previous versions?** This behavior was introduced by [commit](https://github.com/odoo/odoo/commit/8ba2c1e38b636c567511139c5e19b7189430a182 ), which fixed the calculation of fresh(unexpired) quantity displayed in the stock availability widget on sales order lines. Which expects that `scheduled_date` (typically the committed delivery date or expected delivery date, including leadtime) is passed to `read_qties` at [10] , which stores it in the context using the `to_date` key. However, the same `to_date` key is also used by the orderpoint horizon-days logic. This overlap causes the expiration-aware quantity computation to use the horizon date instead of the intended `with_expiration` date , leading to incorrect forecast calculations and the unexpected replenishment behavior described above. [1]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L394-L396 [2]-https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L417-L427 [3]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L374-L381 [4]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L484-L491 [5]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L386 [6]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L152-L154 [7]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L215-L218 [8]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L260-L262 [9]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L391 [10]- https://github.com/odoo/odoo/blob/afa2b6b7b47d6420146ceb6dad897405aa92c682/addons/sale_stock/models/sale_order_line.py#L129 ## Solution It does not make sense to trigger replenishment for perishable products solely because they are expected to expire before the horizon date. Even when the available quantity already satisfies the maximum quantity defined on the reordering rule, the current logic forecasts those products as unavailable in advance of their expiration. Instead, products should only be excluded from the forecast once they have actually expired, or when they must be removed according to the original expiration-handling logic. To achieve this, an additional context key can be introduced to distinguish calls originating from the forecast availability widget from SO line. When the computation is performed for the forecast widget, the existing `to_date` context key should continue to be used so that availability is evaluated at the requested future date. For all other flows, including orderpoint calculations, the system should rely on the `with_expiration` context key instead. This preserves the original behavior, where only already-expired quantities (or quantities that must be removed due to expiration rules) are excluded from availability calculations, preventing incorrect replenishment recommendations for perishable products. opw-6200644 Forward-Port-Of: odoo/odoo#268223
This update resolves an issue where reducing the PO quantity after a partial receipt in multi-step warehouses incorrectly updated the remaining backorder demand. The fix ensures accurate quantity calculations by considering the current move status, preventing overestimation of required units.
Original PR description
**Issue** Reducing the PO quantity after performing a partial receipt, in multi-step receipts warehouse can incorrectly update the remaining receipt quantity. **Steps to reproduce** - Setup 2-route…
**Issue** Reducing the PO quantity after performing a partial receipt, in multi-step receipts warehouse can incorrectly update the remaining receipt quantity. **Steps to reproduce** - Setup 2-route receipt warehouse (Inventory > Configuration > Warehouse Management > Warehouses) - Create a PO for 35 units and confirm it - Click on receive products, set received quantity to 10 and create a backorder - Validate the next transfer - Go back to the PO and change the quantity to 20 - Check the receipt demand -> The backorder picking demand become 35 instead of 10 **Cause** Updating the quantity of a purchase order line, also updates the related picking: https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L120 It updates the picking associated to the backorder since the other one is done: https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L185-L187 https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L197 This ultimately calls: https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L228 To compute the new demand for the picking, it retrieves the `move_dest`: https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L240 To compute `qty_to_push`: https://github.com/odoo/odoo/blob/5fc1e34d174f7f61d692d086d0ff65fbfc72b013/addons/purchase_stock/models/purchase_order_line.py#L247-L249 However, since we are in a 2-route receipt setup, `move_dest` is the move from Input to stock for the done picking. Thus, `qty_to_push` is `20 - 10 = 10` instead of `20 - 35 = -15` **Solution** The previous logic assumes a pull flow, where downstream (move_dest_ids) quantities are always up-to-date and can be used as the source of truth to recompute demand. In push flows (e.g., multi-step receipts), this assumption does not hold. To fix this, we instead base the computation on the quantity of the current moves (qty) if nothing has to be attached. **Additional information** Known limitation: this does not address inconsistencies in return flows. When there're returns, units define in the pol and the one define in the sum of the picking can diverge, thus this pr won't fix that. opw-5512172 Forward-Port-Of: odoo/odoo#269666 Forward-Port-Of: odoo/odoo#248626
17 changes
Resolved issues and error corrections
This fix resolves an issue preventing the 'Send to SII' option from appearing on Chilean vendor bills. The update adjusts internal code to correctly display this button when a DTE is generated, ensuring invoices can be properly transmitted to the SII (Servicio de Impuestos Internos) as required by Chilean regulations. This improves compliance and streamlines the invoicing process.
Original PR description
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF…
**Steps to reproduce:** * Install the **l10n_cl_edi** module. * Go to **Accounting → Configuration → CAFs**, create a new CAF, and upload a valid CAF [XML](https://www.odoo.com/mail/message/1097235975) file. * Create a new **Purchase Journal** with **Use Documents** enabled. * Create a vendor bill using this journal. * Set the **Document Type** to **46 - Liquidación-Factura Electrónica**. * Confirm the vendor bill. **Observed behavior:** * The Send button is not visible on the confirmed vendor bill despite the DTE being generated and `l10n_cl_dte_status` being set to `not_sent`. **Cause:** * `_compute_display_send_button` in `account` only returns `True` for sale documents (`is_sale_document()`), so the "Send" button — which opens the Send & Print dialog containing the "Send to SII" option — was never shown on vendor bills. * `_get_move_constraints` in `account.move.send` unconditionally adds a `not_sale_document` constraint for non-sale documents, blocking the Send & Print dialog from processing vendor bills even if the button were visible. * The cron's `cron_run_sii_workflow` only processes moves with `l10n_cl_dte_status = 'ask_for_status'`, skipping moves still in `not_sent` state. **Fix:** * Override `_compute_display_send_button` in `l10n_cl_edi` to also show the "Send" button on posted moves with `l10n_cl_dte_status == 'not_sent'`, matching the pattern used by `l10n_br_edi`. * Override `_get_move_constraints` in `l10n_cl_edi` to remove the `not_sale_document` constraint for Chilean purchase documents with `not_sent` status, matching the pattern used by `l10n_br_edi`. **REF** During this [refactor](https://github.com/odoo/enterprise/pull/103427/changes/f5617ecf7584cf019897408df94b002622f48d9d), these two methods were inadvertently missed and were not overridden opw-6300571
This update resolves a crash that occurred when users manually corrected bank statement lines within the Odoo Enterprise system. The issue stemmed from a missing context key during record creation, leading to incorrect journal assignments. This fix ensures bank statement lines are created with the correct journal, preventing data inconsistencies and system instability.
Original PR description
When the manual correction tool was used to fill in the lines, we weren't passing the active context when creating the new records. In the case of bank statements, it could be an issue as the `default_journal_id` key is expected to be present to set the correct journal on the newly created bank statement line. Without this key in the context, it would default to the first journal with a valid type (see function `_search_default_journal`). If the journal found this way didn't match the current journal, a crash would occur when modifying the newly created lines. opw-[6294117](https://www.odoo.com/odoo/unassigned-tasks/6294117) Forward-Port-Of: odoo/enterprise#121032 Forward-Port-Of: odoo/enterprise#120745
This update resolves an error that prevented users from sending SMS messages to website visitors. The change updated how the system accesses visitor phone numbers, ensuring compatibility after a recent data restructuring. This ensures the SMS functionality works correctly for all visitors.
Original PR description
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another…
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another `Form` block. - Configure the form action to `Create an Opportunity` and `save`. - Fill in the required fields, including phone number and `Submit` the form. - Go to `Website` > `Reporting` > `Visitors` and click the `SMS` button on the visitor record. `AttributeError: 'website.visitor' object has no attribute 'phone'` After [this commit], which removed the mobile field from res.partner along with all related views, then it was updated to access the phone number from the website visitor. When a user creates an opportunity through the website and then tries to send an sms from the corresponding visitor record, it raises an error [1] because it attempts to access the phone field on website.visitor. when an anonymous (non-logged-in) user creates an opportunity, clicking the sms button on the corresponding visitor record triggers error here [2]. This commit ensures that the correct mobile field is accessed from the website visitor. [this commit]: https://github.com/odoo/odoo/commit/6b820eb6fc6f782ba6a83d605d87b4a1dd2a87be [1]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L13 [2]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L20 sentry-7550340909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270118
This update resolves an issue where text entered into a SelectMenu field was intermittently being cleared, causing data entry problems. The fix ensures the input field accurately reflects user input during asynchronous data updates, improving data accuracy and user experience. It also corrects a related issue with the SelectMenu clearing when empty.
Original PR description
Step to reproduce: 1. Install `website_link` 2. Open Website > Site > Link Tracker 3. Type in text into any pre-defined field (Campaign, Medium, Source) 4. Observe that the input is not showing all…
Step to reproduce: 1. Install `website_link` 2. Open Website > Site > Link Tracker 3. Type in text into any pre-defined field (Campaign, Medium, Source) 4. Observe that the input is not showing all the typed characters Issue: - It's randomly removing characters, for example, type "1234567890" and observe Cause: - SelectMenu updates its internal searchValue only inside the debounced onInput handler `debouncedOnInput`. When an autocomplete callback reloads choices before that debounce fires, the component rerenders with a stale searchValue and writes that outdated value back into the controlled input, overwriting newer characters already typed by the user. Solution: - Update searchValue immediately on every raw input event and keep only the search callback debounced. - Also reset searchValue to null when a required single-select is blurred while empty, so the input falls back to the selected choice label instead of staying visually cleared. opw-6000540 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#267974 Forward-Port-Of: odoo/odoo#255819
This update resolves an issue where users with limited accounting permissions couldn't properly reconcile bank statements, leading to invoices being incorrectly marked as fully paid. The fix ensures accurate reconciliation by safely bypassing a permission check during the automated process, maintaining data auditability and preventing incorrect Account Receivable line additions.
Original PR description
### Issue When you have an invoice partially paid via a method using an Outstanding Account, the payment can be kept open, leaving the invoice considered as partially paid If a user with only…
### Issue When you have an invoice partially paid via a method using an Outstanding Account, the payment can be kept open, leaving the invoice considered as partially paid If a user with only "Invoicing & Banks" rights tries to reconcile a Bank Statement with the same partner, amount, and the invoice name as the memo, the automatic reconciliation fails to properly match the payment Instead, the invoice is incorrectly considered as Fully Paid with an unwanted extra Account Receivable line added ### Cause When a new Bank Statement is created, `_try_auto_reconcile_statement_lines()` is called and matches the outstanding credit, which invokes `set_line_bank_statement_line()` In 19.0, this function creates a balancing line and triggers `move._compute_checked()` to update dependencies Especially `_compute_is_reconciled` But checked as been replaced by `review_state` This FW port will use an update on the `review_state` instead of the `checked` A user with "Invoicing & Banks" rights lacks the `account.group_account_user` group, meaning the move is not marked as `reviewed`, preventing dependencies from computing correctly Consequently, the statement line's `amount_residual` is not cleared and the line is not removed from `remaining_st_line_ids` Later in the process, `_try_auto_reconcile_statement_lines()` is called again with `with_user(SUPERUSER_ID)` Because the payment matching was never finalized in the previous step, the engine fallback matches against the full invoice, adding an incorrect Account Receivable line to close it ### Steps to reproduce - Install `accountant` - Go to Accounting / Configuration / Accounting / Journals - Open the Bank, under Incoming Payments tab, set the Manual Payment method's Outstanding Receipts account to 101403 Outstanding Receipts - Update the Demo user's accounting rights to Invoicing & Banks - Log in with the Demo user - Create and confirm an invoice for Acme Corporation (Amount: $1100) - Register a payment on the invoice (Amount: $500, Keep open) - Copy the invoice name - Open the Bank Reconciliation widget from the Accounting Dashboard - Create and add a new Bank Statement Line (Label: Invoice name, Partner: Acme Corporation, Amount: $500) Before the fix, an unexpected Account Receivable line is created and the invoice is marked as Fully Paid ### Notes Instead of processing the entire block under SUPERUSER_ID, which would hide the creator identity in logs and chatter, the context key `skip_account_review_check=True` is injected during the automated statement line reconciliation This safely bypasses the group check inside `_is_user_able_to_review` for this specific automated flow A fallback using `.with_user(SUPERUSER_ID)` is already implemented twice within the same `_try_auto_reconcile_statement_lines` method for this specific use case, but avoiding it here preserves data auditability opw-6077137 Forward-Port-Of: odoo/odoo#270080
This update resolves an issue where users with restricted accounting rights incorrectly marked invoices as fully paid, leading to incorrect Account Receivable entries. The fix ensures automatic bank reconciliation works correctly for these users by safely bypassing a group check during the reconciliation process, maintaining data auditability.
Original PR description
### Issue When you have an invoice partially paid via a method using an Outstanding Account, the payment can be kept open, leaving the invoice considered as partially paid If a user with only…
### Issue When you have an invoice partially paid via a method using an Outstanding Account, the payment can be kept open, leaving the invoice considered as partially paid If a user with only "Invoicing & Banks" rights tries to reconcile a Bank Statement with the same partner, amount, and the invoice name as the memo, the automatic reconciliation fails to properly match the payment Instead, the invoice is incorrectly considered as Fully Paid with an unwanted extra Account Receivable line added ### Cause When a new Bank Statement is created, `_try_auto_reconcile_statement_lines()` is called and matches the outstanding credit, which invokes `set_line_bank_statement_line()` This function creates a balancing line and triggers `move._compute_checked()` to update dependencies However, `move.checked` requires `_is_user_able_to_review()` to be True A user with "Invoicing & Banks" rights lacks the `account.group_account_user` group, meaning the move is not marked as checked, preventing dependencies from computing correctly Consequently, the statement line's `amount_residual` is not cleared and the line is not removed from `remaining_st_line_ids` Later in the process, `_try_auto_reconcile_statement_lines()` is called again with `with_user(SUPERUSER_ID)` Because the payment matching was never finalized in the previous step, the engine fallback matches against the full invoice, adding an incorrect Account Receivable line to close it ### Steps to reproduce - Install `accountant` - Go to Accounting / Configuration / Accounting / Journals - Open the Bank, under Incoming Payments tab, set the Manual Payment method's Outstanding Receipts account to 101403 Outstanding Receipts - Update the Demo user's accounting rights to Invoicing & Banks - Log in with the Demo user - Create and confirm an invoice for Acme Corporation (Amount: $1100) - Register a payment on the invoice (Amount: $500, Keep open) - Copy the invoice name - Open the Bank Reconciliation widget from the Accounting Dashboard - Create and add a new Bank Statement Line (Label: Invoice name, Partner: Acme Corporation, Amount: $500) Before the fix, an unexpected Account Receivable line is created and the invoice is marked as Fully Paid ### Notes Instead of processing the entire block under SUPERUSER_ID, which would hide the creator identity in logs and chatter, the context key `skip_account_review_check=True` is injected during the automated statement line reconciliation This safely bypasses the group check inside `_is_user_able_to_review` for this specific automated flow A fallback using `.with_user(SUPERUSER_ID)` is already implemented twice within the same `_try_auto_reconcile_statement_lines` method for this specific use case, but avoiding it here preserves data auditability opw-6077137 Forward-Port-Of: odoo/enterprise#120217 Forward-Port-Of: odoo/enterprise#118023
This update improves the speed of queries related to document access permissions, specifically for the 'my/counters' route which is frequently used. By switching to a subquery, the system now utilizes an index more effectively, resulting in a significant reduction in query response times for portal and internal users.
Original PR description
The '/my/counters' route is hit a lot of times on big databases like odoo.com One thing it does is a `self.env['documents.document].search_count([])` With this commit, we use a subquery for the…
The '/my/counters' route is hit a lot of times on big databases like odoo.com
One thing it does is a `self.env['documents.document].search_count([])`
With this commit, we use a subquery for the folder access instead of the current LEFT JOIN.
This ok since the number of folders is typically small compared to regular documents and the query is fast since it can use the index on 'type'
Before as portal user
------
2x Seq Scan
```
Aggregate (cost=1900290.73..1900290.74 rows=1 width=8) (actual time=282.271..282.276 rows=1 loops=1)
Buffers: shared hit=66629
-> Hash Left Join (cost=41649.94..1900044.55 rows=98472 width=0) (actual time=184.202..282.267 rows=3 loops=1)
Hash Cond: (documents_document.folder_id = documents_document__folder_id.id)
Filter: ((hashed SubPlan 2) OR ((documents_document.owner_id = 6) AND ((documents_document.shortcut_document_id IS NULL) OR (documents_document.shortcut_document_owner_id = 6))) OR (((documents_document.access_via_link)::text = ANY ('{edit,view}'::text[])) AND (documents_document.folder_id IS NOT NULL) AND ((hashed SubPlan 4) OR ((documents_document__folder_id.owner_id = 6) AND ((documents_document__folder_id.shortcut_document_id IS NULL) OR (documents_document__folder_id.shortcut_document_owner_id = 6)))) AND (documents_document.is_access_via_link_hidden IS NOT TRUE)))
Rows Removed by Filter: 28085
Buffers: shared hit=66629
-> Seq Scan on documents_document (cost=0.00..1857903.54 rows=187073 width=26) (actual time=0.022..109.288 rows=28088 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))) OR (((access_via_link)::text = ANY ('{edit,view}'::text[])) AND (folder_id IS NOT NULL) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 342576
Buffers: shared hit=33313
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.008..0.009 rows=0 loops=2)
Buffers: shared hit=6
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.008..0.008 rows=0 loops=2)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:16:38'::timestamp without time zone))
Buffers: shared hit=6
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
-> Hash (cost=37016.64..37016.64 rows=370664 width=16) (actual time=164.521..164.521 rows=370664 loops=1)
Buckets: 524288 Batches: 1 Memory Usage: 17824kB
Buffers: shared hit=33310
-> Seq Scan on documents_document documents_document__folder_id (cost=0.00..37016.64 rows=370664 width=16) (actual time=0.005..100.491 rows=370664 loops=1)
Buffers: shared hit=33310
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.003..0.003 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.002..0.003 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:16:38'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Planning:
Buffers: shared hit=69
Planning Time: 1.708 ms
Execution Time: 282.344 ms
```
After as portal user
-----
Only 1x Seq Scan
```
Aggregate (cost=2004948.33..2004948.34 rows=1 width=8) (actual time=116.161..116.165 rows=1 loops=1)
Buffers: shared hit=37942
-> Seq Scan on documents_document (cost=145660.16..2004490.36 rows=183187 width=0) (actual time=24.635..116.155 rows=3 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))) OR (((access_via_link)::text = ANY ('{edit,view}'::text[])) AND (hashed SubPlan 5) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 370661
Buffers: shared hit=37942
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.008..0.009 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.008..0.008 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:15:10'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
SubPlan 5
-> Index Scan using documents_document__type_index on documents_document documents_document_1 (cost=0.42..145625.73 rows=13772 width=4) (actual time=11.688..11.689 rows=0 loops=1)
Index Cond: ((type)::text = 'folder'::text)
Filter: ((hashed SubPlan 4) OR ((owner_id = 6) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 6))))
Rows Removed by Filter: 28198
Buffers: shared hit=4629
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.002..0.002 rows=0 loops=1)
Buffers: shared hit=3
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.001..0.002 rows=0 loops=1)
Index Cond: (partner_id = 7)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 10:15:10'::timestamp without time zone))
Buffers: shared hit=3
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (never executed)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Planning:
Buffers: shared hit=56
Planning Time: 1.544 ms
Execution Time: 116.216 ms
```
Before as internal user
--------
```
Aggregate (cost=1902165.43..1902165.44 rows=1 width=8) (actual time=332.919..332.925 rows=1 loops=1)
Buffers: shared hit=69223 read=370
-> Hash Left Join (cost=41649.94..1901908.04 rows=102955 width=0) (actual time=176.179..332.325 rows=10040 loops=1)
Hash Cond: (documents_document.folder_id = documents_document__folder_id.id)
Filter: ((hashed SubPlan 2) OR ((documents_document.owner_id = 1054906) AND ((documents_document.shortcut_document_id IS NULL) OR (documents_document.shortcut_document_owner_id = 1054906))) OR (((documents_document.access_internal)::text = ANY ('{view,edit}'::text[])) AND ((documents_document.company_id = 1) OR (documents_document.company_id IS NULL))) OR (((documents_document.access_via_link)::text = ANY ('{view,edit}'::text[])) AND (documents_document.folder_id IS NOT NULL) AND ((hashed SubPlan 4) OR ((documents_document__folder_id.owner_id = 1054906) AND ((documents_document__folder_id.shortcut_document_id IS NULL) OR (documents_document__folder_id.shortcut_document_owner_id = 1054906))) OR (((documents_document__folder_id.access_internal)::text = ANY ('{view,edit}'::text[])) AND ((documents_document__folder_id.company_id = 1) OR (documents_document__folder_id.company_id IS NULL)))) AND (documents_document.is_access_via_link_hidden IS NOT TRUE)))
Rows Removed by Filter: 27228
Buffers: shared hit=69223 read=370
-> Seq Scan on documents_document (cost=0.00..1859756.86 rows=190950 width=35) (actual time=15.029..155.718 rows=37268 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))) OR (((access_via_link)::text = ANY ('{view,edit}'::text[])) AND (folder_id IS NOT NULL) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 333396
Buffers: shared hit=33931 read=370
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.155..7.920 rows=148 loops=2)
Buffers: shared hit=1612 read=370
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.110..3.448 rows=200 loops=2)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:17:29'::timestamp without time zone))
Buffers: shared hit=192 read=190
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (actual time=0.022..0.022 rows=1 loops=400)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=1420 read=180
-> Hash (cost=37016.64..37016.64 rows=370664 width=25) (actual time=157.822..157.823 rows=370664 loops=1)
Buckets: 524288 Batches: 1 Memory Usage: 21336kB
Buffers: shared hit=33310
-> Seq Scan on documents_document documents_document__folder_id (cost=0.00..37016.64 rows=370664 width=25) (actual time=0.005..96.730 rows=370664 loops=1)
Buffers: shared hit=33310
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.019..0.372 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.005..0.078 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:17:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (actual time=0.001..0.001 rows=1 loops=200)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
Planning:
Buffers: shared hit=69 read=8
Planning Time: 2.116 ms
Execution Time: 333.013 ms
```
After as internal user
--------
```
Aggregate (cost=2006950.17..2006950.18 rows=1 width=8) (actual time=157.117..157.121 rows=1 loops=1)
Buffers: shared hit=39918
-> Seq Scan on documents_document (cost=145798.74..2006482.26 rows=187165 width=0) (actual time=16.595..156.590 rows=10040 loops=1)
Filter: ((active IS TRUE) AND ((hashed SubPlan 2) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))) OR (((access_via_link)::text = ANY ('{view,edit}'::text[])) AND (hashed SubPlan 5) AND (is_access_via_link_hidden IS NOT TRUE))))
Rows Removed by Filter: 360624
Buffers: shared hit=39918
SubPlan 2
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.019..1.016 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access (cost=0.43..105.55 rows=103 width=9) (actual time=0.012..0.262 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:16:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id (cost=0.42..2.44 rows=1 width=9) (actual time=0.004..0.004 rows=1 loops=200)
Index Cond: (id = documents_access.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
SubPlan 5
-> Index Scan using documents_document__type_index on documents_document documents_document_1 (cost=0.42..145763.43 rows=14124 width=4) (actual time=0.429..14.916 rows=4625 loops=1)
Index Cond: ((type)::text = 'folder'::text)
Filter: ((hashed SubPlan 4) OR ((owner_id = 1054906) AND ((shortcut_document_id IS NULL) OR (shortcut_document_owner_id = 1054906))) OR (((access_internal)::text = ANY ('{view,edit}'::text[])) AND ((company_id = 1) OR (company_id IS NULL))))
Rows Removed by Filter: 23573
Buffers: shared hit=5617
SubPlan 4
-> Nested Loop (cost=0.85..357.41 rows=99 width=4) (actual time=0.007..0.390 rows=148 loops=1)
Buffers: shared hit=991
-> Index Scan using documents_access__partner_id_index on documents_access documents_access_1 (cost=0.43..105.55 rows=103 width=9) (actual time=0.003..0.074 rows=200 loops=1)
Index Cond: (partner_id = 1800102)
Filter: ((expiration_date IS NULL) OR (expiration_date >= '2026-06-18 12:16:29'::timestamp without time zone))
Buffers: shared hit=191
-> Index Scan using documents_document_pkey on documents_document documents_access__document_id_1 (cost=0.42..2.44 rows=1 width=9) (actual time=0.001..0.001 rows=1 loops=200)
Index Cond: (id = documents_access_1.document_id)
Filter: (((access_via_link)::text <> 'none'::text) OR ((documents_access_1.role)::text = ANY ('{view,edit}'::text[])))
Rows Removed by Filter: 0
Buffers: shared hit=800
Planning:
Buffers: shared hit=56
Planning Time: 1.569 ms
Execution Time: 157.171 ms
```
portal user
before https://explain.dalibo.com/plan/e1e755fg7bb26a21
after https://explain.dalibo.com/plan/hb5fa1d201ff164g
internal user with few documents access
before https://explain.dalibo.com/plan/f753bf2aa244dg63
after https://explain.dalibo.com/plan/538dg5ecb120ch84
internal user with *lots* of documents access
before https://explain.dalibo.com/plan/cf76h84537f7ge4a
after https://explain.dalibo.com/plan/45317a5e3168c5bcThis update corrects a technical issue in the UBL BIS3 generation process for Debit Notes. Previously, the system incorrectly used 'LegalMonetaryTotal' instead of the required 'RequestedMonetaryTotal' node, leading to errors in UBL file creation. This change ensures compliance with BIS3 standards for Debit Note UBL exports.
Original PR description
Problem --------- Debit note should have the node `RequestedMonetaryTotal` instead of `LegalMonetaryTotal`. Solution --------- Add a conditional depending on the document type. opw-6295897 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270238
This update fixes an issue where product costs weren't consistently converted to the POS currency during product loading. Previously, product costs were stored in separate currencies, leading to potential inaccuracies in sales calculations. This change ensures all product costs are correctly converted, improving the reliability of pricing in the Point of Sale system.
Original PR description
When loading products in the POS, both the sale price and the cost were converted to the POS currency using `currency_id`. However a product stores its sale price and its cost in two potentially different currencies: `currency_id` (company currency, falling back to the main company) and `cost_currency_id` (company currency, falling back to the current company). opw-6297452 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269829
This update fixes an issue where preparation prints weren't showing all items after transferring an order to a shared table. Previously, only the items on the destination table were printed. Now, when orders are merged, the preparation prints accurately reflect all items from both orders, ensuring accurate kitchen workflows.
Original PR description
When moving an order (Order A) to a table that already has an order (Order B), the merged order only reprints Order B's products. The products from Order A are missing from the preparation reprint.…
When moving an order (Order A) to a table that already has an order (Order B), the merged order only reprints Order B's products. The products from Order A are missing from the preparation reprint. Steps to reproduce: ------------------- * Open a POS session on a Restaurant POS * Create an order (Order A) for Table 1 * Create a second order (Order B) for Table 2 * Transfer/Merge Order A to Table 2 * Reprint the preparation order > Observation: Only the products that were already on Table 2 (Order B) appear on the reprint. Products from Order A are missing. Why the fix: ------------ mergeOrders correctly transfers kitchen history (last_order_preparation_change.lines) via handlePreparationHistory, but does not update uiState.lastPrints on the destination order. The reprint button uses lastPrints.at(-1) when there are no pending changes, so it only shows the destination order's last print batch — ignoring the merged lines entirely. Implementation: After the merge loop, build a consolidated lastPrints entry from the destination order's last_order_preparation_change.lines (which now contains lines from both orders) and push it onto destOrder.uiState.lastPrints so that reprint reflects the full merged state. opw-6060684 Forward-Port-Of: odoo/odoo#270311 Forward-Port-Of: odoo/odoo#256309
This update optimizes how Odoo searches for documents, specifically addressing a complex query that slowed down searches for documents not marked as 'SHARED'. This change aligns with the performance practices used in our production database, resulting in faster and more efficient document searches for users. It’s a performance improvement focused on the Documents module.
Original PR description
Searching for "not 'SHARED'" results in a very complex query. Our own production DB prefers this implementation, also easier to read. credit: https://github.com/odoo/enterprise/pull/105915#discussion_r2745148099 Task-5893183 Forward-Port-Of: odoo/enterprise#120870
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. The change removes a problematic account mapping, ensuring the exported data aligns with the Odoo Profit & Loss view. This improves data accuracy for reporting to the Luxembourg tax authority.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
A recent upgrade to Odoo 19.2 caused an error when accessing the partner website page. This was due to a change in how website templates are structured. This fix updates the system to align with the new template structure, resolving the error and restoring normal website functionality.
Original PR description
*= website_partnership, website_crm_partner_assign Currently, an error occurs when users try to access the `/partners` website page after upgrading database to saas-19.2. This issue occurs because…
*= website_partnership, website_crm_partner_assign Currently, an error occurs when users try to access the `/partners` website page after upgrading database to saas-19.2. This issue occurs because recent changes introduced in PR [1] added a new template as id `index_layout`. Inside this template, a `t-call` element was using a nested `t-set` element to define `additional_title`. We were referencing this `t-set` element in the XPath of the `index` template to override the value of `additional_title`. However, recent changes removed the `t-set` from the `t-call` and replaced it with a direct variable assignment inside the `t-call`. During the upgrade, the migration script automatically moves the `additional_title` attribute into `t-call` and removes the `t-set` from the `t-call` (see the script and related changes in [2]). As a result, the XPath expression that targets the `t-set` element fails because the referenced element no longer exists, which causes the error. This commit fixes the above issue by adapting the `t-call` element to match the recent changes introduced in [2]. Instead of relying on a nested `t-set` element, it now uses the variable directly as an attribute within the `t-call`, and updates the corresponding attribute child accordingly. [1]: https://github.com/odoo/odoo/commit/711c3baad58f3e0f1dc39cb90eb8176aba91e9dd [2]: https://github.com/odoo/odoo/pull/235469/changes#diff-29ae6f0bcf846a2fcaffc38fdd0d3b19ea328c133ff4dfe18cc9725715f34dd9 sentry-7400315548
This update fixes an issue where self-orders created through kiosks or mobile ordering didn't show the correct table information on the payment screen. The fix ensures that table details are accurately displayed for all self-order transactions, improving the customer experience and order accuracy.
Original PR description
### **Issue** Self-orders created through the mobile QR menu or kiosk were not displaying table information in the Order Info section of the payment screen. ### **Root Cause** The Order Details…
### **Issue** Self-orders created through the mobile QR menu or kiosk were not displaying table information in the Order Info section of the payment screen. ### **Root Cause** The Order Details dialog relied on `order.getTable()` to retrieve table information. However, self-orders only store the table reference through `table_id`, which was not properly handled when rendering the dialog, resulting in missing table information. ### **Solution** This PR introduces the following changes: * Add a dedicated `getTableInfo()` helper in `pos_restaurant` to retrieve table information from the order. * Use `getTableInfo()` when building the Order Details dialog fields. * Allow self-order flows to provide table information through `table_id`, ensuring table details are displayed correctly. ### **Steps to Reproduce** 1. Enable the following options in POS Configuration: * QR Menu & Ordering * Service at Table * Online Payment 2. Open a self-order from the mobile QR menu 3. Select a table and complete the payment 4. Open the POS session 5. Open the paid self-order 6. Click the **Details** button on the right side ### **Video Reproduction** https://drive.google.com/file/d/138AoJCTF1HNlCGFgk9BRTb0UTai-uXX7/view?usp=sharing ### **Before** Table information was not displayed in the **Order Info** section for self-orders. ### **After** Table information is now correctly displayed for self-orders. opw-6179516 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update resolves an issue where DIAN XML files were being rejected due to incorrect payment calculations. The fix combines payment amounts into a single tag, ensuring accurate data transmission to the DIAN and preventing errors related to negative payment amounts.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update corrects a bug where the system incorrectly forecasted expiring perishable products as unavailable, leading to unnecessary reordering. Now, the system accurately reflects stock levels and avoids triggering replenishment for perishable goods until they actually expire, resolving confusion for users.
Original PR description
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install…
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install Inventory - Go to settings > Enable 'Lots and Serial Numbers' and 'Expiration Dates' - Create a product 'Vegetable Oil' that is tracked by lots. - Enable Expiration date in the 'Inventory' Section and set Removal date to 2. - Create a receipt for 'Vegetable oil' with Demand 10 and mark it as todo - Details > Set 'Expiration Date' into the future > Set a Lot Number and Save - Validate the receipt - Open Product Form for Vegetable oil > Reordering Rules. - Create a new Reordering rule with Min 5 and Max 10 and save. ## Observed Behavior: The forecasted quantity is calculated as zero, resulting in a quantity to order of 10, even though no replenishment is actually required. The product already satisfies the maximum quantity defined on the reordering rule, and there is no existing demand, as there are no delivery orders or sales order reservations for the product. **Why this is an issue:** When the user navigates from the product form to the On Hand Quantity view to verify the stock situation, the forecasted quantity is shown as 10. This is inconsistent with the value displayed on the reordering rule, creating confusion and making it difficult to understand the actual inventory status. In addition, if the product is configured with a Buy route, running the `Procurement: Run Scheduler` action repeatedly generates new purchase orders for the perishable product. Even if the generated purchase orders are cancelled or completed, since newly purchased stock will also have an expiration date. As a result, the same incorrect forecast calculation occurs again, causing the scheduler to continuously create new purchase orders and leading to an endless replenishment cycle if they expire within horizon days. ## Root cause: When an orderpoint is created or updated, `_compute_qty_to_order` [1] is triggered. This method calls `_compute_qty_to_order_computed` [2] which accesses the forecasted quantity, causing its compute method to called. The forecast computation retrieves context from `_get_product_context` as seen in [3], where the lead horizon date (route lead time + horizon days configured in settings) is passed as `to_date` at [4]. It then reads the product's `virtual_available` quantity using the orderpoint context at [5]. This ultimately invokes `_compute_quantities`, which delegates the calculation of `virtual_available` to `_compute_quantities_dict`, as shown in [6]. As a result, `max_date` is set to the lead horizon date at [7] (for example, one year in the future). Since the product expires before that date, it is included in `expired_unreserved_quant_res`, causing `virtual_available` to be reduced to zero at [8]. Consequently, the forecasted quantity also becomes zero at [9]. Because the forecasted quantity falls below the orderpoint's minimum quantity, a replenishment is incorrectly triggered. **Why did this issue not occur in previous versions?** This behavior was introduced by [commit](https://github.com/odoo/odoo/commit/8ba2c1e38b636c567511139c5e19b7189430a182 ), which fixed the calculation of fresh(unexpired) quantity displayed in the stock availability widget on sales order lines. Which expects that `scheduled_date` (typically the committed delivery date or expected delivery date, including leadtime) is passed to `read_qties` at [10] , which stores it in the context using the `to_date` key. However, the same `to_date` key is also used by the orderpoint horizon-days logic. This overlap causes the expiration-aware quantity computation to use the horizon date instead of the intended `with_expiration` date , leading to incorrect forecast calculations and the unexpected replenishment behavior described above. [1]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L394-L396 [2]-https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L417-L427 [3]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L374-L381 [4]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L484-L491 [5]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L386 [6]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L152-L154 [7]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L215-L218 [8]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L260-L262 [9]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L391 [10]- https://github.com/odoo/odoo/blob/afa2b6b7b47d6420146ceb6dad897405aa92c682/addons/sale_stock/models/sale_order_line.py#L129 ## Solution It does not make sense to trigger replenishment for perishable products solely because they are expected to expire before the horizon date. Even when the available quantity already satisfies the maximum quantity defined on the reordering rule, the current logic forecasts those products as unavailable in advance of their expiration. Instead, products should only be excluded from the forecast once they have actually expired, or when they must be removed according to the original expiration-handling logic. To achieve this, an additional context key can be introduced to distinguish calls originating from the forecast availability widget from SO line. When the computation is performed for the forecast widget, the existing `to_date` context key should continue to be used so that availability is evaluated at the requested future date. For all other flows, including orderpoint calculations, the system should rely on the `with_expiration` context key instead. This preserves the original behavior, where only already-expired quantities (or quantities that must be removed due to expiration rules) are excluded from availability calculations, preventing incorrect replenishment recommendations for perishable products. opw-6200644 Forward-Port-Of: odoo/odoo#268223
This update significantly reduces the memory usage and processing time when loading large General Ledger reports. By optimizing how display names are retrieved, the system now handles complex reports more efficiently, leading to a smoother user experience. This change addresses a performance bottleneck impacting report loading speed.
Original PR description
### Issue Loading a large General Ledger (e.g., during an "Unfold All" action) and retrieving display names for thousands of journal lines (`account.move.line`) causes excessive memory and…
### Issue Loading a large General Ledger (e.g., during an "Unfold All" action) and retrieving display names for thousands of journal lines (`account.move.line`) causes excessive memory and performance overhead. Profiling with `memray` showed that one of the main memory hotspots was located in `custom_label_builder`. **Previous behavior:** Accessing `record.display_name` in a loop without an explicit `fetch()` call triggered lazy computation of the field via `_compute_display_name()`. When the compute method accessed stored dependency fields (such as `name`, `ref`, `move_id`), each cache miss went through `_fetch_field()`, which greedily loaded **all fields sharing the same prefetch group** on the model, far beyond the dependencies of `display_name` alone. This caused the ORM cache to be filled with many unnecessary stored fields for every record in the prefetch set. --- ### Dataset Volume The performance metrics were captured using a dataset consisting of: * **455,694** Journal Items (`account.move.line`) * **19,947** Journal Entries (`account.move`) --- ### Solution Add a single `fetch(['display_name'])` call on the browsed recordset. By calling `fetch(['display_name'])` upfront, the ORM goes through `_determine_fields_to_fetch(['display_name'])`, which walks only the declared `field_depends` of `display_name` and fetches **only those specific stored fields**. nothing more. --- ### Impact & Results | Metric | Before Optimization | After Optimization | Change / Note | | :--- | :--- | :--- | :--- | | **Peak Memory** | ~856 MB | ~223 MB | ~74% reduction | | **Execution Time** | 2.48s | 2.13s | About the same time with multiple tries | OPW-6275158 Forward-Port-Of: odoo/enterprise#121020
15 changes
Resolved issues and error corrections
This update resolves an issue where mass email campaigns were inadvertently using users' personal email servers, causing delays and errors. The changes now ensure that personal servers are excluded from the selection process for mass mailings, preventing campaigns from getting stuck and improving reliability. This ensures consistent email sending functionality.
Original PR description
A personal outgoing mail server is an `ir.mail_server` that belongs to one user. The system only lets that user send through it. Mass mailings do not always respect this, which can cause a few…
A personal outgoing mail server is an `ir.mail_server` that belongs to one user. The system only lets that user send through it. Mass mailings do not always respect this, which can cause a few problems: 1. Admins cannot duplicate a personal server. The copy keeps the same owner, and the rule that says one user can own only one server stops the save. 2. In *Email Marketing > Settings*, the "Dedicated Server" picker offers every server, even personal ones. If an admin picks a personal one, all campaigns get stuck. The cron job runs as Odoobot, the personal server rejects it, and the mailing stays in the queue. 3. When no dedicated server is set, the fallback selection can still land on a personal server (for example because its `from_filter` matches the sender). The cron sends through it and gets rejected. One commit per problem: 1. **mail**: duplicating a personal server now produces a copy with no owner. 2. **mass_mailing**: the picker in the settings hides personal servers. Setting an owner on a server that is already used for mass mailing now raises a clear error that names the campaign blocking the change. 3. **mass_mailing**: personal servers are skipped when the fallback selection runs, so only shared servers are considered. opw-6086077 Forward-Port-Of: odoo/odoo#261537
This update significantly speeds up appointment scheduling, particularly when managing multiple resources like tables in a restaurant. The change streamlines the process of checking resource availability, reducing load times by up to 70% for complex scenarios. This results in a faster and more responsive user experience.
Original PR description
In the current code, for each slot, and for each "available" resource, we check if the resource is available on the slot, based on availability values. Then, we check the remaining capacity of that…
In the current code, for each slot, and for each "available" resource, we check if the resource is available on the slot, based on availability values. Then, we check the remaining capacity of that resource. Also, linked resources information is added when computing the original resource remaining capacity. If many linked resources exist, this will be done several times and is not useful. This commit makes that loop disappear. We now check all resources at once in terms of availability, and linked resources that could be selected (in the appointment resources, in the slot resources (if any restricted resource)) at the same time. Then, the total capacity is the sum of the resource remaining capacity and the ones of available linked resources. Therefore, _slot_availability_is_resource_available is renamed to _slot_available_resources, as it now takes more than one resource and returns all resources among 'resources' that are valid on the slot, based on the availability_values, slot restrictions and booking lines. A noticeable difference is mainly seen when using many resources (and linked resources). For instance, a restaurant with a lot of small tables will have their slot availability check much shorter. BENCHMARK, LOCAL (time only, as number of requests does not change) Only appointment installed For a restaurant with - 10 tables of 2 - 5 tables of 2 linked, 2 times - 10 tables of 4 - 2 table of 2 - time then auto assign On loading /appointment/id: ~ 3.1s -> ~ 1.6s On selecting any number of people (1 to 10): [2s, 2.5s] -> [0.6s, 0.8s] Task-4144524 Forward-Port-Of: odoo/enterprise#107711
This update ensures appointment invitations are only sent when an appointment is actually booked or requested, resolving previous issues where invitations were incorrectly triggered. It now correctly sends invitations to new attendees of booked appointments and ensures the correct status changes are logged, improving the reliability of appointment scheduling notifications.
Original PR description
This PR fix three issues related to the sending of the appointment invitations. Each one has its own commit: - Commit 1 sends invitations only if the event either "booked" or "request". Previously they were sent even if the appointment was cancelled. - Commit 2 prevents the sending of regular invitations and always sends appointment invitation to new attendees of existing booked appointments. - Commit 3 sent appointment invitations if the status of an existing event is set "request". It also add the status change in the log as it would have been if it was done at the creation. Community PR: https://github.com/odoo/odoo/pull/260073 Task-6139036 Forward-Port-Of: odoo/enterprise#114304
This update resolves an error that occurred when users attempted to send SMS messages to website visitors. The fix updated the system to correctly access visitor phone numbers, ensuring the SMS functionality works as intended. This prevents a disruption in lead generation and communication.
Original PR description
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another…
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another `Form` block. - Configure the form action to `Create an Opportunity` and `save`. - Fill in the required fields, including phone number and `Submit` the form. - Go to `Website` > `Reporting` > `Visitors` and click the `SMS` button on the visitor record. `AttributeError: 'website.visitor' object has no attribute 'phone'` After [this commit], which removed the mobile field from res.partner along with all related views, then it was updated to access the phone number from the website visitor. When a user creates an opportunity through the website and then tries to send an sms from the corresponding visitor record, it raises an error [1] because it attempts to access the phone field on website.visitor. when an anonymous (non-logged-in) user creates an opportunity, clicking the sms button on the corresponding visitor record triggers error here [2]. This commit ensures that the correct mobile field is accessed from the website visitor. [this commit]: https://github.com/odoo/odoo/commit/6b820eb6fc6f782ba6a83d605d87b4a1dd2a87be [1]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L13 [2]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L20 sentry-7550340909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270118
This update corrects a technical issue where the UBL BIS3 format for Debit Notes was not fully compliant with the BIS3 standard. Specifically, the system was incorrectly using 'LegalMonetaryTotal' instead of the required 'RequestedMonetaryTotal' node. This change ensures accurate UBL BIS3 generation for Debit Notes, improving data consistency and compliance.
Original PR description
Problem --------- Debit note should have the node `RequestedMonetaryTotal` instead of `LegalMonetaryTotal`. Solution --------- Add a conditional depending on the document type. opw-6295897 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270238
This update resolves an issue where milestone deadline dates would disappear from task views after navigating back or refreshing. The fix ensures that milestone deadlines are consistently displayed in task kanban views and task form views, regardless of user navigation.
Original PR description
Steps to reproduce: 1. Open the Project application and open any project. 2. Filter the tasks by milestone (milestone deadlines appear as expected in the kanban view). 3. Open any task form view. 4.…
Steps to reproduce: 1. Open the Project application and open any project. 2. Filter the tasks by milestone (milestone deadlines appear as expected in the kanban view). 3. Open any task form view. 4. Click the browser's back button (or simply refresh the page while on the task kanban view). Issue: Milestone deadline dates disappear from the task Kanban cards and headers after navigating back or reloading. Why this happens: When hitting the browser back button or refreshing, the web client's router state recovery workflow executes (`loadRouterState` -> `loadState` -> `doAction` -> `_executeActWindowAction`). During this flow, `_getActionParams` checks if it can reuse the cached `lastAction`. However, due to a safety condition introduced in commit ab26f95893 to prevent embedded action showing across different projects, the router falls back to generating a fresh action request via `state.action`. This forces `_loadAction` to fetch the action definition from the database. Because the original base action window `act_project_project_2_project_task_all` lacks the `display_milestone_deadline` key inside its default context dictionary, the reloaded view is rendered without the flags required by the frontend to display milestone deadlines. opw-6283514 Forward-Port-Of: odoo/odoo#269781
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. The change removes a problematic account mapping, ensuring the data aligns with the Odoo Profit & Loss view. This ensures accurate reporting for Luxembourg tax compliance.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update ensures that attachments related to incoming invoices from EDI imports (specifically for Italian businesses) are correctly handled. Previously, detaching these attachments caused issues with bulk XML exports. Now, attachments for Italian invoices are preserved to ensure accurate data transfer and compliance with tax regulations.
Original PR description
The feature introduced in odoo/enterprise#78429 allows users to detach attachments from moves, primarily to facilitate the regeneration and re-sending of outgoing XMLs (e.g., sales invoices) without needing to delete the original attachment. However, detaching should not apply to incoming XML attachments on bills that originate from EDI import, as these attachments are the received source document and are never regenerated by the system. Detaching them inadvertently prevents their inclusion in bulk XML exports. An exception exists for Italy: businesses need to send Tax Integration XMLs back to the SdI. In this specific case, detaching the Tax Integration XML is appropriate and ensures the bulk export finds the latest, correct attachment. Ticket [link](https://www.odoo.com/odoo/project.task/5062132) opw-5062132 Forward-Port-Of: odoo/odoo#267892 Forward-Port-Of: odoo/odoo#239701
This update fixes a potential issue where the standard price for products could be incorrectly calculated due to how user access restrictions were handled. The change ensures that standard price updates account for all available inventory, regardless of user permissions, preventing inaccurate pricing. This improves the reliability of product valuation.
Original PR description
`qty_available` is by default not computed with sudo, while the `total_value` is. This means that in `_update_standard_price`, the customer configuration can have a negative impact on the…
`qty_available` is by default not computed with sudo, while the `total_value` is. This means that in `_update_standard_price`, the customer configuration can have a negative impact on the standard_price compute.
For example, if a user create a custom rule so that specific users have access to specific warehouses only, compute `total_value / qty_available` can actually mean `global_total_value / partial_qty_available`, which creates an aberrant standard price.
To fix this issue, the _update_standard_price must be done in sudo.
OPW-6243363
---
## Test result without fix
```
2026-06-17 12:24:50,810 47572 ERROR oes_test_19 odoo.addons.stock_account.tests.test_stockvaluation: FAIL: TestStockValuation.test_update_standard_price_with_limited_access_users
Traceback (most recent call last):
File "/home/odoo/Odoo/src/19.0/odoo/addons/stock_account/tests/test_stockvaluation.py", line 3624, in test_update_standard_price_with_limited_access_users
self.assertEqual(product.standard_price, 1.0)
AssertionError: 12.11111111111111 != 1.0
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#270559This update fixes an issue where DIAN XML invoices were being rejected due to incorrect calculations of prepaid payments. The fix combines payment amounts into a single tag, ensuring accurate totals and preventing the creation of negative payment lines, which were causing API errors.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update resolves an issue where invoices with reverse charge tax in Poland (fa3) were generating incorrect XML files for ksef transmission. Specifically, the XML lacked the necessary information to accurately reflect the reverse charge amount and total sale value. This fix ensures proper tax reporting and compliance.
Original PR description
**STEP TO REPRODUCE** 1. Create an invoice with a tax with reverse charge (0% EU G for example). 2. Send the invoice to ksef. 3. Open the generated xml, and notice field P_18 is 2 while it should be 1 (because there is reverse charge). Also, there is not P13_10 indicated the total value of sale to which the reverse charge applies. opw-6041836 Forward-Port-Of: odoo/odoo#263764
This update fixes an issue where the withholding tax return incorrectly combined balances with the regular tax return. The change ensures the withholding tax return accurately calculates the independent balance due, resolving a discrepancy in Italian tax reporting. This improves the accuracy of financial reporting for Italian businesses.
Original PR description
Steps to reproduce: - setup an Italian company - make an invoice (for example in May) with a withholding tax and make a transaction to pay it - generate tax returns (opening date in June so that it generates from May) - validate regular tax return for May - validate withholding tax return for May -> The withholding tax return shows an amount to pay with a balance that is a combination of both the regular tax return and the withholding one, while it should be independent of the regular one. task-6116304 Forward-Port-Of: odoo/enterprise#119375
This update fixes a bug in the Preparation Time report for Point of Sale, ensuring that preparation durations are displayed correctly based on the user's current timezone. Previously, the report always used the timezone of the OdooBot, leading to inaccurate data. This change improves reporting accuracy and provides a more reliable view of preparation times.
Original PR description
In POS, the Preparation Time report groups average preparation durations by hour. Those hour buckets were always computed with the timezone of the user who ran the module upgrade (OdooBot /…
In POS, the Preparation Time report groups average preparation durations by hour. Those hour buckets were always computed with the timezone of the user who ran the module upgrade (OdooBot / superuser), not the timezone of the user viewing the report. Changing the user, company, or browser timezone had no effect on the graph until the module was upgraded again. Steps to reproduce: ------------------- * Configure a Preparation Display and create POS orders with measured preparation times. * Open Point of Sale → Reporting → Preparation Time. * Note the hour bucket used for the orders. * Change your user timezone in Preferences and reload the report. > Observation: The hour buckets stay the same. Before the fix, they only changed after upgrading `pos_enterprise`, because the timezone was embedded in the SQL view created during `init()` as superuser. Why the fix: ------------ Replace the static PostgreSQL view with a dynamic `_table_query` so `order_hour` is computed with the current user's timezone on each report read. `init()` now only drops the legacy view instead of recreating it with a frozen timezone. opw-6220248 Forward-Port-Of: odoo/enterprise#118365
This fix resolves an issue where extra prices were incorrectly added to combos when using 'always' attribute types. The update ensures that extra prices are now set on the combo creation page, aligning with the intended behavior for product variants. This improves the accuracy of point-of-sale pricing.
Original PR description
## Steps to reproduce - Create an attribute A, of type always, with 2 values, one should have an extra price - Create an attribute B, of type never, with 2 values - Create a product that has both…
## Steps to reproduce - Create an attribute A, of type always, with 2 values, one should have an extra price - Create an attribute B, of type never, with 2 values - Create a product that has both those attributes - Create a combo with that product with both values for A - Go to the PoS and order that combo with the value that has an extra price for A - The extra price is added ## Why the fix: For variants of type always, a product is created, meaning we can chose which products of this variants to have in our combo. As we can chose this, it means that we can and should chose the extra price on the combo creation page, not on the attribute page. It does not make sense to take the attribute extra price into account, as we do not take the unit price of combo items into account, so this extra price should be set on the combo page and we should ignore the attribute's extra price if the type is "always". The variants are then considered as different products, as they should in this case. If the type of the attribute is never, we can't chose which one gets an extra price on the combo page, so we should still take the attribute's extra price in this situation, as we have no other way to set it. We need to have both an always and a never attribute in order to reproduce this bug because if we only have "always" values, the configuration of the combo item is bypassed and is undefined, so **attribute_value_ids** will be undefined in this code and we won't get any value for the extra price in this code: https://github.com/odoo/odoo/blob/c09e8b2fc24ee75495fc947924e29cf5c601506f/addons/point_of_sale/static/src/app/models/utils/compute_combo_items.js#L44-L49 We now ignore the attribute's extra price if it's type is always, otherwise, it the behavior stays the same. opw-6262431 Forward-Port-Of: odoo/odoo#270625 Forward-Port-Of: odoo/odoo#268567
This update resolves an issue where the system incorrectly forecasted expiring perishable products, leading to unnecessary purchase orders. The fix ensures that forecasts accurately reflect product availability and prevents the scheduler from continuously generating purchase orders for items nearing expiration. This improves inventory accuracy and reduces operational waste.
Original PR description
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install…
Currently when the user receives a perishable product that expires in future, it is considered for reordering regardless if the user has maximum quantity or not. ## Steps to produce: - Install Inventory - Go to settings > Enable 'Lots and Serial Numbers' and 'Expiration Dates' - Create a product 'Vegetable Oil' that is tracked by lots. - Enable Expiration date in the 'Inventory' Section and set Removal date to 2. - Create a receipt for 'Vegetable oil' with Demand 10 and mark it as todo - Details > Set 'Expiration Date' into the future > Set a Lot Number and Save - Validate the receipt - Open Product Form for Vegetable oil > Reordering Rules. - Create a new Reordering rule with Min 5 and Max 10 and save. ## Observed Behavior: The forecasted quantity is calculated as zero, resulting in a quantity to order of 10, even though no replenishment is actually required. The product already satisfies the maximum quantity defined on the reordering rule, and there is no existing demand, as there are no delivery orders or sales order reservations for the product. **Why this is an issue:** When the user navigates from the product form to the On Hand Quantity view to verify the stock situation, the forecasted quantity is shown as 10. This is inconsistent with the value displayed on the reordering rule, creating confusion and making it difficult to understand the actual inventory status. In addition, if the product is configured with a Buy route, running the `Procurement: Run Scheduler` action repeatedly generates new purchase orders for the perishable product. Even if the generated purchase orders are cancelled or completed, since newly purchased stock will also have an expiration date. As a result, the same incorrect forecast calculation occurs again, causing the scheduler to continuously create new purchase orders and leading to an endless replenishment cycle if they expire within horizon days. ## Root cause: When an orderpoint is created or updated, `_compute_qty_to_order` [1] is triggered. This method calls `_compute_qty_to_order_computed` [2] which accesses the forecasted quantity, causing its compute method to called. The forecast computation retrieves context from `_get_product_context` as seen in [3], where the lead horizon date (route lead time + horizon days configured in settings) is passed as `to_date` at [4]. It then reads the product's `virtual_available` quantity using the orderpoint context at [5]. This ultimately invokes `_compute_quantities`, which delegates the calculation of `virtual_available` to `_compute_quantities_dict`, as shown in [6]. As a result, `max_date` is set to the lead horizon date at [7] (for example, one year in the future). Since the product expires before that date, it is included in `expired_unreserved_quant_res`, causing `virtual_available` to be reduced to zero at [8]. Consequently, the forecasted quantity also becomes zero at [9]. Because the forecasted quantity falls below the orderpoint's minimum quantity, a replenishment is incorrectly triggered. **Why did this issue not occur in previous versions?** This behavior was introduced by [commit](https://github.com/odoo/odoo/commit/8ba2c1e38b636c567511139c5e19b7189430a182 ), which fixed the calculation of fresh(unexpired) quantity displayed in the stock availability widget on sales order lines. Which expects that `scheduled_date` (typically the committed delivery date or expected delivery date, including leadtime) is passed to `read_qties` at [10] , which stores it in the context using the `to_date` key. However, the same `to_date` key is also used by the orderpoint horizon-days logic. This overlap causes the expiration-aware quantity computation to use the horizon date instead of the intended `with_expiration` date , leading to incorrect forecast calculations and the unexpected replenishment behavior described above. [1]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L394-L396 [2]-https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L417-L427 [3]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L374-L381 [4]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L484-L491 [5]- https://github.com/odoo/odoo/blob/96b7eed9153eab60288d1624252df6f90ea9505e/addons/stock/models/stock_orderpoint.py#L386 [6]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L152-L154 [7]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L215-L218 [8]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/product.py#L260-L262 [9]- https://github.com/odoo/odoo/blob/9ae1df190cf5ee6a5775eddb32be7b13f5ed92c9/addons/stock/models/stock_orderpoint.py#L391 [10]- https://github.com/odoo/odoo/blob/afa2b6b7b47d6420146ceb6dad897405aa92c682/addons/sale_stock/models/sale_order_line.py#L129 ## Solution It does not make sense to trigger replenishment for perishable products solely because they are expected to expire before the horizon date. Even when the available quantity already satisfies the maximum quantity defined on the reordering rule, the current logic forecasts those products as unavailable in advance of their expiration. Instead, products should only be excluded from the forecast once they have actually expired, or when they must be removed according to the original expiration-handling logic. To achieve this, an additional context key can be introduced to distinguish calls originating from the forecast availability widget from SO line. When the computation is performed for the forecast widget, the existing `to_date` context key should continue to be used so that availability is evaluated at the requested future date. For all other flows, including orderpoint calculations, the system should rely on the `with_expiration` context key instead. This preserves the original behavior, where only already-expired quantities (or quantities that must be removed due to expiration rules) are excluded from availability calculations, preventing incorrect replenishment recommendations for perishable products. opw-6200644 Forward-Port-Of: odoo/odoo#268223
4 changes
Resolved issues and error corrections
This update resolves an issue where the system wasn't properly validating partner banks when processing SEPA direct debit mandates. The change adds a constraint to ensure that mandates are only created for valid partner bank accounts, improving data accuracy and reducing potential errors in payment processing. This enhances the reliability of our SEPA direct debit functionality.
Original PR description
Forward-Port-Of: odoo/enterprise#121023 Forward-Port-Of: odoo/enterprise#120901
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. The change removes a problematic account mapping, ensuring the exported data aligns with the Odoo Profit & Loss view. This ensures accurate reporting for Luxembourg tax compliance.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update resolves an issue where the DIAN XML generated for Point of Sale (PoS) payments was being rejected due to incorrect calculations of prepaid amounts. The fix combines payment amounts into a single tag, ensuring accurate data transmission to the DIAN and preventing errors related to negative payment lines.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update resolves a performance issue related to handling complex invoice cancellation scenarios in the Mexican accounting module. By using a specialized index, the system can now efficiently process invoices with many associated documents, improving overall processing speed and reliability. This change ensures smoother operations for users managing invoices with multiple related records.
Original PR description
The field `l10n_mx_edi_cfdi_origin` can contain a large number of associated UUIDs, especially in complex cancellation scenarios. The default B-tree index fails when this field exceeds 2704 bytes, which occurs after approximately 20 UUIDs. By switching to a trigram index, we avoid the entry size limit of PostgreSQL's B-tree nodes. This ensures that invoices with many related documents can be processed while maintaining efficient search performance for partial matches on this field. **Video before the fix:** https://youtu.be/24u0HbxwIH8 **Video after the fix:** https://youtu.be/sUelv1HZMvI Forward-Port-Of: odoo/enterprise#118868
8 changes
Resolved issues and error corrections
This update resolves an error that prevented users from sending SMS messages to website visitors. The fix corrects how the system accesses visitor phone numbers, ensuring compatibility with the latest website configuration changes. This ensures SMS functionality works as expected for all visitors.
Original PR description
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another…
Currently, an error occurs when a user tries to send an sms to a visitor. **Steps to Reproduce:** - Install `website_crm_sms` without demo data. - Go to `Website` > `Edit`, drag and drop another `Form` block. - Configure the form action to `Create an Opportunity` and `save`. - Fill in the required fields, including phone number and `Submit` the form. - Go to `Website` > `Reporting` > `Visitors` and click the `SMS` button on the visitor record. `AttributeError: 'website.visitor' object has no attribute 'phone'` After [this commit], which removed the mobile field from res.partner along with all related views, then it was updated to access the phone number from the website visitor. When a user creates an opportunity through the website and then tries to send an sms from the corresponding visitor record, it raises an error [1] because it attempts to access the phone field on website.visitor. when an anonymous (non-logged-in) user creates an opportunity, clicking the sms button on the corresponding visitor record triggers error here [2]. This commit ensures that the correct mobile field is accessed from the website visitor. [this commit]: https://github.com/odoo/odoo/commit/6b820eb6fc6f782ba6a83d605d87b4a1dd2a87be [1]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L13 [2]- https://github.com/odoo/odoo/blob/6a0e6443951053f8361e97f42e5e45c32bb73656/addons/website_crm_sms/models/website_visitor.py#L20 sentry-7550340909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270118
This update corrects a technical issue in the UBL BIS3 generation process for Debit Notes. Currently, the system incorrectly used 'LegalMonetaryTotal' instead of the required 'RequestedMonetaryTotal' node, leading to errors in UBL file creation. This change ensures Debit Notes are formatted correctly for UBL BIS3 compliance, improving data accuracy and export functionality.
Original PR description
Problem --------- Debit note should have the node `RequestedMonetaryTotal` instead of `LegalMonetaryTotal`. Solution --------- Add a conditional depending on the document type. opw-6295897 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270238
This update fixes an issue where the partner associated with an invoice was incorrectly overridden by the purchase order during UBL (Universal Business Language) XML imports. The change ensures the purchase order is the definitive source for partner information on invoices, improving data accuracy and consistency. This resolves a potential discrepancy between purchase and billing records.
Original PR description
Fix a bug where the partner of a bill is overriden by the PO matching The chosen logic here is to say that in the context of a purchase, the purchase order is the single source of truth to set the partner on a bill Steps to reproduce: - Create a partner with is_company = True - Create a contact type 'invoice' for this partner - Create a purchase order for the first partner - Import an XML (UBL) that matches this PO - You can see in the import logs that the partner was correctly found first, and then the PO matching override it to set the contact as the partner task-6289358 Forward-Port-Of: odoo/odoo#270780 Forward-Port-Of: odoo/odoo#269223
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. Specifically, it addressed an error caused by an automated entry from account 142, ensuring the data aligns with the Odoo Profit & Loss view. The fix removes unnecessary mappings and adds a test to guarantee accurate reporting.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update resolves an issue where payments to the DIAN (Colombian tax authority) were failing due to incorrect XML formatting. The fix combines payment amounts into a single tag, ensuring accurate calculations and preventing errors related to negative payment amounts.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update resolves a technical issue impacting how Odoo handles Mexican tax invoices (CFDI). The system was struggling to process invoices with many related documents due to a database index limitation. Switching to a different index type ensures smoother processing and improved performance for complex cancellation scenarios.
Original PR description
The field `l10n_mx_edi_cfdi_origin` can contain a large number of associated UUIDs, especially in complex cancellation scenarios. The default B-tree index fails when this field exceeds 2704 bytes, which occurs after approximately 20 UUIDs. By switching to a trigram index, we avoid the entry size limit of PostgreSQL's B-tree nodes. This ensures that invoices with many related documents can be processed while maintaining efficient search performance for partial matches on this field. **Video before the fix:** https://youtu.be/24u0HbxwIH8 **Video after the fix:** https://youtu.be/sUelv1HZMvI Forward-Port-Of: odoo/enterprise#118868
This update resolves an issue where the XML generated for Polish e-invoices (FA3) with reverse charge taxes was incorrectly formatted. Specifically, the XML fields related to reverse charge were not accurately reflected, leading to potential errors in tax reporting. This fix ensures accurate data transmission to the KSEF system.
Original PR description
**STEP TO REPRODUCE** 1. Create an invoice with a tax with reverse charge (0% EU G for example). 2. Send the invoice to ksef. 3. Open the generated xml, and notice field P_18 is 2 while it should be 1 (because there is reverse charge). Also, there is not P13_10 indicated the total value of sale to which the reverse charge applies. opw-6041836 Forward-Port-Of: odoo/odoo#263764
This update resolves an issue where international UPS shipments were failing due to incorrect commercial invoice addresses. The fix initially used the delivery address, but this caused further problems. Now, the system defaults back to the delivery address if country codes don't match, with a warning displayed to the user to ensure accurate invoice information.
Original PR description
Issue ----- When making an international delivery to a partner with different invoice and delivery addresses, we send the delivery address as the `Sold To` address as well. Problematic case 1 ----- -…
Issue ----- When making an international delivery to a partner with different invoice and delivery addresses, we send the delivery address as the `Sold To` address as well. Problematic case 1 ----- - Create a belgian company - Setup UPS - Create a French customer - Add a different french delivery address - Create a product (with some weight) - Create a SO (with UPS delivery) to the customer & confirm - Validate the transfer > Commercial invoice `Sold To` uses the delivery address Solution for case 1 ----- Use the delivery address' `commercial_partner_id`. This leads to another issue in some edge cases... Problematic case 2 (caused by case 1 fix) ----- - Create a belgian company - Setup UPS - Create a French customer - Add a delivery address in Switzerland - Create a product (with some weight) - Create a SO (with UPS delivery) to the customer & confirm - Validate the transfer > UPS error `The Sold To party's country code must be the same as the Ship To party's country code with the exception of Canada and satellite countries.` Solution for case 2 ----- Default back to delivery address for the `Sold To` field when countries don't match, as this is a limitation of the UPS API. Warn the user, either on the SO or the transfer itself (if no SO). Warning looks like this (on SO): <img width="1914" height="716" alt="image" src="https://github.com/user-attachments/assets/f7aa73c4-f24c-42da-8f3e-6a58765ef020" /> ----- Ticket: opw-6200263 Forward-Port-Of: odoo/enterprise#119747 Forward-Port-Of: odoo/enterprise#118031
5 changes
Resolved issues and error corrections
This update resolves an issue where DIAN XML files for Point of Sale (PoS) payments were being rejected due to incorrect calculations of prepaid amounts. The fix combines payment amounts into a single tag, ensuring accurate data transmission to the DIAN and preventing errors related to negative payment lines.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#119255
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. Specifically, an error was present when certain transactions involved account 142. The fix removes this account from the export mapping and adds a test to ensure the accuracy of future exports.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update fixes an issue where payments to Mexican CFDI invoices could be sent multiple times, leading to inaccurate payment records. The fix ensures the 'Update Payments' button only appears after the invoice payment is fully reconciled, preventing over-reporting of payments and maintaining accurate financial data. This improves the reliability of CFDI reporting.
Original PR description
Issue: Sending payments to CFDI before its full amount is reconciled allow sending the same invoice payment several times to CFDI. So some invoices are declared as paid several times and the total…
Issue: Sending payments to CFDI before its full amount is reconciled allow sending the same invoice payment several times to CFDI. So some invoices are declared as paid several times and the total amount of the payment is seen as exceeding the real total. This fix is a back port of odoo/enterprise#108355 and aim to prevent some things the backend allow, but the front end prevents. Following steps could be used to reproduce from 18.3. Steps to reproduce: - In a Mexican company - Create an invoice A of $40 to Inmobiliaria CVA - Confirm and send to CFDI - Go to bank, create a new Bank transaction of $80 - reconcile with Invoice A - Go to invoice A => click on button "Update payments" (it doesn't appear before version 18.3) - Then sheet CFDI and Download There is the first XML sent to CFDI with payment for invoice A - Create an invoice B of $40 to Inmobiliaria CVA - Confirm and send to CFDI - reconcile the transaction with Invoice B - Go to invoice B - Click on button "Update payments" - Then sheet CFDI and Download There is the second XML sent to CFDI with payment for invoices A and B Invoice A payment was sent twice to CFDI Expected behavior: - The "Update payment" button should appear only once the invoice payment is fully reconciled. Current behavior: - The update payment button appear once the invoice is reconciled with a payment. opw-5432421 Forward-Port-Of: odoo/enterprise#119357
This update resolves an issue that prevented efficient processing of invoices with numerous related documents (specifically, those related to Mexican tax filings). By using a different database index, the system can now handle complex cancellation scenarios and maintain fast search performance for finding invoices. This ensures smoother operations for our Mexican customers.
Original PR description
The field `l10n_mx_edi_cfdi_origin` can contain a large number of associated UUIDs, especially in complex cancellation scenarios. The default B-tree index fails when this field exceeds 2704 bytes, which occurs after approximately 20 UUIDs. By switching to a trigram index, we avoid the entry size limit of PostgreSQL's B-tree nodes. This ensures that invoices with many related documents can be processed while maintaining efficient search performance for partial matches on this field. **Video before the fix:** https://youtu.be/24u0HbxwIH8 **Video after the fix:** https://youtu.be/sUelv1HZMvI Forward-Port-Of: odoo/enterprise#118868
This update resolves a problem where Italian fiscal printers would intermittently stop printing POS orders due to unsupported characters in product or payment method names. The fix replaces these characters with spaces, aligning with Epson's official printer documentation to ensure reliable printing functionality. This prevents order data loss and improves the POS experience for Italian users.
Original PR description
Steps to reproduce: - Setup an Italian fiscal printer - Modify the name of a product to use the non-blocking space character "\ "; - In the POS, create an order with the product. Error: the fiscal device will stop midway in the printing process and return an incomplete response to the frontend. The issue can also be reproduce if the character is included in the payment method name or the POS config name. Solution: When formating the xml command, replace all non-supported character by a space character. The non-supported character list is provided by the official [EPSON fiscal printer documentation](https://support.epson.net/setupnavi/?PINF=bsmanual&OSC=WS&LG2=EN&MKN=FP-90III%20RT) in the document "ePOS Fiscal Print Solution Development Guide". Other: Rename the file "dispaly_text.xml" to "display_text.xml". [opw-6244089](https://www.odoo.com/odoo/project/49/tasks/6244089) Forward-Port-Of: odoo/enterprise#120169
11 changes
Resolved issues and error corrections
This update fixes an issue in the Belgian payroll calculations related to DMFA (termination) payments. Previously, the system incorrectly stopped calculating payments after a 3-month period, even with ongoing remuneration. This change ensures that payments continue as long as remuneration exists, preventing employees from being completely unenrolled with no payment during extended sickness periods. The update includes new tests to guarantee accurate calculations.
Original PR description
. Iterate as long as there is a remuneration on the dmfa period > 0, not stop to the previous period only. . The fix was made so if the remuneration on the dmfa period (3 months) is null, system will check the previous remuneration on the previous dmfa period, BUT, if we take a long sickness period on an employee, you can have people with 2 or 3 DMFA fully unenmployed with no remuneration at all . Add the corresponding tests task-6299792
This update resolves a crash that occurred when users were manually correcting bank statement lines within the Odoo Enterprise system. The issue stemmed from a missing context setting during record creation, preventing the correct journal from being assigned. This fix ensures accurate journal assignments and prevents data modification errors.
Original PR description
When the manual correction tool was used to fill in the lines, we weren't passing the active context when creating the new records. In the case of bank statements, it could be an issue as the `default_journal_id` key is expected to be present to set the correct journal on the newly created bank statement line. Without this key in the context, it would default to the first journal with a valid type (see function `_search_default_journal`). If the journal found this way didn't match the current journal, a crash would occur when modifying the newly created lines. opw-[6294117](https://www.odoo.com/odoo/unassigned-tasks/6294117) Forward-Port-Of: odoo/enterprise#121032 Forward-Port-Of: odoo/enterprise#120745
This update resolves an issue where manually created timesheets weren't correctly linked to the associated Sales Order Item. The fix ensures the Sales Order Item information is properly inherited during timesheet creation, improving data accuracy and streamlining the timesheet process. This prevents errors when recording time against sales orders.
Original PR description
### Issue: When manually creating a timesheet from a planning shift's smart button, the Sales Order Item is not inherited. ### Cause: The `planning_slot_id` was missing from the timesheet list view. As a result, the `default_planning_slot_id` passed in the context was dropped during the creation of the new record, preventing us from linking the correct SO line. Solution: Added `planning_slot_id` as `column_invisible="True"` in the timesheet list view so the context default is retained. task-6229397
This update fixes an issue where the Gantt chart popover displayed only start and end dates for project tasks. The change ensures the popover correctly uses the card view, providing richer task details. This improves the user experience when viewing project timelines.
Original PR description
Since odoo/odoo#114328, the kanban view of the action isn't used as gantt popover by default if no popover is defined in the gantt view arch. As a consequence, on the project sharing task gantt view, the popover was the default, basic one which displays only the start and end dates. This commit restores the previous behavior by explictly set on the gantt view the id of the card view to used inside the popover. To achieve this, it was necessary to extract that card view out of the kanban. Followup of task~5262907
This update resolves an issue where the search input in a SelectMenu was unintentionally clearing typed characters due to timing conflicts. The fix ensures the input value is controlled directly, resulting in a more reliable and consistent search experience. This improves usability for users searching within the system.
Original PR description
Before this commit, some very specific timing could cause re-renders after debounced was called but before it was finished, causing a re-render of the input and setting its value to a previous state, removing typed characters. This commit fixes that by making the input value controlled manually, not via the reactivity. Community: https://github.com/odoo/odoo/pull/266912
This update ensures the Documents smart button now displays *all* linked documents for records connected through bridge modules (like approvals, fleet, HR, and projects), regardless of their location. Previously, it only showed documents within a configured folder. The change also includes improvements to document counting across various modules, enhancing the overall document management experience.
Original PR description
* = approvals, fleet, hr_recruitment, project Before this commit, clicking on the Documents smart button, it only displayed the documents contained in the configured folder. But it happens that a document is linked to a record from a bridge module but is not in that configured folder. This commit fix that by displaying all the linked documents for a record from a bridge module. So we toook the opportunity to move the document_count field to the 'documents.mixin' model with its compute method and the 'action_open_documents' method. Task-5948278
This update resolves a problem where order signing with Fiskaly failed after a change to the Fiskaly API key. The system now correctly resets the associated SCU and cash registers, ensuring seamless integration with the Fiskaly system. This prevents order processing errors and maintains accurate financial data.
Original PR description
When the Fiskaly API key/secret is changed, the company is bound to a new Fiskaly organization (owner). The SCU and cash registers stored on the company and POS configs were created under the previous owner and no longer exist for the new one, so signing orders fails with E_CASH_REGISTER_NOT_FOUND. Clear l10n_at_pos_company_scuid and each config's l10n_at_cash_regid together with the access token so they are recreated under the new organization on the next authentication. opw-6297695 Forward-Port-Of: odoo/enterprise#120839
This update fixes an issue where created packages weren't displayed within the barcode picking app when putting items into packs. The fix ensures that users can clearly see the source and destination packages during the packing process, improving workflow and reducing errors. This enhancement simplifies the process of managing packaged goods within Odoo.
Original PR description
### Steps to reproduce: - Enable `Lots & Serial Numbers` and `Packages` in the settings - Create a product tracked by SN and add SN001 and SN002 to stock - Create and confirm a delivery for 2 units -…
### Steps to reproduce: - Enable `Lots & Serial Numbers` and `Packages` in the settings - Create a product tracked by SN and add SN001 and SN002 to stock - Create and confirm a delivery for 2 units - Open the Barcode app and open the delivery - Scan the product > Scan SN001 - Click `Put in Pack` ### Current behavior: The created package is not displayed anywhere. Clicking Put in Pack again nests the package into another package without any visible indication to the user. ### Cause of the Issue: The GroupedLineComponent cannot display neither the source or destination package: https://github.com/odoo/enterprise/blob/c5bbcaeed513817fdda1f3f42f4c9b2440264174/stock_barcode/static/src/components/grouped_line.xml#L4-L21 However, our case the grouped line contains only a single line and prevents the users from viewing the sublines since the `Show Reserved Lots` is disabled on the operation type and only one lot (with additional demand) was scanned: https://github.com/odoo/enterprise/blob/c5bbcaeed513817fdda1f3f42f4c9b2440264174/stock_barcode/static/src/components/grouped_line.js#L75-L77 https://github.com/odoo/enterprise/blob/c5bbcaeed513817fdda1f3f42f4c9b2440264174/stock_barcode/static/src/components/grouped_line.js#L44-L55 opw-6237834 Forward-Port-Of: odoo/enterprise#119114
This update fixes an error in the XML export for the Luxembourg eCDF platform. Incorrect financial year data was being generated due to a specific account entry. The fix removes problematic account mappings and adds a test to ensure accurate reporting going forward.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update resolves an issue where DIAN XML files were being rejected due to incorrect calculations of prepaid payments. The fix combines payment amounts into a single tag, ensuring the total paid matches the due amount and eliminating the need for negative payment lines, improving compliance with DIAN requirements.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
This update fixes a previous limitation where users couldn't properly set prices for hybrid subscription products (those allowing one-time sales). Now, the system requires a plan to be selected for subscription products and correctly filters pricelist items to support one-time pricing for hybrid subscriptions, ensuring accurate and flexible pricing options.
Original PR description
Before this commit:
1. Users could save a pricelist rule for a pure subscription product without assigning a plan.
2. Hybrid subscription products (where 'Allow One-Time Sale' is True) were filtered out of the pricelist item form when no plan was selected, preventing users from setting a one-time price.
After this commit:
- The `plan_id` field on the product template form is now mandatory if the product is a subscription and does not allow one-time sales.
- The `product_tmpl_id` domain on the pricelist item form is updated to `['|', ('recurring_invoice', '=', bool(plan_id)), ('allow_one_time_sale', '=', True)]`, allowing users to select hybrid products for one-time pricing.
task: 6164232
Forward-Port-Of: odoo/enterprise#120323
Forward-Port-Of: odoo/enterprise#1152711 change
Resolved issues and error corrections
This update resolves an issue where DIAN XML files were being rejected due to incorrect calculations of prepaid payments. The fix combines payment amounts into a single tag, ensuring accurate totals and preventing negative payment lines that triggered errors with the DIAN API. This ensures proper reporting and compliance for Colombian Point of Sale transactions.
Original PR description
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the…
**Steps to reproduce:** To test this, you will need an official DIAN setup, because this error comes from the response to our API call to the DIAN. - Setup the DIAN in a colombian company - Open the PoS - Order a product - Before paying, make the amount we are paying bigger than the amount due - We get an error response from the API, the error is saying that the total due does not match what we paid **Why the fix:** Currently, the xml is rejected because the sum of the **PaidAmount** in the **PrepaidPayment** tag is not equal to what we are trying to pay for. This is happening because to avoid the fact that we can not send a line with negative amount, we used the **abs()** function on the line amount to make it positive. The negative line comes from the fact that when we have a total due that is below the amount paid, we create a new payment line with a negative amount to balance it out. But as we can't send lines with negative amount, we needed to make it positive. This does not work, as the sum of the lines' amount will then be too much compared to what we are paying for, because instead of substracting it we will be adding it. To avoid this, we now group the amount in one single tag and send it this way. This ensures that the sent amount is correct and equals the amount due, and does not send a negative line. opw-6232575 Forward-Port-Of: odoo/enterprise#121075 Forward-Port-Of: odoo/enterprise#119255
9 changes
Resolved issues and error corrections
This update ensures that database indexes automatically update when a field's index type changes (e.g., from btree to trigram). Previously, upgrades silently ignored these changes, leading to outdated indexes and potential performance issues. Now, the system proactively rebuilds indexes to match the field's requirements, maintaining optimal database performance.
Original PR description
Description of the issue/feature this PR addresses: `Registry.check_indexes` derives a column index's name as `<table>__<column>_index`, which does **not** encode the access method, and only creates…
Description of the issue/feature this PR addresses:
`Registry.check_indexes` derives a column index's name as `<table>__<column>_index`, which does **not** encode the access method, and only creates the index when no index of that name already exists. It never inspects the access method of an existing index.
As a consequence, changing a field's `index=` kind on an **already-indexed** column is silently ignored on existing databases. For example `account.move.name` was changed from a plain btree index to `index='trigram'`:
```python
name = fields.Char(
...
index='trigram',
)
```
On a fresh database this creates the expected GIN/trigram index. On any database that already had the btree index, the old btree index keeps its name, so `check_indexes` finds the name present and does nothing. The `(=)ilike` searches the trigram index was meant to accelerate keep falling back to sequential scans, with no error or warning.
Current behavior before PR:
### Steps to reproduce
1. Install a module on an existing DB while a `Char` field is `index=True` (btree).
2. Change the field to `index='trigram'` and upgrade the module.
3. `\d <table>` in psql — the index is still `USING btree`, not `USING gin`.
Desired behavior after PR is merged:
`check_indexes` now also reads each existing index's access method (`pg_am.amname`). When the method no longer matches what the field expects (`gin` for trigram, `btree` otherwise), the stale index is dropped and recreated. The drop is issued inside the **same savepoint** as the recreate, so a failed rebuild (e.g. a lock timeout) rolls the drop back and never leaves the column without an index.
Scope: only the access method is reconciled. A change that alters solely the partial predicate (`btree` -> `btree_not_null`) keeps the same method and is intentionally left untouched.
### Notes
- This extends the existing index-management logic in place and keeps the current "keep unexpected index" behaviour for fields that dropped `index=` entirely; only fields that still want an index, of a different method, are rebuilt.
- Trigram rebuilds still require the `pg_trgm` extension; without it the GIN index is skipped exactly as before (`self.has_trigram` guard).
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update corrects a requirement in the Nemhandel integration for Danish UBL invoices. Specifically, it adds a 'TaxCategory' node to the 'AllowanceCharge' element, which is now necessary to meet UBL formatting standards. This ensures proper invoice processing with Nemhandel and avoids potential errors.
Original PR description
Add the TaxCategory node in AllowanceCharge node as it's a requirement for some UBL format. It has been spoted with Nemhandel, as it requires a single tax category in the AllowanceCharge. no-task
This update resolves an issue where selling combos through the Point of Sale (PoS) system triggered a warning and prevented order validation due to eTIMS registration requirements. The fix ensures that individual combo items are correctly registered, allowing combo sales to proceed smoothly without these blocking errors. This improves the PoS experience for Kenyan businesses using eTIMS.
Original PR description
Steps to reproduce ------------------ 1. Install l10n_ke_edi_oscu_pos. 2. Select the Kenyan company. 3. Enable eTIMS on the PoS. 4. Register the products inside a combo, but not the combo itself. 5. Sell the combo in the PoS. Observation ----------- We see a warning that the combo must be registered to eTIMS, and the order can't be validated. What's happening ---------------- In the PoS a combo adds a 0 price parent line for the combo product, but the combo is not a real item to send to eTIMS, only the products inside it are, and (as per step 4) the combo is not registered. `checkEtimsFields` sees the combo as not registered, so it raises the warning in `showUnregisteredProductsWarning` and blocks the payment in `validateOrder`. Fix --- In the backend, we skip sending the parent combo line to eTIMS, and on the frontend, we make the combo parent line not need eTIMS registration, so the warning and the block don't apply to it. opw-6253306
This update fixes an issue where purchase order receipt deadlines weren't updating correctly after quantities were reduced to zero. The fix ensures that cancelled stock moves no longer incorrectly influence the calculated deadline, leading to more accurate and reliable delivery scheduling. This improves the overall efficiency of our inventory management.
Original PR description
Steps to reproduce the bug:
- Create a Purchase Order with 2 products and confirm it
- Note the receipt's deadline (= date_planned of both lines)
- Set the quantity of one PO line to 0
- Update the scheduled date (date_planned) of the purchase order
Problem:
the receipt deadline does not update.
The receipt kept the old deadline from the cancelled move. When a PO line qty is set to 0, `_merge_moves` cancels the corresponding stock move via `_action_cancel`. Then `_update_move_date_deadline` correctly skips cancelled moves (filtered by `state not in ('done', 'cancel')`), so the cancelled move retains its original `date_deadline`. However, `_compute_date_deadline` on `stock.picking` used
`move_ids.filtered('date_deadline')`, which not checks move state, so the stale deadline of the cancelled move was included in the min/max computation.
opw-6292600This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. The change removes a problematic account mapping, ensuring the exported data aligns with the Odoo Profit & Loss view. This ensures accurate reporting for Luxembourg tax compliance.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571 Forward-Port-Of: odoo/enterprise#121010
This update resolves an issue impacting the processing of invoices related to Mexican VAT (CFDI). The system now uses a more efficient index, allowing it to handle complex cancellation scenarios with numerous linked documents without performance slowdowns. This ensures smoother invoice processing and avoids potential errors.
Original PR description
The field `l10n_mx_edi_cfdi_origin` can contain a large number of associated UUIDs, especially in complex cancellation scenarios. The default B-tree index fails when this field exceeds 2704 bytes, which occurs after approximately 20 UUIDs. By switching to a trigram index, we avoid the entry size limit of PostgreSQL's B-tree nodes. This ensures that invoices with many related documents can be processed while maintaining efficient search performance for partial matches on this field. **Video before the fix:** https://youtu.be/24u0HbxwIH8 **Video after the fix:** https://youtu.be/sUelv1HZMvI Forward-Port-Of: odoo/enterprise#118868
This update fixes an issue where the time recorded for productive work was slightly inaccurate when work orders exceeded their expected duration. The fix ensures that productive time is calculated precisely, leading to more reliable productivity reports. This improves the accuracy of time tracking for manufacturing operations.
Original PR description
Version: -------- - 18.0+ Steps to reproduce: ------------------- - Install `mrp` - Create a Manufacturing Order - In the `Work Orders` tab, add a work order with an `Expected Duration` of 15 seconds…
Version:
--------
- 18.0+
Steps to reproduce:
-------------------
- Install `mrp`
- Create a Manufacturing Order
- In the `Work Orders` tab, add a work order with an `Expected Duration` of 15 seconds (00:15)
- Confirm the Manufacturing Order
- Start the work order timer
- Let it run for 19 seconds and pause it
- Open the Productivity report through the `external link` icon
Issue:
------
The time split between productive time and reduced-speed time is off by
one second when the elapsed duration exceeds the expected duration.
Example:
Observed:
- Fully Productive Time: 14 s
- Reduced Speed: 5 s
Expected:
- Fully Productive Time: 15 s
- Reduced Speed: 4 s
Cause:
-------
When click into `Pause` button it trigger `button_pending` -> `stop_employee` -> `_close`
In `_close()` computes the boundary between productive and performance
time by subtracting the excess from the end
of the timer:
https://github.com/odoo/odoo/blob/b1a6682896a4f113799340a768ec2eb2b8bdc69d/addons/mrp/models/mrp_workcenter.py#L594
`wo.duration` is the sum of all productivity record durations, each
stored as `round((date_end - date_start).total_seconds() / 60, 2)`.
For 19 elapsed seconds: `round(19 / 60, 2) = 0.32` min, so:
excess = 0.32 - 0.25 = 0.07 min = 4.2 s
productive_date_end = T+19s - 4.2s = T+14.8s ← fractional second
`_compute_duration` then strips sub-second precision via
`.replace(microsecond=0)`, truncating T+14.8s to T+14s.
https://github.com/odoo/odoo/blob/b1a6682896a4f113799340a768ec2eb2b8bdc69d/addons/mrp/models/mrp_workcenter.py#L542
As a result:
- Record 1 (productive): T → T+14s → 14 s = 0.23 min (wrong)
- Record 2 (performance): T+14s → T+19s → 5 s = 0.08 min (wrong)
The rounding of `wo.duration` from the true value (0.3166… min) to
0.32 min shifts the computed boundary by 0.8 s, which `.replace
(microsecond=0)` then truncates, silently stealing one second from
productive time and adding it to reduced-speed time.
Fix:
---
Compute the productive/performance boundary using actual elapsed seconds
instead of rounded float durations.
For the common single-timer case `remaining_expected_seconds = 0.25 * 60
= 15.0` (exact), so `productive_date_end = T+15.000000s` — no fractional
part, nothing for `.replace(microsecond=0)` to truncate.
- Record 1 (productive): T → T+15s → 15 s = 0.25 min ✓
- Record 2 (performance): T+15s → T+19s → 4 s ≈ 0.07 min ✓
The multi-timer case (e.g. pause → resume → pause) is also handled
correctly
---
opw-6302745
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis update resolves an issue where adding COGS lines to product tax calculations caused unintended recalculations of all tax lines, leading to incorrect tax amounts. The fix ensures COGS lines aren't treated as base tax lines, preventing this recalculation and maintaining accurate manual tax adjustments. This improves the reliability of tax calculations within the system.
Original PR description
## Description of the issue/feature this PR addresses: Setup plus video 1. Go to settings, enable "Automatic Valuation" and "Storeable Locations". 2. Navigate to Product Categories. 3. Create a new…
## Description of the issue/feature this PR addresses: Setup plus video 1. Go to settings, enable "Automatic Valuation" and "Storeable Locations". 2. Navigate to Product Categories. 3. Create a new product category with the costing method Standard Price and the inventory valuation Automatic. 4. Navigate to Products, click into any product. 5. Add the new product category to this product under General Information. 6. Add any tax in the purchase tax field. 7. In the Accounting tab of the product, add any account to the Price Difference Account field. https://drive.google.com/file/d/1i2DHEt0g9G5Edad_QB3QaFkOT49cbMAZ/view?usp=sharing Instructions to reproduce error 1. Navigate to Purchase. 2. Add a customer, then add the configured product. 3. Add a tax to the line. Ensure that the tax and price_unit are nonzero. 4. Confirm the order. 5. Receive the product. 6. Create the bill. 7. Edit the tax on the vendor bill, then save the changes. Notice that the changes are kept. 8. Select Confirm. Notice that the changes to the tax line are not kept, and that the COGS lines appeared (with taxes applied to them). 9. Reset the bill to draft. 10. Click into the configured product and remove the product category. 11. Repeat steps 7-8 . No COGS lines, and the tax line is the manually set value. ## Current behavior before PR: COGS lines with taxes have no net effect on any tax lines as they cancel each other out. However, their creation triggers the recalculation of all tax lines, undoing any manual adjustments to tax lines. ## Desired behavior after PR is merged: This commit ensures that COGS lines are not considered base tax lines, so that their creation does not trigger the recalculation of other base tax lines. opw-5387248 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update resolves an issue where E-Way Bill amounts were incorrectly calculated when sales orders used tax-included prices. The fix ensures that tax is properly accounted for, leading to accurate E-Way Bill generation for sales and purchase orders with tax-included pricing. This improves compliance and reporting accuracy.
Original PR description
`*` = `ewaybill_Stock, sale_stock, purchase_stock` **Steps to reproduce:** * Install `l10n_in_ewabill_stock` and `l10n_in_sale_Stock`. * Set the Default Tax Price Setting to "Tax Included". * Create…
`*` = `ewaybill_Stock, sale_stock, purchase_stock` **Steps to reproduce:** * Install `l10n_in_ewabill_stock` and `l10n_in_sale_Stock`. * Set the Default Tax Price Setting to "Tax Included". * Create a Sales Order (e.g. unit price 300, qty 600, 18% GST) and confirm the Delivery Challan/Delivery Order. * Generate an E-Way Bill from the Delivery Challan. **Observed behavior:** * The Taxable Amount and Total Invoice Amount are displayed incorrectly in the generated E-Way Bill, including both the printed document and the JSON. * The `ewaybill_price_unit` shows the tax-excluded price (e.g. 254.24) instead of the original tax-included price (300), leading to a double tax exclusion when `compute_all` processes it. **Cause:** * `_l10n_in_get_product_price_unit` in both `l10n_in_sale_stock` and `l10n_in_purchase_stock` unconditionally used `price_subtotal / qty` to compute the E-Way Bill price unit. `price_subtotal` is always tax-excluded, so for tax-included prices, the tax was already stripped. * `_l10n_in_tax_details_by_stock_move` then passed this already tax-excluded price to `compute_all` with taxes that have `price_include=True`, causing `compute_all` to strip the tax a second time (e.g. 254.24 / 1.18 = 215.46 instead of the correct 254.24). **Fix:** * Check whether any of the line's taxes have `price_include` set. If so, use `price_total / qty` (which preserves the tax-included price) so that `compute_all` can correctly extract the tax. Otherwise, continue using `price_subtotal / qty` as before. opw-6273101 Forward-Port-Of: odoo/odoo#268504
4 changes
Resolved issues and error corrections
This update resolves an issue where E-Way Bill amounts were incorrectly calculated when sales prices included tax. The fix ensures that tax-included prices are properly processed, preventing double tax calculations and ensuring accurate E-Way Bill generation. This impacts sales orders with 'Tax Included' settings.
Original PR description
`*` = `ewaybill_Stock, sale_stock, purchase_stock` **Steps to reproduce:** * Install `l10n_in_ewabill_stock` and `l10n_in_sale_Stock`. * Set the Default Tax Price Setting to "Tax Included". * Create…
`*` = `ewaybill_Stock, sale_stock, purchase_stock` **Steps to reproduce:** * Install `l10n_in_ewabill_stock` and `l10n_in_sale_Stock`. * Set the Default Tax Price Setting to "Tax Included". * Create a Sales Order (e.g. unit price 300, qty 600, 18% GST) and confirm the Delivery Challan/Delivery Order. * Generate an E-Way Bill from the Delivery Challan. **Observed behavior:** * The Taxable Amount and Total Invoice Amount are displayed incorrectly in the generated E-Way Bill, including both the printed document and the JSON. * The `ewaybill_price_unit` shows the tax-excluded price (e.g. 254.24) instead of the original tax-included price (300), leading to a double tax exclusion when `compute_all` processes it. **Cause:** * `_l10n_in_get_product_price_unit` in both `l10n_in_sale_stock` and `l10n_in_purchase_stock` unconditionally used `price_subtotal / qty` to compute the E-Way Bill price unit. `price_subtotal` is always tax-excluded, so for tax-included prices, the tax was already stripped. * `_l10n_in_tax_details_by_stock_move` then passed this already tax-excluded price to `compute_all` with taxes that have `price_include=True`, causing `compute_all` to strip the tax a second time (e.g. 254.24 / 1.18 = 215.46 instead of the correct 254.24). **Fix:** * Check whether any of the line's taxes have `price_include` set. If so, use `price_total / qty` (which preserves the tax-included price) so that `compute_all` can correctly extract the tax. Otherwise, continue using `price_subtotal / qty` as before. opw-6273101
This update fixes an issue where the Luxembourg eCDF XML export incorrectly reported financial year data. The change removes a problematic account mapping, ensuring the exported data aligns with the Odoo Profit and Loss view. This improves data accuracy for Luxembourg tax reporting.
Original PR description
Issue: Users reported that the financial year result in the XML export for the Luxembourg eCDF platform is incorrect, despite being correct in the Odoo Profit and Loss visualization. The exported XML populated incorrect amounts in cell 0161 under certain circumstances (namely, in the case of an explicit entry from account 999999 to account 142000). Solution: * Removed account 142 entirely from both the `ACCOUNTS_2019` and `ACCOUNTS_2020` dictionaries so it no longer auto-populates cells 0161/0162 (up to 2019 included) and 2955/2956 (from 2020 onward). * Removed the 2019 threshold condition in the loop bypass for account 142. * Removed the hard-coded manual pop for cell 2955 since it has been removed from the mapping. * Deleted the redundant reassignment of `net142` in the loss calculation block. Ticket [link](https://www.odoo.com/odoo/project.task/6059571) opw-6059571
This update enables branch companies to register on the PEPPOL network as 'sender only' participants, mirroring their parent company's registration. This simplifies the registration process for branch offices and ensures compliance with PEPPOL requirements, streamlining international trade operations.
Original PR description
This task backports the ability to register branch companies as sender only using the same identifier as their parent company task-id-6069374
This update resolves an issue where a duplicate stock move was being created in backorders after a quality check failure. The fix prevents the creation of an unnecessary move when a quality issue redirects a transfer, ensuring backorders are accurately reflected and avoids redundant inventory management.
Original PR description
Steps to reproduce: ------------------- - Install `purchase`, and `quality_control` - Enable Multi-Step Routes in Inventory settings - Create a storable product tracked by lot - Create a Quality…
Steps to reproduce:
-------------------
- Install `purchase`, and `quality_control`
- Enable Multi-Step Routes in Inventory settings
- Create a storable product tracked by lot
- Create a Quality Point:
- Operation Type: Internal Transfer
- Control Per: Quantity
- Configure a failure location
- Enable the 3-Step Incoming route for the warehouse
- Duplicate the Internal Transfer operation type and name it `Internal Transfer 2` Configure the operation types:
- Internal Transfer: <-- this is original one
- Source Location: WH/Input
- Destination Location: WH/Quality Control
- Internal Transfer 2:
- Source Location: WH/Quality Control
- Destination Location: WH/Stock
- Open the warehouse's 3-Step Incoming route
- Locate the push/pull rule whose source location is `WH/Quality Control`
- open that and Change its operation type from internal transfer -> `Internal Transfer 2`
- Create and confirm a Purchase Order for 20 units of the product
- Open the generated receipt
- Validate 12 units using Lot-1 and create a backorder
- Open the Internal Transfer (where source is that PO and there operation type
is internal transfer that original one)
- Demand: 20
- Reserved Quantity: 12
- Click into detail button on stock move and split the move line into:
- 9 units from Lot-1
- 3 units from Lot-1
- save
- Open the Quality Check
- Pass 9 units
- Fail 3 units
- Validate the transfer and create a backorder
- Open the internal transfer whose operation type `Internal Transfer 2`
Issue:
------
`Internal Transfer 2` contains two stock moves:
- Move 1:
- Demand: 20
- Quantity: 9
- This is correct.
- Move 2:
- Demand: 8
- Quantity: 0
- This move should not exist.
Expected behavior:
------------------
`Internal Transfer 2` should contain only the move corresponding to the successfully passed quantity:
- Demand: 20
- Quantity: 9
No additional move with demand 8 and quantity 0 should be generated.
Cause:
--------------------------
A 3-step route pre-creates the whole chain at confirm time: Receipt -> Internal Transfer -> Internal Transfer 2, each linked through move_dest_ids/move_orig_ids. Both fields are the two sides of a single many2many relation (stock_move_move_rel), so removing the relation from either side drops it entirely.
When the user confirms the failure on the quality check, the wizard calls quality.check._move_line_to_failure_location() (quality.py). Because the failure location differs from the next operation's source location, it runs
https://github.com/odoo/enterprise/blob/3a5e30f0bb7c1660827fd209cd74ef3bcd538213/quality_control/models/quality.py#L386
and `_break_mto_link()` then does unlink of `orig_location_id`
```py
self.move_orig_ids = [Command.unlink(parent_move.id)]
self.procure_method = 'make_to_stock'
```
**This is intentional** : it switches Internal Transfer 2 to `make_to_stock` so its `_action_assign()` reserves only what is physically present at Quality Control, instead of chain-reserving the 3 failed units that were rerouted to the failure location.
The side effect is that, because the relation is shared, unlinking parent_move from Internal Transfer 2's move_orig_ids also empties the Internal Transfer move's move_dest_ids.
- when the picking is validated and the backorder is created
When the picking is later validated, `_action_done()` splits off the remaining, never- reserved demand into a backorder move via `_split()` -> `_prepare_move_split_vals()`,
which copies `move_dest_ids` from the original move. but that's already empty,
because of the break at the time of failed quality check.
So the backorder is created with no `move_dest_ids` even though its 8 units have nothing to do with the quality failure they simply haven't
been reserved/processed yet.
https://github.com/odoo/odoo/blob/6feedc18b7873bfde62047ece1a5de882319bc21/addons/stock/models/stock_move.py#L1955
Then action_done trigger ` _action_confirm()` it calls `_push_apply()`.
https://github.com/odoo/odoo/blob/6feedc18b7873bfde62047ece1a5de882319bc21/addons/stock/models/stock_move.py#L966-L967
` _push_apply()` only skips a move if it already has `move_dest_ids` since the backorder has none, it doesn't skip — it searches for an applicable push rule from WH/Quality Control, finds the very same rule that already produced Internal Transfer 2
the first time, and creates a brand new Internal Transfer 2 move for its own demand (8)
via `rule._run_push()`. That new move has quantity 0,
Fix:
--------------------------
Before pushing a move that has no destination, `_push_apply()` now first checks whether a failed, diverted quality check (same product, same upstream chain via move_orig_ids) already exists for it. If so, this move is just a leftover backorder of a transfer that already had its link broken for quality reasons, and the existing destination move already covers it — so the push is skipped instead of creating a duplicate.
Nothing about the quality check wizard or the existing link-breaking
logic was changed; this only stops that breakage from leaking into a later, unrelated backorder.
----
opw-6298637