Wednesday, September 2, 2026
24 changes · master
Resolved issues and error corrections
This restores rules that prevent costs for re-invoiced products from being counted twice in project analytics when anglo-saxon accounting is used. It also brings back related test coverage and applies the logic per company, improving accuracy in multi-company workflows.
Original PR description
Restores the sale_project_stock_account module removed by 37b615f14305 ("[REM] sale_, project_stock_account: remove dead code"), which excludes re-invoiced products from the analytic lines generated…
Restores the sale_project_stock_account module removed by 37b615f14305
("[REM] sale_, project_stock_account: remove dead code"), which excludes
re-invoiced products from the analytic lines generated by stock moves
when anglo-saxon accounting is enabled, together with the test covering
it and the project_stock_account filter it extends.
That removal was justified by _account_analytic_entry_move() no longer
existing, concluding that analytic lines are not created from stock
moves anymore. That was true at the time: 08b62a4bbcc6 had dropped the
method along with the override of project_stock_account that filtered
the moves, and skipped the test guarding it in the same commit, so
removing the module turned nothing red. 1d510f2c2e05 then reintroduced
the analytic lines under _create_analytic_move, which is called whenever
moves are done, leaving the module wrongly missing.
_get_valid_moves_domain is restored on the new method, and with it the
exclusion of the re-invoiced products, whose cost is already carried by
the customer invoice under anglo-saxon accounting. 9a614c91ad7d has
since given project_stock_account a _create_analytic_move override that
skips the moves whose operation type has analytic costs disabled; the
restored filter is merged into that override instead of being added
beside it, and _get_valid_moves_domain keeps only the project condition,
the analytic-costs one now being applied to every move.
The exclusion is now decided per move, on the company of the move
itself: the removed code read the flag on the company of the user
processing the delivery, which ignores the company the delivery belongs
to. As the domain is evaluated for each move, the condition moves into
it instead of gating its construction.
TestAnalytics is un-skipped, as TestAnalyticsReinvoice extends it and
would otherwise inherit its skip. The other tests skipped to fast merge
the valuation refactoring are re-enabled separately.
The restored files predate 8a5b99545dfa, which renamed the product field
expense_policy to reinvoice_policy, so the domain and the test use the
new name.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#278698
Forward-Port-Of: odoo/odoo#275246Opening the Sales Commissions report could fail because one percentage field was still available in the pivot report even though it could not be totaled. The field has been removed from the pivot measures so business users can load and analyze commission reports normally again.
Original PR description
Issue: `achieved_rate` lost its aggregator (set to aggregator=None) in 69e3827df02 so it wouldn't be summed/averaged in the list view, but it was still declared as a measure in the pivot view. Pivot always needs an aggregate function for its measures, so PivotModel's _getMeasureSpecs threw No aggregate function has been provided for the measure 'achieved_rate', crashing Sales > Reporting > Commissions on load. Solution: Remove achieved_rate as a pivot measure, consistent with it already being excluded from list-view totals. opw-6523870
Frontdesk kiosk check-ins no longer trigger an error when a visitor selects a host and the station has no email template configured. The fix ensures email notifications are only required or sent when the necessary template settings are available, keeping visitor registration smooth.
Original PR description
Currently, an error occurs when a visitor record is created through the kiosk in Frontdesk. **Steps to Reproduce;** - Install the `frontdesk` module. - Go to `Employees` and create a new employee or…
Currently, an error occurs when a visitor record is created through the kiosk in Frontdesk.
**Steps to Reproduce;**
- Install the `frontdesk` module.
- Go to `Employees` and create a new employee or open an existing one.
Set the employee's `email` and `work phone`.
- Go to `Frontdes` > `Stations` and create a station.
- Enable `Host Selection` and select that employee.
- Enable `Notify by email` and remove the `Frontdesk email template`.
- Open the `kiosk URL`, fill in the information, click `Browse All Hosts`,
select that `employee`, and click `Continue`.
- Error is logged in the terminal.
**Another case:**
- Clear the `email template`, disable `Notify by email`, and perform `step 6th`.
- The same error is raised.
`ValueError: Expected singleton: mail.template()`
After [recent commit], when a visitor record is created [1] through the kiosk and the selected
host has a work email while Notify by email is enabled on the station, the system attempts to
notify the host by email [2] . During this process, it renders the email body [3] and subject
using the station's mail template, which raises an error [4] when no mail template is configured.
Similarly, when Notify by email is disabled, if the selected host has no linked user record
but does have a work email, the notification falls back to email [5]. This also raises the same
error when no mail template is configured.
This commit ensure a mail template is required whenever Notify by email is enabled on a station.
For the fallback email notification (when the host has no user account), the system checks that
both a work email and a mail template are available before attempting to send the email,
since email notifications are not mandatory in this case.
[recent commit]: https://github.com/odoo/enterprise/pull/108297/changes
[1]: https://github.com/odoo/enterprise/blob/d025c1a6a87a90bdaead4195e5f996d058294604/frontdesk/controllers/main.py#L151-L162
[2]- https://github.com/odoo/enterprise/blob/d025c1a6a87a90bdaead4195e5f996d058294604/frontdesk/models/frontdesk_visitor.py#L112-L113
[3]- https://github.com/odoo/enterprise/blob/d025c1a6a87a90bdaead4195e5f996d058294604/frontdesk/models/frontdesk_visitor.py#L128-L131
[4]: https://github.com/odoo/odoo/blob/c76a3aedf523da6f751960d2b5dfbc0016416ef1/addons/mail/models/mail_render_mixin.py#L765
[5]- https://github.com/odoo/enterprise/blob/d025c1a6a87a90bdaead4195e5f996d058294604/frontdesk/models/frontdesk_visitor.py#L108-L111
sentry-7624715784
Forward-Port-Of: odoo/enterprise#125178Customers are now stopped from completing checkout when pricing rules unexpectedly make a cart total zero while zero-priced product sales are blocked. Instead of hanging at payment, they are sent back to the cart with a clear warning, reducing failed checkouts and support issues.
Original PR description
When 'Prevent Sale of Zero Priced Product' is enabled and a country-group pricelist prices a product at 0 once the customer's country becomes known during checkout, the cart total becomes 0. The 'free order' branch then validated the order at the payment step, which could hang and time out for a cart that is not actually sellable. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286025 Forward-Port-Of: odoo/odoo#284959
This fixes an issue in the Timesheets grid where clicking a day-based cell could get stuck showing the old value instead of cycling through the expected options. Users can now reliably update grid entries without refreshing or reselecting records, reducing friction during timesheet entry.
Original PR description
1. Open Timesheets > Configuration > Settings and set "Encoding Unit" to "Days", then save 2. Open Timesheets > Timesheets > All Timesheets and switch to the grid view 3. Click an empty cell -> it is selected and keeps displaying 0, and further clicks no longer cycle it through 0, 0.5 and 1 day The grid cell components memoise the value they display with `computed()`, but `GridCell` is a plain class whose `value` is mutated in place when the user edits the grid: reading it registers no dependency, and handing the same cell object back to the `cell` signal does not notify either. The memo is therefore never invalidated, and `FloatToggleGridCell.onChange` picks the next value of the range from that frozen value, so the toggle stops advancing as well. With this PR, the cell value is a signal, so every component deriving from it recomputes when the grid is edited. Task-6527559
Sales users without accounting permissions can now open invoices that use cash rounding when they are allowed to view the invoice. This prevents an unnecessary access error and keeps the sales invoicing workflow working smoothly.
Original PR description
**Behavior:** When a sales user without accounting access rights tries to view an invoice created from a sale order where a cash rounding is applied, an AccessError is raised. This occurs because…
**Behavior:** When a sales user without accounting access rights tries to view an invoice created from a sale order where a cash rounding is applied, an AccessError is raised. This occurs because loading the invoice view triggers `_compute_tax_totals()`, which then passes the invoice's `invoice_cash_rounding_id` to `_get_tax_totals_summary()`. Which then ends up failing when trying to access fields on the `cash_rounding` record due to missing accounting rights, even though the user is allowed to view the parent invoice. This is fixed by ensuring reading fields on `cash_rounding` during tax total computation bypasses the access check using `sudo()`, as the user already has legitimate access to the invoice itself. **Steps to reproduce:** - As an admin, enable cash rounding then create one. - Create an invoice and set the Cash Rounding Method - In debug mode, go to 'Set Default Values' in the debug dropdown and set Cash Rounding Method = your rounding for all users - Go to users, and ensures that Demo has no accounting rights but has sales user rights - As Demo, create a sale order, confirm it, then create the related invoice. - When trying to acces said invoice, you should get an Access Error opw-6379781 Forward-Port-Of: odoo/odoo#284212 Forward-Port-Of: odoo/odoo#278815
Users can now safely discard changes after reordering very long lists of order lines spread across multiple pages. This prevents an error that could interrupt quotation editing and improves reliability when working with large sales documents.
Original PR description
**Steps to reproduce:** - Create a quotation - Add order lines until reaching 200 (duplicating helps), save and ensure that the pager appeared (1-200/201) - Drag one of the lines with the handle to…
**Steps to reproduce:** - Create a quotation - Add order lines until reaching 200 (duplicating helps), save and ensure that the pager appeared (1-200/201) - Drag one of the lines with the handle to reorder them - Discard all changes (X shaped button) You will have an evaluation error **Behavior:** When loading a list of records exceeding the limit, only parts of the records are saved in `_cache`. When triggering a reordering of said list, all records need to be loaded including the ones on other pages: https://github.com/odoo/odoo/blob/d4eff7b14d37a9b59c95d304542a5a12629bc90b/addons/web/static/src/model/relational_model/static_list.js#L1139-L1149 `_getResIdsToLoad()` gets all Ids missing from the cache, these are then passed through `._createRecordDatapoint` and will then be stored in `_cache`. The issue is that the Datapoints are getting created with only `activeFields` as data, which in our case are `id` and `sequence` When discarding the changes `._checkValidity()`is called on each Datapoint: https://github.com/odoo/odoo/blob/d4eff7b14d37a9b59c95d304542a5a12629bc90b/addons/web/static/src/model/relational_model/record.js#L423-L429 And then `._isInvisible()` is called. This is where the issue happens, since only `sequence` and `id` are stored, when we try evaluate `combo_item_id`, which is the condition to see if sequence is invisible, `combo_item_id` is not found and we get an Evaluation Error. This commit prevents going into `._checkValidity()` by adding `this.isInEdition` to the check leading to it, requiring that the record is in 'edit' mode which is not the case for records created with `_createRecordDatapoint()` opw-6399024 Forward-Port-Of: odoo/odoo#281018
Point of Sale sessions can now close correctly when orders include both a lot-tracked product and a kit containing that product. The fix correctly totals quantities across multiple matching order lines, preventing an error that blocked session closing.
Original PR description
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The…
**Step to reproduce :** 1. Create Product A and enable lot tracking for it. 2. Create a Kit product and add Product A as one of its components. 3. Go to PoS and create orders using: - Product A - The Kit product - A combination of Product A and the Kit product 4. Create three different orders with these combinations. 5. Try to close the PoS session. **Issue :** An error occurs when attempting to close the PoS session. The issue is in the pos_mrp module, specifically in the _get_lot_line_qty method. When accessing the following line: `lines_data[move.bom_line_id.bom_id.product_tmpl_id.product_variant_id.id]['order_lines'].qty` we expect order_lines to contain a single record. However, in this scenario, multiple order lines can be returned for the same product. As a result, accessing .qty directly on order_lines raises a singleton error. **Solution :** With this fix, we first retrieve the quantity from each order line and thensum the quantities together. This ensures that multiple matching order lines are handled correctly and prevents the singleton error when closing the PoS session. opw-6442765 Forward-Port-Of: odoo/odoo#285612 Forward-Port-Of: odoo/odoo#281622
This fix prevents the Belgian payroll Dimona button or badge from disappearing after employee records are autosaved. It ensures HR teams can reliably access the required Dimona action when managing employee onboarding and payroll compliance.
Original PR description
Steps to reproduce: - Create an employee, fill in name, CP, contract start date, wage. - Leave the form without saving manually (e.g. open another record). - Select the employee again: the Dimona button/badge is gone and stays gone regardless of further edits. l10n_be_needs_dimona_in and l10n_be_dimona_next_action are server-computed but readonly=False let autosave submit a stale value, permanently blocking the recompute. Drop the stray readonly=False on both and add a regression test. Task 6515877 Forward-Port-Of: odoo/enterprise#129731 Forward-Port-Of: odoo/enterprise#129648
Incoming vendor bills processed from email will no longer use the saved original email file as the main invoice attachment. This helps ensure users see the actual invoice document preview instead of a broken or irrelevant email copy when emails include embedded PDFs.
Original PR description
When an incoming mail server has the "Keep Original" option enabled, a copy of every incoming mail is stored as original_email.eml. Because it is not a document of the invoice, the system unattaches…
When an incoming mail server has the "Keep Original" option enabled, a copy of every incoming mail is stored as original_email.eml. Because it is not a document of the invoice, the system unattaches it, however it may still be used as main attachment in case no other PDF or image was attached to the message. Steps to reproduce: - Configure an incoming mail server with "Keep Original" enabled, using an alias pointing to a vendor bill journal. - Send a mail to that alias containing an xml embedding a PDF. - Open that bill Issue: The invoice's main attachment points to the .eml file. This occurs because it is set before the PDF is extracted from the xml. Then, when import extracts the PDF, it is added as attachment on the invoice, but we already have a main attachment that won't be overwritten. However, once a pdf or image is added as attachment, the system will show the (broken) preview. opw-6431726 Forward-Port-Of: odoo/odoo#285409 Forward-Port-Of: odoo/odoo#280318
Uruguay electronic invoicing now uses the exchange rate saved on the original invoice instead of recalculating it later. This prevents credit notes or related documents from showing mismatched rates when currency rates are updated after an invoice is posted.
Original PR description
18.0 introduced an `invoice_currency_rate` field, storing the currency rate used for each invoice. Currently `_l10n_uy_edi_get_used_rate` uses the `currency.convert()` method using the date and company of the invoice. This is vulnerable to inaccuracy if the currency rates are changed after the invoice posting. For example, if an invoiceis posted and the currency rates are updated, the invoice will use the old rate, while `_l10n_uy_edi_get_used_rate` will return the new one. In the case of the customer in the related ticket, this caused their credit note reference currency rate to mismatch with the one on the invoice. This PR changes the `currency.convert()` call to fetching and inverting the `invoice_currency_rate` field. This ensures the returned value will match the one used for the invoice. opw-6456867 Forward-Port-Of: odoo/enterprise#129620 Forward-Port-Of: odoo/enterprise#128190
Incoming VoIP calls that time out and go to voicemail are now clearly marked as missed. This avoids confusing call messages and keeps call records accurate for users reviewing their communication history.
Original PR description
Steps to reproduce: - Call your softphone with your smartphone. - Let the smartphone ring forever. => At some point, you reach the voicemail and the softphone stops ringing but: - The softphone…
Steps to reproduce: - Call your softphone with your smartphone. - Let the smartphone ring forever. => At some point, you reach the voicemail and the softphone stops ringing but: - The softphone displays a weird message with emojis. - The call record stays "Trying to call" (and might *display* "Ended unexpectedly" in 19.2). This commit fixes those two issues by showing a proper "Call missed" message and switching the call record status to "Missed". This is done in 19.2 and not before... because the behavior before is even more problematic: - 19.1: the softphone keeps ringing and crash if you try to answer, that was mostly fixed thanks to [1] and this commit takes profit of those big ameliorations to fix the issue here. - 19.0: you do not even reach voicemail (it stops without saying anything). This was apparently fixed as a side-effect of [2], which we do not consider worth even partially backporting as not critical. [1]: https://github.com/odoo/enterprise/commit/942f32316ab02d8c739fe7fdd5ec2bdde472a68e [2]: https://github.com/odoo/enterprise/commit/d24d7f3406ca47e7ac529d69957b0ad481d553bf task-6453500 Forward-Port-Of: odoo/enterprise#128731 Forward-Port-Of: odoo/enterprise#127075
This fix ensures mail records clean up temporary calculated data when a store is closed. It prevents memory from building up during long test runs, improving reliability and avoiding browser-session timeouts.
Original PR description
Before this commit, the mail files of WebSuite.test_unit_desktop take the heap from 82 MB to 1665 MB, read on the [MEMINFO] line each file logs after a forced major GC. Past 2 GB that GC stops…
Before this commit, the mail files of WebSuite.test_unit_desktop take the heap from 82 MB to 1665 MB, read on the [MEMINFO] line each file logs after a forced major GC. Past 2 GB that GC stops returning on runbot and the suite dies on "AssertionError: Script timeout exceeded". This happens because a record holds its computeds in a RecordScope that only the deletion of that record destroys, and a test deletes no record. The test teardown calls _runDisposeFns on the store, which stops what each record registered there and destroys the scope of the store alone, so every computed stays an observer of the signals it read. Message.richBody reads a signal of the module-level emojiLoader, so each message ever rendered keeps its store, its env and its app alive for the whole browser session. This commit registers the destruction of a record scope among the dispose functions of the store, so that the store teardown reaches the computeds of every record, as it already reaches the effect of every compute field. The same run then ends on 127 MB. https://runbot.odoo.com/odoo/error/946840
The Nilvera PDF button now appears only where relevant for Turkish companies, reducing confusion for other users. Invoicing users without administrator rights can now send, fetch, and download Nilvera e-invoices without access errors, and invoices with unknown status are refreshed immediately when requested.
Original PR description
## Description of the issue/feature this PR addresses: Two related Nilvera e-invoice issues affecting Turkish companies: - The "Fetch Nilvera PDF" button appeared for every company, not just Turkish…
## Description of the issue/feature this PR addresses:
Two related Nilvera e-invoice issues affecting Turkish companies:
- The "Fetch Nilvera PDF" button appeared for every company, not just Turkish ones.
- Sending/fetching e-invoices (or fetching the PDF) as a non-admin Invoicing user raised an
AccessError, because the company's Nilvera API key field is restricted to System/Settings users
and several call sites read it without `.sudo()`.
## Current behavior before PR:
- The PDF-fetch button shows on both the list view and form view of `account.move` regardless of
the company's country.
- An Invoicing-only user (no System/Settings access) gets an AccessError when sending/fetching
e-invoices or fetching the PDF, because `_get_nilvera_client` reads
`company.l10n_tr_nilvera_api_key` without `.sudo()`.
## Desired behavior after PR is merged:
- Both buttons are only visible for Turkish companies (the list-view button has no bound record, so
its gate is driven by a new `l10n_tr` session_info/JS context injection instead of a direct field
reference).
- Invoicing users can send/fetch e-invoices and fetch the PDF without hitting an AccessError.
## Things to add on forward-port
The `.sudo()` fix lives inside `_get_nilvera_client` itself (`l10n_tr_nilvera/lib/nilvera_client.py`),
so every caller that goes through it is already fixed automatically once this diff forward-ports.
Only call sites that read `company.l10n_tr_nilvera_api_key` **directly**, bypassing
`_get_nilvera_client`, still need their own `.sudo()`:
### 19.4
- [ ] Fix e-Dispatch/e-Receipt fetch gate-check access error (direct read of the API-key field,
sudo it the same way as `_l10n_tr_nilvera_company_get_documents` in this PR)
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#285360
Forward-Port-Of: odoo/odoo#274312Fixes an accounting issue where cost of goods sold could be overstated after a sale was delivered, returned, credited, and sold again. Credit notes are now included so earlier invoices and refunds properly offset each other, improving inventory cost accuracy.
Original PR description
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS…
**Steps to reproduce:** - create a storable product with a positive quantity a cost of 10 and average perpetual category - confirm a SO for 1 quantity, validate delivery - confirm invoice for 1 (COGS should be 10) - return the delivery and validate - create a credit note from the invoice for 1 and confirm (COGS should be 10) - return the return and validate - change the standard price to 100 - create an invoice from the SO for 1 and confirm **Current behavior:** cogs are 190 **Expected behavior:** cogs should be 100 **Cause of the issue:** _get_posted_cogs_value doesn't take into account the credit notes (only the account moves with type 'out_invoice' are taken into account in the sum) https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L185-L186 So in our case the first invoice and the credit note don't cancel out each other. The same goes for _get_cogs_qty (which returns the total cogs past + current), in the past cogs it doesn't take into account the quantities of the credit note. https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/sale_stock/models/account_move.py#L172-L174 So the quantity of the first invoice and the one of the credit note don't cancel out each other. As a result, the return value from _get_cogs_value() for the second invoice is : price unit = 100 returned by _get_cogs_price_unit() https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L68 which returned the standard price because the product has an average cost method https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/stock_move.py#L275-L280 cogs_qty = 2 (instead of 1 if credit was taken into account as -1 in the sum) self._get_posted_cogs_value() = 10 (instead of 0 if credit note cogs were taken into account in the sum as -10) return value = (100 * 2 -10)/1 = 190 https://github.com/odoo/odoo/blob/0824dc24665de6bfa805d540e756cdcb006edba6/addons/stock_account/models/account_move_line.py#L75 **fix:** if we take into account the credit note the return value will be : (100 * 1 - 0)/1 = 100 the mechanism of the already posted cogs value is there for cases where we only delivered a part of the quantity and then delivered the rest, but in the case where we delivered and then returned (with credit notes) it shouldn't have an impact. Thus the idea to include the credit note so that it can cancel out the first invoice opw-6426111 Forward-Port-Of: odoo/odoo#284151 Forward-Port-Of: odoo/odoo#282893
Belgian payroll now calculates holiday pay regularization from the amounts already recovered during the year and the official attestation cap, instead of recalculating from the employee's current salary. This prevents past leave-related payroll values from changing after a salary update, making year-end adjustments more predictable and accurate.
Original PR description
Previously, holiday pay regularization was calculated using the current payslip's wage, causing past leave values to change whenever the salary was updated. Update `_get_holiday_pay_regularization`…
Previously, holiday pay regularization was calculated using the current payslip's wage, causing past leave values to change whenever the salary was updated.
Update `_get_holiday_pay_regularization` to compute the remaining balance directly from historical 90% recovered amounts (`HolPayRec`) and the attestation cap. This ensures the regularization stays fixed based on the wage at the time leave was taken.
During the year, paid leave days deduct 90% of the wage (HolPayRec). During the annual
regularization, this method calculates the adjustment needed to reach 100% of the wage
equivalent, capped by the total attestation amount (amount_to_recover).
Variables:
- recovered_amount: Total 90% leave deductions (HolPayRec) already applied.
- amount_to_recover: Total holiday pay cap from previous year's attestations.
- total_100_pct_wage: 100% wage equivalent ((recovered_amount / 9.0) * 10.0).
- max_recoverable: Upper limit allowed to recover, min(total_100_pct_wage, amount_to_recover).
- regularization_amount: Difference between max_recoverable and recovered_amount.
Formula:
regularization_amount = min((recovered_amount / 9.0) * 10.0, amount_to_recover) - recovered_amount
Return value = -regularization_amount
Examples:
1. Negative Return Value (Deduction / Further Recovery):
- amount_to_recover = €1,000
- recovered_amount = €450 (90%)
- total_100_pct_wage = (450 / 9) * 10 = €500
- max_recoverable = min(500, 1000) = €500
- regularization_amount = 500 - 450 = €50
- Return: -€50 (Deducts remaining 10% from employee).
2. Positive Return Value (Refund / Over-recovery Correction):
- amount_to_recover = €300 (Low attestation cap)
- recovered_amount = €450 (Already recovered throughout the year)
- total_100_pct_wage = (450 / 9) * 10 = €500
- max_recoverable = min(500, 300) = €300
- regularization_amount = 300 - 450 = -€150
- Return: +€150 (Refunds €150 to employee because recovery exceeded the attestation cap).
Returns:
tuple: (-regularization_amount, explanation_info)
Task : 6453625Belgian payroll now counts the employee's final working day when deciding whether a contract meets the short-term employment threshold. This prevents sick leave from being incorrectly treated as unpaid for employees whose contract spans exactly three months.
Original PR description
Steps: - Create a short term employee, contract spanning exactly 3 months - Create a sick leave for the employee - Sick leave is unpaid Cause: - Currently the threshold for determining short term employees was fixed at 3 months exactly, meaning that excludes the last working day of the employee Solution: - Short term employee threshold is now adjusted to take into consideration the last working day of the employee in its computation. Task: 6515351
Fixed an issue that could prevent users from opening WhatsApp Business account settings after switching Odoo to another language, such as French. The page now relies on a language-independent identifier, so translated labels no longer cause an error.
Original PR description
Currently, an error occurs when a user tries to view the WhatsApp Business account. **Steps to Reproduce:** - Install the `whatsapp_oauth` module. - Go to `Settings` > `Languages` and add `French…
Currently, an error occurs when a user tries to view the WhatsApp Business account. **Steps to Reproduce:** - Install the `whatsapp_oauth` module. - Go to `Settings` > `Languages` and add `French (BE)`, then `switch to it`. - Go to `WhatsApp` > `Configuration` > `WhatsApp Business Accounts (Comptes Whatsapp Business)`. `ValueError: L'élément '<xpath expr="//div[contains(normalize-space(.), 'Receiving Messages')]">' ne peut être localisé dans la vue parente` When the user changes the language, the text in the view is translated [1]. Since the WhatsApp account view tries to locate the div using the plain text Receiving Messages [2]. Since the text has been translated in the parent view, the XPath can no longer locate the element and raise the error. This commit ensures that the XPath uses the name attribute to identify the element, which is language-independent. We cannot use the class attribute because the same class is used by other div elements. [1]- https://github.com/odoo/enterprise/blob/a3db72899cb97612505aebbd5450aeb65205db73/whatsapp/views/whatsapp_account_views.xml#L81-L84 [2]- https://github.com/odoo/enterprise/blob/a3db72899cb97612505aebbd5450aeb65205db73/whatsapp_oauth/views/whatsapp_account_views.xml#L61-L63 sentry-7534862409 Forward-Port-Of: odoo/enterprise#130003
This fixes a subcontracting receipt issue where partially received items could be incorrectly split into backorders, causing non-subcontracted products to remain fully pending. The added regression coverage helps ensure mixed receipts with subcontracted and regular products are processed consistently.
Original PR description
### Steps to reproduce: - Create a subcontracted product P1 - Create a storable product P2 - Buy 5 units of both products from your subcontractor - On the receipt set both moves quantity to 2 Units -…
### Steps to reproduce: - Create a subcontracted product P1 - Create a storable product P2 - Buy 5 units of both products from your subcontractor - On the receipt set both moves quantity to 2 Units - Validate the receipt and create a backorder #### > Only the subcontracted move has been kept on the receipt and a backorder was created for 3 units of P1 and 5 of P2. ### Cause of the issue: Setting the quantity of the subcontracted move will automatically record the quantities on the subcontracted MO: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/stock_move.py#L83 https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/stock_move.py#L123 https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/mrp_production.py#L91 However, the `_update_finished_move` method adds and update the related subcontracted move lines marking them as *picked* to adapt the related reservation: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/mrp_subcontracting/models/mrp_production.py#L118-L164 This is problematic since picking a move line will also pick the move: https://github.com/odoo/odoo/blob/9440ff9064c77af0159a427de8f5e5721aec0de5/addons/stock/models/stock_move.py#L261-L267 And only picked moves are considered to be processed at picking validation. ### Note: The exact same issue had already been fixed in 17.0: db8b33ebb9fe23507bcba30b12741e4d688ae549 However, the fix had an issue concerning the barcode behavior as it removed the picked computation for subcontracted moves which made hybrid pickings such as the above one (with one subcontracted and one non-subcontracted move) impossible to process in the barcode app. As such, the fix and test where reverted in cf2d18c92bee55ef79db1a338e9baf12f258ee5b The present commit provides an alternative fix of the original issue keeping subcontracted moves unpicked by quantity changes without affecting the picked computation of subcontracted moves (e.g. adding a picked move line on a subcontracted move will still pick that move). Enterprise: https://github.com/odoo/enterprise/pull/123884 opw-6330584 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285614 Forward-Port-Of: odoo/odoo#275304
Fixes an issue where expanding a newly created Calendar event dialog opened a blank form instead of the event just created. This prevents duplicate events from being created for the same time slot and makes quick event creation more reliable.
Original PR description
Problem: In Calendar, dragging a slot opens the quick create dialog. Typing a subject and clicking the expand button in the dialog header creates the event, but the form that opens is empty, and…
Problem: In Calendar, dragging a slot opens the quick create dialog. Typing a subject and clicking the expand button in the dialog header creates the event, but the form that opens is empty, and saving it creates a second event for the same slot. Cause: `FormViewDialog` stores the id of the record it saved in `currentResId`, which `onExpand` passes as `res_id`. `saveRecord` sets it only in the branch that runs when no `onRecordSave` prop is given. `AttendeeCalendarController` passes one since 30478fe855cd (odoo/odoo#239435), so `currentResId` stays false and the action opens a new record. Solution: Set `currentResId` in the shared branch of `saveRecord`. It is private to `FormViewDialog`, so a consumer passing `onRecordSave` cannot set it. Steps to reproduce: - Open Calendar. - Drag a slot in the week view to open the quick create dialog. - Type a meeting subject. - Click the expand button in the dialog header. - Observe that the form opens on a new record while the event is created. Ticket [link](https://www.odoo.com/odoo/project/49/tasks/6476930) opw-6476930 Forward-Port-Of: odoo/odoo#285899
Mexican CFDI invoice XML files now use the customer or user language for unit of measure labels when invoices are sent in bulk. This prevents English labels from appearing unexpectedly in Spanish-language invoices, improving consistency and compliance of generated documents.
Original PR description
### Issue before this commit: When a CFDI invoice was sent through a mass action (Send & Print executed by OdooBot), the unit of measure in the generated CFDI appeared in English instead of the…
### Issue before this commit: When a CFDI invoice was sent through a mass action (Send & Print executed by OdooBot), the unit of measure in the generated CFDI appeared in English instead of the configured language (e.g. Spanish. ### Steps to reproduce the issue: 1.Download Accounting, Contacts and l10n_mx 2. Switch to Spanish (MX) language 3. Set the language of "Inmoviliaria CVA" and "XENON INDUSTRIAL ARTICLES" to Spanish (MX) 4. In contacts select Archived in filters and switch OdooBot language to Spanish (MX) 5. Create and confirm two invoices in the database, one for each customer but don't send these invoices 6. Go to the list view and select these two invoices, and click on "Send and print" and select CFDI 7. Check any of the XML files generated in any of the invoices (in the CFDI tab of the invoice) 8. See that the UoM in the XML file, will be set in english rather than Spanish (MX) ### Cause of the issue: The context under which the batch action runs does not contain the lang key. As a result, translated fields (such as product_uom_id.name) were read in the source language (English) instead of the executing user's language, because nothing in the CFDI generation chain explicitly forced the correct lang into the context. ### Reason to introduce the fix: A CFDI must always report translated fields in the correct language. The fix ensures the invoice is read with the executing user's language when the context doesn't already specify one, so translated fields are consistently correct across both flows. opw-6399860 Forward-Port-Of: odoo/enterprise#129916 Forward-Port-Of: odoo/enterprise#125698
Fixes an error that could block users when creating a second time off accrual allocation with the same plan. This improves reliability for HR teams managing employee leave balances, especially in payroll configurations that trigger accrual recalculations.
Original PR description
**Steps to Reproduce:** - Install the Time Off app and Belgian Payroll (l10n_be_hr_payroll). - Create and validate an accrual allocation for currently logged-in user with: No end date Any start date…
**Steps to Reproduce:** - Install the Time Off app and Belgian Payroll (l10n_be_hr_payroll). - Create and validate an accrual allocation for currently logged-in user with: No end date Any start date An accrual plan configured with a carry-over milestone Any Time Off Type - Create another allocation for the same employee, using the same accrual plan and same Time Off Type, but with a different start date that is not in the future. - Select the accrual plan. The error is raised immediately during the onchange. **Issue:** - When the accrual allocation onchange computes the accrued balance, a temporary allocation is used internally to simulate the accrual computation. - This temporary record is discarded after the computation. - The discarded temporary record can remain pending for computed field recomputation. - When the onchange later triggers recomputation, the stale temporary NewId can cause: `KeyError: <NewId origin=18>` **Root Cause:** - Temporary allocations created with 'new(origin=allocation)' can add computed fields to `env.transaction.tocompute`. - `invalidate_recordset()` clears the temporary record's cache but does not remove the `NewId` from the pending recomputation queue. - Since the temporary record has no database row to recompute from, the NewId can later be picked up during recomputation. - This can lead to a KeyError when the framework tries to access cached data for the discarded temporary record. **Solution:** - Properly discard temporary allocations after the accrual simulation. - In addition to invalidating the cache, remove the temporary NewId from `env.transaction.tocompute` using `remove_to_compute()`. - Use this cleanup for temporary allocations created with `new(origin=...)`. **Result** - Prevents the KeyError during accrual allocation onchange. - Allows users to create another allocation with the same accrual plan without triggering the RPC error. **opw-6390559** --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#285799 Forward-Port-Of: odoo/odoo#283772
This fix ensures mentions in social posts are correctly recognized and linked to the right profiles on Facebook, LinkedIn, and Twitter/X. It helps users publish clearer social content and avoids broken or incorrectly displayed mentions in the social posting tools.
Original PR description
This commit fixes an issue with the mention regexes for social_facebook as they weren't properly replaced by the initial mechanism. The initial mechanism was introduced by [1]. Now, we check every possible mention and if it is indeed a known mention, then we replace it by the correct link to their profile. [1]: https://github.com/odoo/enterprise/commit/4dacc5fce72687680080f75feef875fbfb3dbd15 task-6026857
Confirmed sales orders that are locked now consistently hide product configuration edit buttons, including configurable products, event tickets, and booths. This prevents users from seeing actions that should not be available and keeps the order lock behavior consistent across sales screens.
Original PR description
### Steps to Reproduce: 1. Go to Sales > Configuration > Settings 2. Under Quotations & Orders, enable "Lock Confirmed Sales" 3. Now create a Quotation with 2 products, 1 with attributes and 1…
### Steps to Reproduce: 1. Go to Sales > Configuration > Settings 2. Under Quotations & Orders, enable "Lock Confirmed Sales" 3. Now create a Quotation with 2 products, 1 with attributes and 1 without 4. Confirm the order 5. Hover over the product with attributes and notice that it allows you to to edit, but hovering over the other product does NOT reveal the edit icon ### Issue: When a user has configured their database to lock sale orders, it should not be possible to edit the products once the order has been confirmed. It currently only happens for products that have attributes. While standard products cannot be edited in a locked state, a missing condition on configurable products allows the button to persist when hovering over the description column. This creates an inconsistent UI state where users appear to have access to modify some of the line items. ### Solution: To fix this, we updated the `hasConfigurationButton` getter in `sale_product_mixin.js` to evaluate the parent document's locked state. By adding `!this.props.record.model.root.data.locked` to the getter's return logic, the edit button is now globally hidden across all related widgets (such as `sale_label_text` and `sale_product_field`) when the sales order is locked. This ensures that configurable products properly respect the locked document restrictions just as standard products do. Additionally, this locked condition was applied to the method overrides in the `event_sale` and `event_booth_sale` modules, ensuring that event tickets and booths also hide their configuration buttons on locked orders.Additionally, this locked condition was applied to the method overrides in the event_sale and event_booth_sale modules, ensuring that event tickets and booths also hide their configuration buttons on locked orders. opw-6512288 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284882