Daily updates from Odoo
Friday, June 19, 2026
9 changes · 19.0
Enhancements to existing features
This update enhances the system's ability to process payments through Powens and Saltedge by adding debtor and creditor information to the data sent to our payment processor. This change ensures accurate payment initiation and improved integration with payment gateways.
Original PR description
In order to be able to initiate payments using Powens and Saltedge, we need to include debtor information in the payload sent to Odoofin. This commit adds the necessary fields and updates the tests accordingly. Task ID: 5977148, 6095729 Forward-Port-Of: odoo/enterprise#119843
Resolved issues and error corrections
This update resolves an issue preventing users with Sales access from inserting data into Quotation templates through the spreadsheet management feature. The change adds a setting to ensure the necessary flag is enabled when the module is installed and the user has the correct permissions, improving usability.
Original PR description
Current behavior before PR: - The `can_insert_in_spreadsheet` session flag was not set by the spreadsheet_sale_management module. - Users with proper Sale access rights still could not insert into Quotation templates. Desired behavior after PR is merged: - Added logic to set `can_insert_in_spreadsheet` when the module is installed and the user has the required access rights. Task: [5960761](https://www.odoo.com/odoo/project/2328/tasks/5960761) Forward-Port-Of: odoo/enterprise#120903 Forward-Port-Of: odoo/enterprise#108674
This update significantly speeds up the generation of key financial reports, specifically the Trial Balance and Unallocated Earnings reports. By removing unnecessary checks and adding a new database index, the system processes these reports much faster, reducing processing times by up to 70%.
Original PR description
* By removing the access right check, we often are able to remove a `JOIN` on `account_move`. This `JOIN` was forcing the planner to take a worse plan and do a lot more work. * The new index allows…
* By removing the access right check, we often are able to remove a `JOIN` on `account_move`. This `JOIN` was forcing the planner to take a worse plan and do a lot more work.
* The new index allows many optimizations in the query plan, including removing the need for various `Scan`, `Nested Loop` nodes.
| | Unallocated Earnings | Trial Balance | Code Engine |
|------------|----------------------|---------------|-------------|
| Before | 38s | 22s | 15s |
| W/O access | 11s | 6s | 5s |
| & W Index | 6s | 6s | 7s |
Note that these timings are done on hot queries. The index would help in all the cases because it allows to avoid many Rows Removed by Filter.
Future work will also improve the cases where the domain contains:
```python
[
'|',
('account_id.include_initial_balance', '=', True),
('date', '>=', fiscalyear_start),
]
```
When splitting this in two queries, the new index also helps keeping the number of rows filtered very low.
________________________________________________________________________
# `_get_unallocated_earnings_lines`
## Before
```sql
SELECT account_move_line.company_id,
COALESCE(SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)), 0.0) AS balance,
COALESCE(SUM((account_move_line.debit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS debit,
COALESCE(SUM((account_move_line.credit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS credit
FROM "account_move_line" LEFT JOIN "account_account" AS "account_move_line__account_id" ON ("account_move_line"."account_id" = "account_move_line__account_id"."id") JOIN "account_move" AS "account_move_line__move_id" ON ("account_move_line"."move_id" = "account_move_line__move_id"."id")
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = '_trial_balance_middle_periods' OR account_currency_table.period_key IS NULL)
WHERE (("account_move_line"."account_id" IS NOT NULL AND ("account_move_line__account_id"."account_type" IN ('equity_unaffected') OR split_part("account_move_line__account_id"."account_type", '_', 1) IN ('income', 'expense'))) AND "account_move_line"."company_id" IN (1) AND "account_move_line"."date" < '2026-01-01'::date AND "account_move_line"."date" <= '2026-04-30'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted')) AND (("account_move_line"."company_id" IN (1) OR "account_move_line"."company_id" IS NULL) OR ("account_move_line__move_id"."invoice_user_id" IN (1078415) OR "account_move_line__move_id"."partner_id" IN (1889993)))
GROUP BY account_move_line.company_id;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=1001.44..1409209.41 rows=1 width=100) (actual time=38461.802..38461.890 rows=1 loops=1)
-> Gather (cost=1001.44..1409209.38 rows=1 width=100) (actual time=38461.786..38461.877 rows=2 loops=1)
Workers Planned: 1
Workers Launched: 1
-> Partial GroupAggregate (cost=1.44..1408209.28 rows=1 width=100) (actual time=32388.314..32388.316 rows=1 loops=2)
-> Nested Loop (cost=1.44..1407952.19 rows=17138 width=18) (actual time=0.371..31450.650 rows=2829550 loops=2)
-> Nested Loop (cost=1.00..1371951.59 rows=38651 width=22) (actual time=0.286..20582.850 rows=2829550 loops=2)
-> Parallel Index Scan using account_account_pkey on account_account account_move_line__account_id (cost=0.29..1862.11 rows=66 width=4) (actual time=0.210..5.952 rows=1936 loops=2)
Filter: (((account_type)::text = 'equity_unaffected'::text) OR (split_part((account_type)::text, '_'::text, 1) = ANY ('{income,expense}'::text[])))
Rows Removed by Filter: 2294
-> Index Scan using account_move_line_account_id_date_idx on account_move_line (cost=0.72..20697.02 rows=6191 width=26) (actual time=5.337..10.510 rows=1462 loops=3872)
Index Cond: ((account_id = account_move_line__account_id.id) AND (account_id IS NOT NULL) AND (date < '2026-01-01'::date) AND (date <= '2026-04-30'::date))
Filter: ((company_id = 1) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Rows Removed by Filter: 1570
-> Index Scan using account_move_pkey on account_move account_move_line__move_id (cost=0.43..0.92 rows=1 width=12) (actual time=0.004..0.004 rows=1 loops=5659099)
Index Cond: (id = account_move_line.move_id)
Filter: ((account_move_line.company_id = 1) OR (account_move_line.company_id IS NULL) OR (invoice_user_id = 1078415) OR (partner_id = 1889993))
Planning Time: 9.433 ms
Execution Time: 38461.932 ms
```
## Without access rights
```sql
SELECT account_move_line.company_id,
COALESCE(SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)), 0.0) AS balance,
COALESCE(SUM((account_move_line.debit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS debit,
COALESCE(SUM((account_move_line.credit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS credit
FROM "account_move_line" LEFT JOIN "account_account" AS "account_move_line__account_id" ON ("account_move_line"."account_id" = "account_move_line__account_id"."id")
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = '_trial_balance_middle_periods' OR account_currency_table.period_key IS NULL)
WHERE (("account_move_line"."account_id" IS NOT NULL AND ("account_move_line__account_id"."account_type" IN ('equity_unaffected') OR split_part("account_move_line__account_id"."account_type", '_', 1) IN ('income', 'expense'))) AND "account_move_line"."company_id" IN (1) AND "account_move_line"."date" < '2026-01-01'::date AND "account_move_line"."date" <= '2026-04-30'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted'))
GROUP BY account_move_line.company_id;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=0.72..2326905.01 rows=1 width=100) (actual time=11392.164..11392.166 rows=1 loops=1)
-> Nested Loop (cost=0.72..2325919.40 rows=65706 width=18) (actual time=0.753..9939.918 rows=5659099 loops=1)
-> Seq Scan on account_account account_move_line__account_id (cost=0.00..919.07 rows=112 width=4) (actual time=0.011..4.111 rows=3872 loops=1)
Filter: (((account_type)::text = 'equity_unaffected'::text) OR (split_part((account_type)::text, '_'::text, 1) = ANY ('{income,expense}'::text[])))
Rows Removed by Filter: 4589
-> Index Scan using account_move_line_account_id_date_idx on account_move_line (cost=0.72..20697.02 rows=6191 width=22) (actual time=1.180..2.469 rows=1462 loops=3872)
Index Cond: ((account_id = account_move_line__account_id.id) AND (account_id IS NOT NULL) AND (date < '2026-01-01'::date) AND (date <= '2026-04-30'::date))
Filter: ((company_id = 1) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Rows Removed by Filter: 1570
Planning Time: 0.630 ms
Execution Time: 11392.193 ms
```
## With the new index
```sql
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=0.72..1224754.13 rows=1 width=100) (actual time=6021.088..6021.090 rows=1 loops=1)
-> Nested Loop (cost=0.72..1223768.52 rows=65706 width=18) (actual time=0.140..4563.265 rows=5659099 loops=1)
-> Seq Scan on account_account account_move_line__account_id (cost=0.00..919.07 rows=112 width=4) (actual time=0.009..3.596 rows=3872 loops=1)
Filter: (((account_type)::text = 'equity_unaffected'::text) OR (split_part((account_type)::text, '_'::text, 1) = ANY ('{income,expense}'::text[])))
Rows Removed by Filter: 4589
-> Index Scan using idx_aml_reporting on account_move_line (cost=0.72..10856.39 rows=6191 width=22) (actual time=0.002..1.080 rows=1462 loops=3872)
Index Cond: ((company_id = 1) AND (account_id = account_move_line__account_id.id) AND (account_id IS NOT NULL) AND (date < '2026-01-01'::date) AND (date <= '2026-04-30'::date) AND ((parent_state)::text = 'posted'::text))
Filter: (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[]))
Rows Removed by Filter: 0
Planning Time: 1.258 ms
Execution Time: 6021.114 ms
```
# `_report_custom_engine_trial_balance`
## Before
```sql
SELECT
"account_move_line"."account_id" AS "groupby_key_account_id",
COALESCE(SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)), 0.0) AS balance,
COALESCE(SUM((account_move_line.debit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS debit,
COALESCE(SUM((account_move_line.credit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS credit
FROM "account_move_line" LEFT JOIN "account_account" AS "account_move_line__account_id" ON ("account_move_line"."account_id" = "account_move_line__account_id"."id") JOIN "account_move" AS "account_move_line__move_id" ON ("account_move_line"."move_id" = "account_move_line__move_id"."id")
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = '_trial_balance_middle_periods' OR account_currency_table.period_key IS NULL)
WHERE ("account_move_line"."company_id" IN (1) AND "account_move_line"."date" <= '2026-04-30'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted') AND (("account_move_line"."account_id" IS NOT NULL AND ("account_move_line__account_id"."account_type" NOT IN ('equity_unaffected') AND (split_part("account_move_line__account_id"."account_type", '_', 1) NOT IN ('income', 'expense') OR split_part("account_move_line__account_id"."account_type", '_', 1) IS NULL))) OR "account_move_line"."date" >= '2026-01-01'::date)) AND (("account_move_line"."company_id" IN (1) OR "account_move_line"."company_id" IS NULL) OR ("account_move_line__move_id"."invoice_user_id" IN (1078415) OR "account_move_line__move_id"."partner_id" IN (1889993)))
GROUP BY "groupby_key_account_id";
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=3845774.51..3845891.36 rows=779 width=100) (actual time=22590.401..22953.611 rows=622 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=3845774.51..3845864.10 rows=779 width=100) (actual time=22590.390..22952.586 rows=1116 loops=1)
Workers Planned: 1
Workers Launched: 1
-> Sort (cost=3844774.50..3844776.45 rows=779 width=100) (actual time=22562.645..22562.672 rows=558 loops=2)
Sort Key: account_move_line.account_id
Sort Method: quicksort Memory: 151kB
Worker 0: Sort Method: quicksort Memory: 148kB
-> Partial HashAggregate (cost=3844723.45..3844737.09 rows=779 width=100) (actual time=22562.148..22562.519 rows=558 loops=2)
Group Key: account_move_line.account_id
Batches: 1 Memory Usage: 1065kB
Worker 0: Batches: 1 Memory Usage: 1065kB
-> Hash Left Join (cost=1139137.12..3819476.57 rows=1442679 width=18) (actual time=10306.112..21012.284 rows=4067550 loops=2)
Hash Cond: (account_move_line.account_id = account_move_line__account_id.id)
Filter: (((account_move_line.account_id IS NOT NULL) AND ((account_move_line__account_id.account_type)::text <> 'equity_unaffected'::text) AND ((split_part((account_move_line__account_id.account_type)::text, '_'::text, 1) <> ALL ('{income,expense}'::text[])) OR (split_part((account_move_line__account_id.account_type)::text, '_'::text, 1) IS NULL))) OR (account_move_line.date >= '2026-01-01'::date))
Rows Removed by Filter: 2829550
-> Parallel Hash Join (cost=1138175.75..3814586.90 rows=1494949 width=22) (actual time=6131.481..19429.577 rows=6897099 loops=2)
Hash Cond: (account_move_line.move_id = account_move_line__move_id.id)
Join Filter: ((account_move_line.company_id = 1) OR (account_move_line.company_id IS NULL) OR (account_move_line__move_id.invoice_user_id = 1078415) OR (account_move_line__move_id.partner_id = 1889993))
-> Parallel Index Scan using idx_wan on account_move_line (cost=0.56..2667561.57 rows=3371483 width=30) (actual time=0.182..10737.129 rows=6897099 loops=2)
Index Cond: ((company_id = 1) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Filter: ((date <= '2026-04-30'::date) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])))
Rows Removed by Filter: 303744
-> Parallel Hash (cost=1085103.97..1085103.97 rows=4245697 width=12) (actual time=6087.945..6087.946 rows=4795563 loops=2)
Buckets: 16777216 Batches: 1 Memory Usage: 539328kB
-> Parallel Seq Scan on account_move account_move_line__move_id (cost=0.00..1085103.97 rows=4245697 width=12) (actual time=0.132..4719.857 rows=4795563 loops=2)
-> Hash (cost=855.61..855.61 rows=8461 width=16) (actual time=4.600..4.601 rows=8461 loops=2)
Buckets: 16384 Batches: 1 Memory Usage: 535kB
-> Seq Scan on account_account account_move_line__account_id (cost=0.00..855.61 rows=8461 width=16) (actual time=0.013..3.674 rows=8461 loops=2)
Planning Time: 29.548 ms
Execution Time: 22953.702 ms
```
## Without access rights
```sql
SELECT
"account_move_line"."account_id" AS "groupby_key_account_id",
COALESCE(SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)), 0.0) AS balance,
COALESCE(SUM((account_move_line.debit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS debit,
COALESCE(SUM((account_move_line.credit) * COALESCE(account_currency_table.rate, 1)), 0.0) AS credit
FROM "account_move_line" LEFT JOIN "account_account" AS "account_move_line__account_id" ON ("account_move_line"."account_id" = "account_move_line__account_id"."id")
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = '_trial_balance_middle_periods' OR account_currency_table.period_key IS NULL)
WHERE ("account_move_line"."company_id" IN (1) AND "account_move_line"."date" <= '2026-04-30'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted') AND (("account_move_line"."account_id" IS NOT NULL AND ("account_move_line__account_id"."account_type" NOT IN ('equity_unaffected') AND (split_part("account_move_line__account_id"."account_type", '_', 1) NOT IN ('income', 'expense') OR split_part("account_move_line__account_id"."account_type", '_', 1) IS NULL))) OR "account_move_line"."date" >= '2026-01-01'::date))
GROUP BY "groupby_key_account_id";
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=2875894.51..2876117.19 rows=779 width=100) (actual time=5505.661..5806.173 rows=622 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=2875894.51..2876076.29 rows=1558 width=100) (actual time=5505.647..5804.748 rows=1622 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=2874894.49..2874896.43 rows=779 width=100) (actual time=5491.557..5491.581 rows=541 loops=3)
Sort Key: account_move_line.account_id
Sort Method: quicksort Memory: 150kB
Worker 0: Sort Method: quicksort Memory: 144kB
Worker 1: Sort Method: quicksort Memory: 143kB
-> Partial HashAggregate (cost=2874843.44..2874857.07 rows=779 width=100) (actual time=5491.055..5491.420 rows=541 loops=3)
Group Key: account_move_line.account_id
Batches: 1 Memory Usage: 1065kB
Worker 0: Batches: 1 Memory Usage: 1065kB
Worker 1: Batches: 1 Memory Usage: 1065kB
-> Hash Left Join (cost=962.09..2834512.35 rows=2304634 width=18) (actual time=6.720..4466.785 rows=2711700 loops=3)
Hash Cond: (account_move_line.account_id = account_move_line__account_id.id)
Filter: (((account_move_line.account_id IS NOT NULL) AND ((account_move_line__account_id.account_type)::text <> 'equity_unaffected'::text) AND ((split_part((account_move_line__account_id.account_type)::text, '_'::text, 1) <> ALL ('{income,expense}'::text[])) OR (split_part((account_move_line__account_id.account_type)::text, '_'::text, 1) IS NULL))) OR (account_move_line.date >= '2026-01-01'::date))
Rows Removed by Filter: 1886366
-> Parallel Index Scan using account_move_line__company_id_index on account_move_line (cost=0.72..2827275.65 rows=2388134 width=22) (actual time=0.080..3433.378 rows=4598066 loops=3)
Index Cond: (company_id = 1)
Filter: ((date <= '2026-04-30'::date) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Rows Removed by Filter: 202669
-> Hash (cost=855.61..855.61 rows=8461 width=16) (actual time=6.531..6.531 rows=8461 loops=3)
Buckets: 16384 Batches: 1 Memory Usage: 535kB
-> Seq Scan on account_account account_move_line__account_id (cost=0.00..855.61 rows=8461 width=16) (actual time=0.022..5.568 rows=8461 loops=3)
Planning Time: 10.803 ms
Execution Time: 5806.252 ms
```
## With the new index
```sql
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=3804515.40..3804572.57 rows=200 width=100) (actual time=6225.615..6565.399 rows=622 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=3804515.40..3804562.07 rows=400 width=100) (actual time=6225.605..6564.588 rows=853 loops=1)
Workers Planned 2
Workers Launched: 2
-> Sort (cost=3803515.37..3803515.87 rows=200 width=100) (actual time=6211.591..6211.605 rows=284 loops=3)
Sort Key: account_move_line.account_id
Sort Method: quicksort Memory: 71kB
Worker 0: Sort Method: quicksort Memory: 68kB
Worker 1: Sort Method: quicksort Memory: 126kB
-> Partial HashAggregate (cost=3803504.23..3803507.73 rows=200 width=100) (actual time=6211.303..6211.503 rows=284 loops=3)
Group Key: account_move_line.account_id
Batches: 1 Memory Usage: 288kB
Worker 0: Batches: 1 Memory Usage: 288kB
Worker 1: Batches: 1 Memory Usage: 569kB
-> Parallel Append (cost=7.68..3778010.68 rows=2549355 width=100) (actual time=472.875..5759.570 rows=2937896 loops=3)
-> Parallel Bitmap Heap Scan on account_move_line (cost=380080.85..946147.41 rows=259602 width=100) (actual time=1418.158..3029.272 rows=1054880 loops=1)
Recheck Cond: ((journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])) AND (company_id = 1) AND (date <= '2026-04-30'::date) AND (date >= '2026-01-01'::date) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])))
-> BitmapAnd (cost=380080.69..380080.69 rows=623044 width=0) (actual time=1321.505..1321.506 rows=0 loops=1)
-> Bitmap Index Scan on account_move_line__journal_id_index (cost=0.00..158146.78 rows=14398682 width=0) (actual time=1100.520..1100.520 rows=14401687 loops=1)
Index Cond: (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[]))
-> Bitmap Index Scan on idx_aml_reporting (cost=0.00..221622.14 rows=1389653 width=0) (actual time=161.288..161.288 rows=1054880 loops=1)
Index Cond: ((company_id = 1) AND (date <= '2026-04-30'::date) AND (date >= '2026-01-01'::date) AND ((parent_state)::text = 'posted'::text))
-> Merge Join (cost=7.68..2819116.49 rows=2289753 width=100) (actual time=44.892..4575.995 rows=2586269 loops=3)
Merge Cond: (account_move_line_1.account_id = account_move_line__account_id.id)
-> Parallel Index Scan using idx_aml_reporting on account_move_line account_move_line_1 (cost=0.72..2853823.83 rows=2320470 width=18) (actual time=0.042..3730.932 rows=4598051 loops=3)
Index Cond: ((company_id = 1) AND (account_id IS NOT NULL) AND (date <= '2026-04-30'::date) AND ((parent_state)::text = 'posted'::text))
Filter: (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[]))
Rows Removed by Filter: 173
-> Index Scan using account_account_pkey on account_account account_move_line__account_id (cost=0.29..1071.81 rows=8349 width=4) (actual time=0.043..3.777 rows=4474 loops=3)
Filter: (((account_type)::text <> 'equity_unaffected'::text) AND ((split_part((account_type)::text, '_'::text, 1) <> ALL ('{income,expense}'::text[])) OR (split_part((account_type)::text, '_'::text, 1) IS NULL)))
Rows Removed by Filter: 3831
Planning Time: 1.112 ms
Execution Time: 6565.487 ms
```
# `_compute_formula_batch_with_engine_account_codes`
## Before
```sql
SELECT
account_move_line.account_id AS account_id,
SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)) AS sum,
COUNT(account_move_line.id) AS aml_count
FROM "account_move_line" JOIN "account_move" AS "account_move_line__move_id" ON ("account_move_line"."move_id" = "account_move_line__move_id"."id")
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = 'None_2026-06-10' OR account_currency_table.period_key IS NULL)
WHERE ("account_move_line"."company_id" IN (1) AND "account_move_line"."date" <= '2026-06-10'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted')) AND (("account_move_line"."company_id" IN (1) OR "account_move_line"."company_id" IS NULL) OR ("account_move_line__move_id"."invoice_user_id" IN (1078415) OR "account_move_line__move_id"."partner_id" IN (1889993)))
GROUP BY account_move_line.account_id;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=4119792.83..4119999.93 rows=779 width=44) (actual time=15387.279..15724.790 rows=839 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=4119792.83..4119974.61 rows=1558 width=44) (actual time=15387.268..15723.982 rows=2202 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=4118792.81..4118794.76 rows=779 width=44) (actual time=15360.304..15360.333 rows=734 loops=3)
Sort Key: account_move_line.account_id
Sort Method: quicksort Memory: 102kB
Worker 0: Sort Method: quicksort Memory: 99kB
Worker 1: Sort Method: quicksort Memory: 99kB
-> Partial HashAggregate (cost=4118745.66..4118755.40 rows=779 width=44) (actual time=15359.911..15360.156 rows=734 loops=3)
Group Key: account_move_line.account_id
Batches: 1 Memory Usage: 553kB
Worker 0: Batches: 1 Memory Usage: 553kB
Worker 1: Batches: 1 Memory Usage: 553kB
-> Parallel Hash Join (cost=1138175.34..4107895.66 rows=1085000 width=14) (actual time=4293.304..14318.483 rows=4681102 loops=3)
Hash Cond: (account_move_line.move_id = account_move_line__move_id.id)
Join Filter: ((account_move_line.company_id = 1) OR (account_move_line.company_id IS NULL) OR (account_move_line__move_id.invoice_user_id = 1078415) OR (account_move_line__move_id.partner_id = 1889993))
-> Parallel Seq Scan on account_move_line (cost=0.16..2963297.25 rows=2446945 width=22) (actual time=0.113..8270.809 rows=4681102 loops=3)
Filter: ((date <= '2026-06-10'::date) AND (company_id = 1) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Rows Removed by Filter: 6023968
-> Parallel Hash (cost=1085103.97..1085103.97 rows=4245697 width=12) (actual time=4266.392..4266.392 rows=3197042 loops=3)
Buckets: 16777216 Batches: 1 Memory Usage: 539360kB
-> Parallel Seq Scan on account_move account_move_line__move_id (cost=0.00..1085103.97 rows=4245697 width=12) (actual time=0.059..3265.827 rows=3197042 loops=3)
Planning Time: 6.028 ms
Execution Time: 15724.870 ms
```
## Without access rights
```sql
SELECT
account_move_line.account_id AS account_id,
SUM((account_move_line.balance) * COALESCE(account_currency_table.rate, 1)) AS sum,
COUNT(account_move_line.id) AS aml_count
FROM "account_move_line"
JOIN (VALUES (1, CAST(NULL AS VARCHAR), CAST(NULL AS DATE), CAST(NULL AS DATE), 'current', 1)) AS account_currency_table(company_id, period_key, date_from, date_next, rate_type, rate)
ON account_move_line.company_id = account_currency_table.company_id
AND (account_currency_table.period_key = 'None_2026-06-10' OR account_currency_table.period_key IS NULL)
WHERE ("account_move_line"."company_id" IN (1) AND "account_move_line"."date" <= '2026-06-10'::date AND "account_move_line"."display_type" NOT IN ('line_section', 'line_subsection', 'line_note') AND "account_move_line"."journal_id" IN (5, 181, 2, 407, 571, 570, 506, 169, 271, 167, 121, 6, 9, 241, 655, 623, 242, 63, 331, 123, 3, 18, 142, 4, 320, 170, 218, 236, 97, 143, 343, 64, 521, 660, 139, 344, 118, 119, 274, 179, 441, 619, 439, 89, 522, 91, 17, 8, 128, 135, 19, 10, 7, 180, 134, 124, 52, 53, 137, 98, 1, 96, 57) AND "account_move_line"."parent_state" IN ('posted'))
GROUP BY account_move_line.account_id;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=2852792.27..2852999.37 rows=779 width=44) (actual time=4663.910..4980.515 rows=839 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=2852792.27..2852974.05 rows=1558 width=44) (actual time=4663.900..4979.680 rows=2214 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=2851792.25..2851794.20 rows=779 width=44) (actual time=4650.874..4650.905 rows=738 loops=3)
Sort Key: account_move_line.account_id
Sort Method: quicksort Memory: 103kB
Worker 0: Sort Method: quicksort Memory: 99kB
Worker 1: Sort Method: quicksort Memory: 100kB
-> Partial HashAggregate (cost=2851745.10..2851754.84 rows=779 width=44) (actual time=4650.446..4650.708 rows=738 loops=3)
Group Key: account_move_line.account_id
Batches: 1 Memory Usage: 553kB
Worker 0: Batches: 1 Memory Usage: 553kB
Worker 1: Batches: 1 Memory Usage: 553kB
-> Parallel Index Scan using account_move_line__company_id_index on account_move_line (cost=0.72..2827275.65 rows=2446945 width=14) (actual time=0.147..3636.824 rows=4681102 loops=3)
Index Cond: (company_id = 1)
Filter: ((date <= '2026-06-10'::date) AND ((parent_state)::text = 'posted'::text) AND ((display_type)::text <> ALL ('{line_section,line_subsection,line_note}'::text[])) AND (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[])))
Rows Removed by Filter: 119633
Planning Time: 0.423 ms
Execution Time: 4980.584 ms
```
## With the new index
```sql
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=1000.74..2857192.00 rows=779 width=44) (actual time=3339.293..7476.511 rows=839 loops=1)
Group Key: account_move_line.account_id
-> Gather Merge (cost=1000.74..2857166.68 rows=1558 width=44) (actual time=3339.266..7475.827 rows=1136 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial GroupAggregate (cost=0.72..2855986.83 rows=779 width=44) (actual time=0.439..4673.216 rows=379 loops=3)
Group Key: account_move_line.account_id
-> Parallel Index Scan using idx_aml_reporting on account_move_line (cost=0.72..2831507.64 rows=2446945 width=14) (actual time=0.055..3919.782 rows=4681102 loops=3)
Index Cond: ((company_id = 1) AND (date <= '2026-06-10'::date) AND ((parent_state)::text = 'posted'::text))
Filter: (journal_id = ANY ('{5,181,2,407,571,570,506,169,271,167,121,6,9,241,655,623,242,63,331,123,3,18,142,4,320,170,218,236,97,143,343,64,521,660,139,344,118,119,274,179,441,619,439,89,522,91,17,8,128,135,19,10,7,180,134,124,52,53,137,98,1,96,57}'::integer[]))
Rows Removed by Filter: 173
Planning Time: 0.485 ms
Execution Time: 7476.576 ms
```This update resolves an issue where the system incorrectly rejected zero measurements received from caliper devices via IoT. The fix allows for valid zero readings, ensuring accurate data capture for quality checks. It also includes a minor typo correction.
Original PR description
Fix a check on the IoT response that incorrectly rejected valid measurements of 0 from caliper devices Also fix a typo opw-6184669
This update fixes a technical detail in the Odoo Enterprise system related to the DiDi Food delivery provider. The provider's technical name has been corrected from 'didifood' to 'didi' to align with recent updates from UrbanPiper. This ensures accurate identification and integration of the DiDi Food delivery service.
Original PR description
In this commit: - Following up on this commit https://github.com/odoo/enterprise/commit/6d44f403e3fc8f47e899b9ac6fc63b8a3f66861d, UrbanPiper updated `DiDi Food` technical name to `didi`. So, we are updating the `DiDi Food` provider technical name from `didifood` to `didi`. Task-6310690
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 user experience.
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
This update resolves an issue where users couldn't select child contacts when creating bank statement lines. The change aligns the system's contact selection process, allowing users to correctly associate bank statements with child contacts within the bank reconciliation workflow. This improves the usability and accuracy of financial reporting.
Original PR description
When creating a bank statement line, we can not set an individual contact that is a children of a company contact. However, when clicking on the 'Set Partner' button, all contacts are shown in the modal list view. This commit aligns the domain coming from the 'Set Partner' button with the domain from the 'partner_id' field of the auto reconcile wizard Steps: - Have a contact X, with a child contact Y - Create and confirm an invoice for contact Y, amount 1000 - Create a bank statement line for 1000 -> You can not select Y, only X - Click 'Add & Close' - Click on 'Set Partner' button -> Y is displayed opw-6205154
This update eliminates a frequent warning message that appeared when automatic OCR was disabled for invoices. This message was not helpful to users and has now been removed, streamlining the invoice processing experience. It improves the user experience by reducing unnecessary notifications.
Original PR description
The warning "Automatic OCR does not apply to this document" was logged for every upload when automatic OCR isn't enabled, it isn't very useful. opw-[6232122](https://www.odoo.com/odoo/unassigned-tasks/6232122)
This update fixes a display issue where rental prices weren't correctly formatted on the website. The fix adds a necessary separator (/) to ensure rental prices and durations are shown clearly, improving the user experience for rental product configuration and ordering. This ensures accurate pricing information is presented to customers.
Original PR description
Steps to produce: --- - Install the `Rental and eCommerce `modules. - Create a rental product and configure a rental price for it. - Add an optional product from the Sales tab. - Publish the product on the website. - Open the product page on the website and click` Add to Cart`. Issue: --- - In the product configurator, the rental price is displayed without the `/` separator between the price and the rental duration period. Cause: --- - The string used to generate the rental duration label does not include the `/` separator. Fix: --- - Add the missing `/` separator to the rental duration label so that rental prices are displayed correctly. Before: --- <img width="974" height="185" alt="image" src="https://github.com/user-attachments/assets/64a88a60-bcc0-4657-97fd-584da57d0aff" /> After: --- <img width="967" height="188" alt="image" src="https://github.com/user-attachments/assets/b4d50019-1db4-4817-a8ce-446cc3c55df4" /> opw-6293015