Friday, September 11, 2026
14 changes · master
Resolved issues and error corrections
Manufacturing orders now use the newly generated serial number when production is reset and resumed, instead of accidentally producing with an old serial number. The serial number button label was also clarified so users can better distinguish generating new serials from editing existing ones.
Original PR description
This PR addresses a bug introduced in the PR https://github.com/odoo/odoo/pull/277276 To reproduce the bug: 1- Produce a serial tracked product with serial number 00001 for example. 2- Set MO to…
This PR addresses a bug introduced in the PR https://github.com/odoo/odoo/pull/277276 To reproduce the bug: 1- Produce a serial tracked product with serial number 00001 for example. 2- Set MO to progress. 3- Clear the lot_producing_ids, and generate a new one 00002. 3- Produce again. = The MO produces a product with the old serial number 00001 instead of 00002. This happens because while producing the MO, it finds the move already has its own lot_ids but we need to check it's different than the `lot_producing_ids` generated on the MO to re-assign it again. Side improvement: The button `Generate Serial` is now named `Generate Serial Numbers` and becomes `Edit Serial Numbers` whenever there are already multiple generated serial numbers on the MO. This is more relevant to the user now since they now have the ability to set a done MO to progress and change the quantity to produce hence, it makes sense to edit the serial numbers when they are asked to specify a serial number in that case.
The WhatsApp account screen now only shows the Disconnect option to the right administrators and only for accounts that can safely use it. This prevents manually configured WhatsApp accounts from accidentally losing incoming messages and avoids access errors for regular internal users.
Original PR description
It was previously deprecated in a different commit. task-6544628
When users update the shipment weight while adding shipping to a sales order, the system now saves that change before recalculating carrier rates. This prevents outdated shipping prices from being shown and makes rate comparisons more reliable.
Original PR description
Steps to reproduce: 1. Open a Sales Order 2. Click Add Shipping 3. Click `Get all rates` button 4. Change the weight 5. Click `Get all rates` button again 6. Observe that `get_wizard_carrier_rate` was called with the same weight The problem is that the record is not saved before fetching the rates. This commit ensures it happens every time the button is clicked. Also, some code in the `choose.delivery.carrier` is not reusable. This commit fixes these issues. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix ensures cash basis tax amounts are properly matched when an invoice is reversed with a credit note, even when the transition account is not set for payment reconciliation. This helps keep accounting records accurate and avoids lingering unreconciled tax balances after reversals.
Original PR description
Steps:
---------
1. Activate Cash Basis
2. Set a tax as being Cash Basis
a. The transition account must have Payment Reconciliation == false
3. Create an Invoice with a tax on it, post it.
4. Create a Credit Note for that invoice, post it
The Tax Amount is not reconciled, but it must be.
If the transition account is Payment Reconciliation == true, it works.
Solution:
---------
Before unreconciling, we store the accounts that were previously stored. During reversal, lines that are not reconciled and are part of the move are auto-reconciled.
Most of the reversal reconciliation logic has been moved in the `_post` method, this is because we want this behavior to apply each time we _post and not only when passing through the wizard with a forced reversal.
task-6499274
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prScheduled financial reports now use the intended company instead of including data from every company available to the scheduler. This helps businesses with multiple companies receive accurate report emails for the selected company only.
Original PR description
This commit https://github.com/odoo/odoo/commit/94ef1127dfa8af17795f39590c8a37edfa117994 changed how with_company() works to ensure it only adds a new company to the allowed companies. This made the reports options include all companies when sent through the cron (since crons have every company enabled by default). This commit fixes this issue by relying on the context key allowed_company_ids instead of with_company(). task-6505248 Forward-Port-Of: odoo/enterprise#129728
The WhatsApp account screen now hides the Disconnect option for manually configured accounts and limits it to WhatsApp administrators. This prevents accidental service disruption where Meta stops sending messages while Odoo still shows the account as configured, and avoids access errors for regular internal users.
Original PR description
Problem: The Disconnect button showed on every account holding a token, so manually configured accounts got it too. Pressing it tells Meta to stop sending messages to the database, then fails while…
Problem: The Disconnect button showed on every account holding a token, so manually configured accounts got it too. Pressing it tells Meta to stop sending messages to the database, then fails while clearing the token, which a manual account needs, so Odoo rolls back while Meta does not. The account keeps looking configured and incoming messages stop, and there is no way back from the interface, Meta only allows re-subscribing through the API. The button relied on `show_disconnect`, a computed field reading `token`, which is restricted to WhatsApp administrators while every internal user has read access on `whatsapp.account`. Opening the account form as a regular user therefore raised an AccessError. Solution: Restrict the header to WhatsApp administrators and test the account fields directly in its `invisible`, which hides the button on manual accounts and removes the need for the computed field. Add a safety check on write access in `button_disconnect`. Hiding a button only removes it from the screen, the method stays callable over RPC, and the request to Meta happens before the write that would have been refused. Task-6544628 Forward-Port-Of: odoo/enterprise#130759
When a user fixes an accidental mention in a message, Odoo now correctly removes the replaced contact from the notification list. This prevents unintended recipients from being notified when their name only appears as part of a longer corrected mention.
Original PR description
When a user mentions the wrong partner and corrects it by continuing to type, e.g. picking "John" by mistake, typing further so the text becomes "@ John Doe" and picking "John Doe" in the suggestion…
When a user mentions the wrong partner and corrects it by continuing to type, e.g. picking "John" by mistake, typing further so the text becomes "@ John Doe" and picking "John Doe" in the suggestion popup, the discarded first pick stays in the composer's mentioned partners. On post, mentions are validated by searching the body for "@<name>", and "@ John" is found inside "@ John Doe", so the partner the user tried to replace is kept in the recipients and gets notified even though no mention of them remains visible in the message. Validate mentions from the longest mention text to the shortest, counting the occurrences of each text and blanking them out before looking for shorter ones. A partner whose mention text only appears inside a longer mention is dropped, while distinct partners sharing the same name each consume one occurrence. Steps to reproduce: - Create contacts "John" and "John Doe" - On any record, open the chatter and type "@John", pick "John" by mistake, then keep typing " Doe" and pick "John Doe" in the suggestion popup to correct it - Send the message => The message is also sent to "John" although only "@John Doe" appears in the body. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287267 Forward-Port-Of: odoo/odoo#284239
Time off requests now keep the correct duration when an automation rule creates an activity during save. This prevents one-day requests from incorrectly appearing as zero days, improving reliability for HR workflows that use automated reminders or tasks.
Original PR description
Problem: A time off request is saved with a duration of 0 days instead of 1 day as soon as an automation rule on Time Off has an action that creates an activity. Cause: `_compute_field_value` runs…
Problem: A time off request is saved with a duration of 0 days instead of 1 day as soon as an automation rule on Time Off has an action that creates an activity. Cause: `_compute_field_value` runs the actions while the fields of the computation it wraps are still protected and not written yet. On `hr.leave` the actions run from a computation nested in `_compute_date_from_to`, so `date_from` is empty when the activity notification reads `display_name`, and `_compute_duration` stores 0. Setting `date_from` afterwards does not mark `number_of_days` to compute again since it is protected. Solution: Extract the snapshot `_filter_pre` already does into `_keep_to_compute` and wrap the post filter and the actions with it, so the fields depending on the running computation stay to compute. Same treatment as 488419a5ca49 (odoo/odoo#243611) on the pre filter. Steps to reproduce: - Enable the developer mode. - Go to Settings > Technical > Automation Rules and create a rule on the Time Off model with the trigger On create and edit. - Add an action of type Create Activity, set its Responsible to another user, and save. - Go to Time Off > New, pick an employee and a time off type, and request one working day. - Observe that the request shows a duration of 0 days. Ticket [link](https://www.odoo.com/odoo/project.task/6498783) opw-6498783 Forward-Port-Of: odoo/odoo#287536 Forward-Port-Of: odoo/odoo#286791
Inventory overview buttons now appear correctly without requiring batch picking to be enabled. Related settings were also aligned so transport management correctly depends on batch, wave and cluster transfers, reducing configuration confusion.
Original PR description
During the PR [^1], the group `stock.group_stock_picking_batch` was accidentally added on the buttons to quickly see the number of opened pickings by operation type. As result, those buttons weren't displayed until the batch picking setting was enabled. This commit fixes that issue. [^1]: https://github.com/odoo/odoo/pull/282884
Mobile dashboard carousels now use the correct figure size when calculating their layout. This prevents carousel content from appearing at the wrong size on phones, improving readability and usability for dashboard users.
Original PR description
Now the carousels in o-spreadsheet use the figure dimension to compute their layout. It means that the previous implementation of the mobile dashboard where we gave the wrong figure size and relied on the css to resize the figure was not working anymore. This commit correcly size the figure POJO passed as props of the mobile dashboard figures. Task: [6534166](https://www.odoo.com/web#id=6534166&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixes several Belgian payroll issues affecting CP200 sectoral bonuses, including eligibility for departing employees, bonus caps, prorated amounts, and gross income reporting. This helps ensure final payslips, tax forms, and payroll declarations reflect the correct bonus amounts.
Original PR description
- Employment bonus cap read 'BONUS_ONSS' / 'BONUS_TOTAL'. Neither rule exists Use ONSS_BONUS. - Eligibility only checked contract_date_end, which is set on the employee only once the departure is applied. Accept departure_date too, like the eco-vouchers rule. - BONUS_GROSS was missing from the monthly structure: empty total gross on the payslip, and the 281.10 declared the tax without the income. and BONUS_GROSS and BONUS_PP now display only when non zero. - Worked day lines credited only the immediate parent category, every other path walks the whole chain, now it goes up the parent chain recursivly - Drop the unused bonus withholding helper and an unreachable guard in the prorata loop. task - 6032929
Changing a Belgian employee’s contract start date no longer overwrites a manually selected joint committee. This preserves payroll setup choices for student employees while still applying standard defaults when appropriate.
Original PR description
Steps to reproduce: - Create a Belgian employee, select Student and choose a joint committee. - Save the employee, then set or change the contract start date. - The committee is replaced by the default or cleared. Task 6542868
This fixes VoIP so the same user can receive calls reliably when signed in from multiple browser tabs or devices. It also aligns allowed extension numbers with the phone service range, preventing invalid extension setups and keeping configuration simpler.
Original PR description
## [FIX] voip: one registration binding per softphone window Asterisk identifies a registration binding by its Contact URI alone: the registrar compares candidates with pjsip_uri_cmp() and names the…
## [FIX] voip: one registration binding per softphone window Asterisk identifies a registration binding by its Contact URI alone: the registrar compares candidates with pjsip_uri_cmp() and names the stored object after the MD5 of that URI. Neither `+sip.instance` nor `reg-id` is consulted, RFC 5626 section 5.4.1 not being implemented on the inbound registrar side. Every window of a user therefore advertised the same Contact and collapsed into a single binding, so only the last one to REGISTER could be rung - across tabs and across devices alike. The discriminator has to be a URI parameter rather than part of the user part: res_pjsip_path.c matches the user part against the AOR name to decide whether to attach the edge proxy's RFC 5626 flow-token Path, and without that Path an inbound INVITE cannot be routed to the browser at all. Parameters after the host are ignored by that lookup yet do make two URIs compare unequal, so `x-odoo-unique` buys distinct bindings at no cost to the Path. It is assigned once per user agent instead of being computed in the configuration getter, so that reading the configuration twice cannot yield two different Contact URIs. ## [FIX] voip: restrict voip.extension range Before this commit: - voip module validates against voip.extension number length: between 100 and 99999 - phone service configures tenants to allow extensions between 10 and 9999 As there is a discrepancy between those 2, we decided to allow the ranges that overlap between both parts. So, after this commit: - voip module allows extensions numbers between 100 and 9999. Rationale We decided to remove the 5-number range on that part instead of adding it in the phone service part, because: - 9900 extension numbers is plenty enough and it can lower a bit the complexity of the asterisk dialplan, which makes the asterisk dialplan regeneration more lightweight - it is easier to add a range in the future than remove one, due to migration nightmare
This fixes an issue where some SEPA direct debit payments could be created without the required mandate linked. The change helps ensure direct debit payment flows remain reliable and avoids failures caused by missing mandate information.
Original PR description
When registering a payment (e.g. through the payment register wizard), the mandate was not always linked to the created payment, leaving `mandate_id` empty and breaking SEPA direct debit…
When registering a payment (e.g. through the payment register wizard), the mandate was not always linked to the created payment, leaving `mandate_id` empty and breaking SEPA direct debit flows(`account.direct.debit.mandate()` instead of the expected mandate). During `create`, the `_validate_mandate_id` constraint reads `needs_mandate_of_type`, a non-stored computed field. Its computation triggers a recompute of `payment_method_id`, which re-enters the same constraint while `needs_mandate_of_type` is still being computed. The ORM then protects the field: reading it returns a bogus `False` and caches it. `_compute_mandate_id` consumes that poisoned value and permanently stores an empty `mandate_id`, since its actual dependencies never change afterwards. Fix: make `_compute_needs_mandate_type` and `_compute_mandate_id` depend on `payment_method_line_id` instead of `payment_method_id`, so mandate is computed without reading a field that can be in a protected context. note: I was only able to replicate the runbot error through testing, and not through web ui/functionally. runbot-error-946999 runbot-error-947001 runbot-error-947010 runbot-error-947011 runbot-error-947012 runbot-error-947013 runbot-error-947014 runbot-error-947015 runbot-error-947016