Daily updates from Odoo
Thursday, August 20, 2026
11 changes · 19.0
Enhancements to existing features
Users can again use folder action menus when working with document views embedded in spreadsheets or knowledge articles. This makes it easier to share and reuse live folder views while keeping shared access tokens protected from unintended exposure.
Original PR description
Also impacted: test_documents_full It is convenient to export a dynamic view of a folder in both spreadsheet and knowledge links settings. * Care is taken to avoid leaking access folders tokens through the search panel/model's state in knowledge. * We also enable sharing folders shared via link through embedded views as it enables benefitting from the power of them vs. adding the link to the folder in the article. * As with other actions initiated on shortcuts, the "real" operation is done on the target. Sharing the target is simpler than patching a folder "child_of" to return the target children (shortcut as documents_unique_folder_id is not supported). Task-5180137
Turkish payroll settings are updated for 2026, including clearer minimum wage naming and new configurable social security contribution values. This helps payroll teams calculate SSI contributions more accurately within the updated minimum and maximum contribution limits.
Original PR description
- Update the Turkish payroll rule parameters for 2026. - Rename the minimum wage parameter to 'Turkiye Minimum Net Wage'. - Add configurable parameters for the SSI minimum contribution base and employee contribution rate. - Update the SSI contribution computation to account for both the minimum and maximum contribution bases. **task-6397284**
Resolved issues and error corrections
Australian payroll batches can now include employees with and without leave allocations without causing an error. This helps payroll teams process mixed payslip batches reliably, with missing unused leave values treated as zero instead of blocking the batch.
Original PR description
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError: ``` File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip…
Creating a batch of payslips mixing employees with and without leave allocations raises a KeyError:
```
File "l10n_au_hr_payroll/models/hr_payslip.py", line 869, in _add_unused_leaves_to_payslip
annual_gross = leaves_totals[payslip.id]['annual'] * daily_wage
~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 22
```
Current Issue:
`_l10n_au_get_unused_leave_by_type` only materialises leaves_by_date[payslip.id] inside the allocation loop, so a payslip whose employee has no matching allocation never gets a key. `_l10n_au_get_unused_leave_totals` then rebuilt a plain dict out of those entries and only fell back to a defaultdict when leaves_by_date was completely empty. A mixed batch is not empty, so the plain dict was returned and `_add_unused_leaves_to_payslip` raised on the payslips that were missing from it.
This never showed up in the UI, **where payslips are created one at a time**: a single slip either has an allocation, or produces an empty mapping that hits the fallback.
Approach:
Build the totals on a defaultdict and update it instead of returning a plain dict, so any payslip without allocation resolves to 0 rather than being absent. This also drops the need for the empty special case, and keeps the mapping consistent with the defaultdict returned by `_l10n_au_get_unused_leave_by_type`, which `_l10n_au_get_leaves_for_withhold` indexes the same way.
task-6465229Odoo now correctly updates VoIP call records when a call is answered or rejected in another phone application such as Linphone. This prevents those calls from being incorrectly marked as missed, giving users a more accurate call history when Odoo is open alongside external VoIP tools.
Original PR description
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and…
Steps to reproduce: - Have an external VoIP software configured (e.g. Linphone) - Have your Odoo configured and opened too - Call your VoIP number, using your smartphone => Both the softphone and Linphone ring - Answer or reject using Linphone => The VoIP call record in Odoo immediately switches from "Trying to call" to "Missed". While there was no guarantee for our VoIP integration to work alongside Linphone in 19.0, we decided this should be an easy safe enough fix. Starting 19.2 (with [1]), the fix will be simplified and hopefully prettier thanks to the ameliorations that were made. After this fix, provided Odoo is open while Linphone is used, the call records will now switch to the right terminated / rejected status, still immediately once Linphone answers / rejects. In future versions and especially 20.0+, the system will be different and will allow way more features like this one to work better (e.g. here the call record only even exists if Odoo is opened while using Linphone and we won't have any information about the call duration). [1]: https://github.com/odoo/enterprise/commit/942f32316ab02d8c739fe7fdd5ec2bdde472a68e task-6449259
Auto planning for monthly schedules now correctly includes the last day of the selected month. This prevents working time from being left unscheduled because of timezone conversion issues, improving reliability for sales planning and resource scheduling.
Original PR description
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To…
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To Plan" button, then click "Auto Plan". 4. Make sure the "Month" filter is selected in the scale options and observe the planned slots. Issue: -------- When auto planning slots for a month, the last day of the month is excluded. For example, slots are scheduled only until July 30th, even though July 31st is a working day. Cause: -------- While preparing the context, `stopDate` is set to July 31st at 00:00. It is then passed to [serializeDateTime()](https://github.com/odoo/odoo/blob/dacaad91bba8f959daf5d89a046c5a1c11e48eec/addons/web/static/src/core/l10n/dates.js#L553-L560), which converts the datetime to UTC. Depending on the user's timezone, this can shift the date to the previous day, causing the last day of the month to be excluded. Solution: ------------ Use `localEndOf()` to set `stopDate` to the local end of the selected range before passing it to `serializeDateTime()`. This ensures the last day of the month is preserved during UTC conversion. **NOTE:** Forward-port the solution from the 18.0 version, which was adapted to the publish shift use case in 18.3 and introduced this issue. Add a HOOT test case to prevent this regression in future versions. References: [18](https://github.com/odoo/enterprise/commit/bc3db24f83473d5646f7c2cfca8ed1c5b064ea2e) and [saas-18.3](https://github.com/odoo/enterprise/commit/c81fba31780869940f726b695ad46a87f69798fb) opw-6391495
Internal transfers between a branch and its parent company can now be reconciled even when the branch transaction was processed with a reconciliation model. This prevents erroneous company mismatch errors and keeps branch-to-company bank matching working as expected.
Original PR description
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: -…
When reconciling an internal transfer between a branch and its parent company, an User Error is raised if the branch transaction has been processed via reconciliation model. Steps to reproduce: - Have a company with branch both selected - On the branch, create a reconciliation model "Internal transfer" that assigns the whole balance to the liquidity transfer account - Have a Bank journal on the company and a Bank journal on the branch - On the branch bank journal, create a -100 transaction 'testb' and reconcile it using the branch internal transfer model - On the company bank journal, creata a 100 transaction, open the reconciliation widget and select the branch transaction to match it Issue: The reconciliation is refused with a company inconsistency error ``` Uh-oh! You’ve got some company inconsistencies here: - “BNK1/2026/00011 test” belongs to company “YourCompany” while “Reconciliation Model” (reconcile_model_id: 'Internal Transfer branch') belongs to another company. To avoid a mess, no company crossover is allowed! ``` However, if user manually assign the transfer account to the branch transaction, the reconciliation proceed as expected Analysis: When reconciling, we build the counterpart journal item by cloning the values of the matched move line, copying also the reconcile model. That field is company dependent and flagged copy=False, so it should not be propagated. opw-6365856
Resetting a bank statement line to draft no longer triggers an error caused by an unexpected system response. This helps accounting users complete transaction corrections without interruption.
Original PR description
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on…
Currently, an error occurs when resetting a **bank** statement line to draft. **Steps to Reproduce:** - Install the `Accounting` module. - Go to `Accounting Dashboard` and click the `three-dot` on bank journal. - Open `Transactions`. - Create a new `statement line`. - Select the `statement line`, click the `gear action`, and click `Reset to Draft`. `AttributeError: 'bool' object has no attribute 'setdefault'` After the [recent commit], when resetting the statement line to draft, the server action run [1] and the linked move is going reset to draft, and the method returns the result [2]. After the mentioned commit, the method returns True [3]. When the result from [4] is passed to clean_action, it raises an error [5]. This commit ensures that it returns None after resetting the statement line linked to the invoice to draft, as it previously returned None and same as like [6]. [recent commit]: https://github.com/odoo/odoo/commit/712718d9df0fd5044ac57fdd9ec58e64bece36c0 [1]- https://github.com/odoo/enterprise/blob/7ca28a1c079d22b60e3756ca9b4f404771f214e8/account_accountant/views/bank_rec_widget_views.xml#L541-L551 [2]- https://github.com/odoo/enterprise/blob/eae51e3ca155ca29e1de54f4bd223e7540b2aa7f/account_accountant/models/account_bank_statement.py#L112-L114 [3]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/account/models/account_move.py#L6236-L6251 [4]: https://github.com/odoo/odoo/blob/a6f99706c6a62fc65666a0ff5e58fa465b41a6fb/addons/web/controllers/action.py#L53-L59 [5]: https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/web/controllers/utils.py#L24 [6]: https://github.com/odoo/odoo/blob/d7df2e8acff9eb7066993fa6a0b0c6d7c85baabc/addons/account/models/account_payment.py#L1206-L1208 sentry-7354160052
The TikTok sales integration now skips shops that have not completed authorization when syncing orders. This prevents scheduled order imports from failing due to missing authorization details, keeping the sync process stable for connected shops.
Original PR description
Currently, an error occurs when orders are being fetched from shops with pending authorization. Steps to replicate: - Install `sale_tiktok`. - Open Sales > Configuration > Shops (Under the title…
Currently, an error occurs when orders are being fetched from shops with pending authorization.
Steps to replicate:
- Install `sale_tiktok`.
- Open Sales > Configuration > Shops (Under the title tiktok shops).
- Click `Connect New Shop` > Give values for `App key, App secret, Service ID`.
- Click `Connect Shop & Authorize` and then Return back to Odoo.
- Run the Scheduled Action `TikTok Shop: sync orders`.
Error:
```
File '/home/odoo/src/enterprise/saas-19.4/sale_tiktok/utils.py', line 171, in make_tiktok_api_request
if now > shop.access_token_expire_datetime - timedelta(minutes=5):
TypeError: unsupported operand type(s) for -: 'bool' and 'datetime.timedelta'
ValueError: TypeError('unsupported operand type(s) for -: 'bool' and 'datetime.timedelta'') while evaluating
'model._sync_orders()'
```
Cause:
- Since the shop has not yet been authorized with TikTok, the `access_token_expire_datetime` field is not set. This field is only populated after the shop is successfully authorized (see [this]).
- Later, when the `TikTok Shop: sync orders` cron runs, the flow reaches [here], where we checks whether the access token is expired and needs to be refreshed. At this point, `access_token_expire_datetime` is still False because the shop has not been authorized yet.
Solution:
- The orders should only be fetched from those shops that are authorized with TikTok.
- Used the `access_token` field to determine whether a shop is authorized, as it is only populated after the authorization flow is successfully completed.
[this]: https://github.com/odoo/enterprise/blob/593409ca160863d3e985f3cf5e2aadda1d450b41/sale_tiktok/controllers/onboarding.py#L52-L54
[here]: https://github.com/odoo/enterprise/blob/593409ca160863d3e985f3cf5e2aadda1d450b41/sale_tiktok/utils.py#L171
sentry-7631179329Barcode deliveries now use the real storage location of a scanned serial-numbered item, even when the workflow does not ask workers to scan a source location. This prevents stock from being deducted from the wrong warehouse location, avoiding inaccurate inventory balances such as stale or negative quantities.
Original PR description
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial…
Steps to reproduce --- 1. Enable Storage Locations and Lots/Serial Numbers. 2. Set the delivery operation type's "Source Location" to "Do not scan". 3. Create a serial-tracked product with a serial stored in a sublocation (e.g. WH/Stock/Section 2). 4. Confirm a sale order for it, open the delivery in Barcode, and scan an unreserved serial. Issue --- Scanning the unreserved serial creates a new move line that falls back to _defaultLocation() because the decoded scan carries no source location (the operation type does not require scanning one) and never carries the serial's quant location. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_model.js#L937-L944 For a delivery, that default resolves to the picking's own source location (the parent WH/Stock), so the line is sourced from the parent instead of the sublocation where the serial physically sits. https://github.com/odoo/enterprise/blob/f42cfa7265ce32bd95f7f805cf4fb54d8b79cce1/stock_barcode/static/src/models/barcode_picking_model.js#L1542-L1544 On validation the unit is deducted from the parent location instead of the sublocation, leaving a stale quant of the serial in the sublocation and a negative quant at the parent. opw-5864414
Fixes a General Ledger issue where opening balances could show the wrong foreign currency amount or a missing currency when companies with different currencies share the same chart of accounts. This prevents misleading balances in multi-company accounting reports.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared…
**Steps to reproduce:** * Install the **Accounting** module. * Create two companies with different currencies (e.g. **USD** and **CAD**) sharing the same **Chart of Accounts**. * Open a shared account and: * Add both companies in the **Company** field. * Under the **Mappings** tab, configure a mapping for each company. * In each company, create and post a journal entry on the same shared account (for example, a receivable account) using the company's own currency. * Set the journal entry dates to the **current month**. * Open **Accounting → Reporting → General Ledger**. * Change the reporting period to the **following month** so the posted entries are shown as the **Initial Balance**. * Open the report separately for each company. **Observed behavior:** * From the **CAD company**, the Initial Balance displays **USD 2,000** instead of the expected **USD 1,000**. * From the **USD company**, the **Currency** column on the Initial Balance is **blank**. **Cause:** * The SQL query for the `id_with_accumulated_balance` groupby used `SUM(amount_currency)` and `MIN(currency_id)` to aggregate all pre-period lines into a single Initial Balance row. * In a multi-company shared Chart of Accounts, lines from different companies (each with their own currency) were collapsed into the same group, causing `SUM(amount_currency)` to add amounts across currencies and `MIN(currency_id)` to return an arbitrary currency ID. * Additionally, the Python accumulation loop incorrectly performed **integer addition** on `currency_id` (a foreign key), further corrupting the displayed currency. **Fix:** * Replace `SUM(amount_currency)` and `MIN(currency_id)` with `CASE` expressions `MIN = MAX` is a uniformity check that works for **any number of currencies**: if every row in the group shares the same currency the condition is true and the correct sum is returned; if even one row differs the condition is false and both fields return `NULL`. The original three-column `GROUP BY (id, date, account_id)` is preserved. * The Initial Balance row now correctly shows a **blank** currency column, consistent with the Odoo 18 behavior, instead of an incorrect aggregated foreign currency amount. opw-6375310
This fix prevents barcode-related inventory screens from failing when tracked and untracked stock movement lines appear together. It keeps tracking details correctly matched to the right lines and shows a clear message for lines without tracking information instead of causing an error.
Original PR description
Problem: `_compute_electronic_product_code` built `tracking_number_list` by filtering out move lines without a lot_id/lot_name, but kept iterating over the full, unfiltered `move_line_ids`. As soon as a tracked product had an untracked move line mixed in with tracked ones (e.g. a manufacturing byproduct move line with no lot), the two lists fell out of sync: at best tracking numbers got assigned to the wrong move line, at worst `tracking_number_list[i]` went out of range and raised an IndexError. Solution: Exclude untracked move lines from `move_line_ids` before building `tracking_number_list`, so both stay the same length and index- aligned. Untracked lines get their own explicit "no tracking number" error instead of breaking the alignment for the rest. Steps to reproduce: Open runbot V19 -> go to moves history (Inventory) -> add `electronic_product_code` to list view using studio -> remove filter/select all records -> https://anotepad.com/notes/jwxyskc2