Thursday, April 23, 2026
15 changes · 18.0
Resolved issues and error corrections
The Czech VIES Summary Report XML export now removes the country prefix from VAT numbers before generating the file. This brings the export in line with the official format and helps avoid validation issues when submitting the report.
Original PR description
**Steps to reproduce:** - Install the `l10n_cz_reports` module and switch to a `CZ Company` - Create an invoice for a customer with a VAT number, add a product, and set the Transaction Code (enable…
**Steps to reproduce:** - Install the `l10n_cz_reports` module and switch to a `CZ Company` - Create an invoice for a customer with a VAT number, add a product, and set the Transaction Code (enable it from the optional columns if needed). - Navigate to Reporting > VIES Summary Report. - Observe the value in the `VAT Number` column (includes country code). - From the dropdown, export the report as XML. **Observation:** In the generated XML file, the `c_vat` field contains the VAT number including the country code (e.g., `CZ12345679`) instead of only the numeric part (`12345679`). **Root cause:** At [1], the VAT number is directly taken from the report lines without removing the country code. **Fix:** This commit ensures that the `c_vat` field contains only the VAT number without the country code, complying with the official VIES XML format requirements. Ref: https://adisspr.mfcr.cz/dpr/adis/idpr_pub/epo2_info/popis_struktury_detail.faces?zkratka=DPHSHV#:~:text=Tax%20identification%20number%20of%20the%20purchaser%20(only%20the%20numeric%20part) [1]: https://github.com/odoo/enterprise/blob/c4f2c3442f30f5ac972dd136a3642acc5bcc6da2/l10n_cz_reports_2025/models/l10n_cz_vies_summary_handler.py#L29-L62 opw-6093259 Forward-Port-Of: odoo/enterprise#113083
This change fixes a problem where sales orders with discounts and foreign currencies could produce an unbalanced customer invoice and block billing. It improves invoice creation reliability for businesses using multiple currencies and discount accounts.
Original PR description
**STEP TO REPRODUCE** 1. Install the sale and accounting module. 2. Create 2 products, and setup each one with a different income account. 3. From the accounting settings, setup an account for Invoice Line discount -> Customer Invoice account. 4. Enable a currency, and create a pricelist for this currency. 5. Create the following SO: pricelist -> the pricelist you created previously. currency rate : 0.000717398539 line a: product_a, price 10, discount 57.85% line b: product_b, price 70, discount 57.85% From this SO, try to create an invoice. It will fail, saying the invoice it tried to create is unbalanced. opw-5974048
This change fixes a problem where some bank reconciliations could fail to validate after the foreign currency line was reassigned to a different account. The exchange difference is now kept correctly on the counterpart line, so the entry stays balanced and can be posted successfully.
Original PR description
Current behavior: When reconciling a bank statement line whose journal currency differs from the company currency and whose foreign_currency_id equals the company currency, mounting a vendor bill…
Current behavior: When reconciling a bank statement line whose journal currency differs from the company currency and whose foreign_currency_id equals the company currency, mounting a vendor bill with force-full-match and re-tagging the auto_balance line to a company-currency account raises UserError: "The entry is not balanced." on Validate. Expected behavior: The reconciliation should validate successfully with the exchange difference carried on the counterpart line. Required Modules - account_accountant - l10n_au (any company-currency localization reproduces, AU used here) Steps to reproduce: 1. Install account_accountant and l10n_au. Company currency = AUD. 2. Activate JPY and add a rate for 2025-10-01: 109.6788 JPY/AUD. 3. Create a bank journal: Name: ANZ EUR Account (JPY) Type: Bank Currency: JPY 4. Create an account: Code: 1082 Name: Deposits - Paid to Mazak Type: Current Assets Currency: (leave empty — accepts any) 5. Create vendor partner "Yamazaki". 6. Add a second JPY rate for 2025-11-01: 105.069 JPY/AUD (creates the FX gap between bill date and statement date). 7. Create and post a vendor bill: Vendor: Yamazaki Currency: JPY Bill date: 2025-10-01 One line: JPY 37,000,000, no tax 8. Create a bank statement line on the JPY journal: Date: 2025-11-01 Amount: -33,331,248 (JPY) Foreign currency: AUD, amount: -303,898.18 9. Open reconciliation on the statement line. 10. Manual Operations → mount the vendor bill with "Allow partial" OFF so the bill is force-matched fully. The widget now displays four lines: liquidity, new_aml (Trade Creditors), exchange_diff (Exchange Rate Loss), and auto_balance (AUD). 11. Click the auto_balance line and change its account to 1082 Deposits - Paid to Mazak. 12. Click Validate. Expected result The move posts: ANZ -303,898.18 / Trade Creditors +352,149.54 / Deposits -48,251.36, Σ = 0. Actual result UserError: The entry is not balanced. Cause of the issue: In _lines_prepare_auto_balance_line (bank_rec_widget.py:424-475), the auto_balance line's amount_currency is computed from the JPY gap converted at the transaction rate while its balance is computed independently as the company-currency plug. When the statement line's foreign_currency_id equals the company currency, currency_id on the auto_balance line resolves to the company currency, yet the two numbers disagree. account.move.line._inverse_amount_currency (account_move_line.py:1250-1260) enforces balance = amount_currency whenever currency_id == company_currency_id, so at move.write the balance is silently clamped to amount_currency, dropping the FX component. _validation_lines_vals (bank_rec_widget.py:1382-1406) has already squashed the exchange_diff into the new_aml, so the FX stays on the JPY side while its AUD-side offset is clamped away. _check_balanced (account_move.py:2462-2474) then raises. Fix: In _lines_prepare_auto_balance_line, when transaction_currency_id == company_currency_id, force amount_currency = open_balance before returning the vals. The line then reaches account.move.write already satisfying the _inverse_amount_currency invariant, the clamp is a no-op, and the FX squashed into new_aml is preserved. opw-6122823
This update resolves a reporting discrepancy where planned hours were incorrectly shown for public holidays. The fix accurately accounts for public holidays applied across multiple schedules and handles timezone differences, ensuring accurate timesheet forecasts.
Original PR description
### Steps to reproduce: - Install project_timesheet_forecast module - Configure your database to have a far timezone (Montevideo/Uruguay in my case) - Create a public holiday starts from 12AM to…
### Steps to reproduce: - Install project_timesheet_forecast module - Configure your database to have a far timezone (Montevideo/Uruguay in my case) - Create a public holiday starts from 12AM to 11:59PM with a calendar - Create a planning slot for a resource that overlap with the public holiday - Check the Timesheets / Planning analysis report - Group by employees > day **- Check the date of the public holiday and notice there are still planned hours shown** - Remove the calendar from the public holidays that we created previously - Check the report once again **- Notice the day of the public holiday and the day after has no planned hours** ### Cause: In the query we are using to exclude the leave days from the report we only exclude the ones that has calendar_id assigned, not taking into consideration that some of the public holiday are general and is not applied to just one working schedule. Also if we have a leave starting midnight to 11:59PM since we store dates in database as UTC for timezone like Uruguay's one it will shift the end with one day which will introduce inconsistencies ### Fix: We check if the calendar_id is null on the resource_calendar_leaves and make sure we take timezone of the resource into account when checking the dates of the leaves. opw-5027070 Forward-Port-Of: odoo/enterprise#111846
This update resolves an issue where sending final invoices to ZATCA would fail when down-payments had been reversed. The fix ensures that reversed down-payment invoices are correctly handled, preventing a 'singleton' error and allowing the final invoice to be successfully generated. This improves the reliability of ZATCA invoice processing.
Original PR description
Sending the final invoice of a sale order to ZATCA crashed with `ValueError: Expected singleton: account.move(a, b)` when the sale order had multiple down-payment references. Steps to reproduce: 1.…
Sending the final invoice of a sale order to ZATCA crashed with `ValueError: Expected singleton: account.move(a, b)` when the sale order had multiple down-payment references. Steps to reproduce: 1. Configure a SA company and setup ZATCA 2. Create a sale order and confirm it 3. Deliver the product line. 3. From the sale order, create a down-payment invoice (fixed amount, e.g. 115) and post it (DP1). 4. On DP1, click "Credit Note" and choose "Full refund and new draft invoice"; validate. DP1 becomes `reversed` and a new draft down-payment DP2 is created. Post DP2. 5. From the sale order, create the final regular invoice and post it. 6. Send the final invoice to ZATCA (or generate its XML) -> `ValueError: Expected singleton: account.move(a, b)`. Root cause: _l10n_sa_get_line_prepayment_vals looks up the related down-payment move through the down-payment sale order line shared with the product line. The filter matched any out_invoice with _is_downpayment() == True, so the reversed DP1 and the active DP2 both ended up in the recordset, and reading .name raised the singleton error. Prefer non-reversed down-payment moves when available, but fall back to reversed ones if no alternative exists (e.g. when generating a credit note of the final invoice after the original down-payment was itself reversed). opw-6116265 Forward-Port-Of: odoo/odoo#259384
This update fixes an issue where the DSO (Days Sales Outstanding) data displayed on the invoice dashboard was inaccurate. The problem stemmed from a discrepancy in how fiscal years were handled, leading to misaligned reporting. This change ensures that DSO figures are now correctly calculated and displayed, providing more reliable financial insights.
Original PR description
Invoice dashboard data, specifically DSO, was incorrectly aligned due to a mismatch in the fiscal year structure. Task-6049887
This update fixes an issue where Odoo incorrectly applied EU VAT rules for B2B transactions. Now, the system accurately determines VAT based on where the goods are actually delivered, ensuring compliance with EU regulations and preventing incorrect VAT exemptions for domestic sales.
Original PR description
**Description of the issue/feature this PR addresses:** Odoo currently determines the tax treatment of EU B2B transactions primarily based on the customer's VAT country. This leads to incorrect…
**Description of the issue/feature this PR addresses:** Odoo currently determines the tax treatment of EU B2B transactions primarily based on the customer's VAT country. This leads to incorrect classification of some transactions as intra-Community supplies when the customer provides a valid foreign EU VAT number but the goods are delivered within the seller's country. Under EU VAT rules (Directive 2006/112/EC, Articles 32 and 138), an intra-Community supply only exists if the goods are physically dispatched or transported from one Member State to another. If the goods remain in the seller's country, the transaction must be treated as a domestic supply subject to local VAT, regardless of the customer's foreign VAT identification. **Current behavior before PR:** When a customer has a valid EU VAT number from another Member State, Odoo may apply intra-Community tax treatment (0% VAT) even if the delivery address is located in the seller's country and no cross-border movement of goods occurs. This results in: - Incorrect VAT exemption being applied. - Transactions being treated as intra-Community supplies when they are legally domestic supplies. - Potential inconsistencies with EU VAT compliance and reporting. **Desired behavior after PR is merged:** Tax determination takes into account the actual place of delivery of the goods. If the goods are delivered within the seller's country and no intra-Community transport occurs, the transaction is treated as a domestic supply and local VAT is applied, even when the customer provides a valid foreign EU VAT number. This ensures that intra-Community tax treatment is only applied when there is an actual cross-border movement of goods, aligning Odoo's behavior with EU VAT Directive requirements. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr cc @Tecnativa ping @carlosdauden @juancarlosonate-tecnativa
This update fixes an issue where flexible work schedules were incorrectly calculating overtime. The change adjusts how the system determines the date range for flexible hours, ensuring accurate overtime indications are displayed for employees with varying work arrangements. This improves the reliability of timesheet data.
Original PR description
**problem:** On timesheets, the overtime indication next to an employee's name is incorrect when using flexible work schedules. for example: a "Flexible 20h" schedule (4h a day) shows 1h of negative…
**problem:** On timesheets, the overtime indication next to an employee's name is incorrect when using flexible work schedules. for example: a "Flexible 20h" schedule (4h a day) shows 1h of negative overtime even when the employee has logged exactly 20h for the week. **steps to reproduce:** 1. Create a new working schedule with flexible hours enabled for example (20h/week, 4h/day average) 2. Assign this schedule to an employee 3. Go to Timesheets, search for the employee 4. Navigate to a past week 5. Enter 4h on each working day 6. Observe the overtime indication shows incorrect value (-01:00) **cause:** In `resource/models/resource_calendar.py`, the flexible hours algorithm that determines the date range by converts UTC boundaries to the employee's timezone. When the employee's timezone has a positive UTC offset (UTC+1, like in brussels time zone), `Sun 23:59:59 UTC` becomes `Mon 00:59:59 CET`, pushing `end_date` to the next Monday. This creates an 8 day range instead of 7. The algorithm then starts a new weekly budget for the spillover day and allocates 1 extra hour, making `allocated_hours` 20.9999998 instead of 20. **fix:** - Use the UTC date before conversion to the employee's timezone when determining the flexible date range. **note:** Updating the test (test_no_carried_over_leaves_for_flexible_resource) in hr_holidays/tests/test_expiring_leaves.py expected duration logic, is to match the corrected inclusive day range and prevent asserting the previous spillover behavior. link to the enterprise PR: odoo/enterprise#112879 opw-5970511 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an issue where the overtime indication on timesheets was incorrect when employees used flexible work schedules. The fix ensures accurate overtime calculations by correctly handling time zone conversions and preventing an extra hour from being added to the weekly budget. This improves the reliability of timesheet reporting.
Original PR description
**problem:** On timesheets, the overtime indication next to an employee's name is incorrect when using flexible work schedules. for example: a "Flexible 20h" schedule (4h a day) shows 1h of negative…
**problem:** On timesheets, the overtime indication next to an employee's name is incorrect when using flexible work schedules. for example: a "Flexible 20h" schedule (4h a day) shows 1h of negative overtime even when the employee has logged exactly 20h for the week. **steps to reproduce:** 1. Create a new working schedule with flexible hours enabled for example (20h/week, 4h/day average) 2. Assign this schedule to an employee 3. Go to Timesheets, search for the employee 4. Navigate to a past week 5. Enter 4h on each working day 6. Observe the overtime indication shows incorrect value (-01:00) **cause:** In `resource/models/resource_calendar.py`, the flexible hours algorithm that determines the date range by converts UTC boundaries to the employee's timezone. When the employee's timezone has a positive UTC offset (UTC+1, like in brussels time zone), `Sun 23:59:59 UTC` becomes `Mon 00:59:59 CET`, pushing `end_date` to the next Monday. This creates an 8 day range instead of 7. The algorithm then starts a new weekly budget for the spillover day and allocates 1 extra hour, making `allocated_hours` 20.9999998 instead of 20. **fix:** - Use the UTC date before conversion to the employee's timezone when determining the flexible date range. - prefer `self` when it is the flexible calendar being queried, so hr_contract's `_get_calendar_at()` override cannot substitute the contract's calendar parameters (full_time_required_hours, hours_per_day) for the flexible ones. **note** Updating the test (`test_no_carried_over_leaves_for_flexible_resource`) in `hr_holidays/tests/test_expiring_leaves.py` expected duration logic, is to match the corrected inclusive day range and prevent asserting the previous spillover behavior. link to the enterprise PR: https://github.com/odoo/enterprise/pull/112879 link to the community PR: https://github.com/odoo/odoo/pull/257269 opw-5970511 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an issue where Datev reports incorrectly calculated tax amounts when vendor bills used taxes with multiple repartition lines. The fix ensures that tax amounts are accurately added, leading to correct tax reporting for Datev customers. This resolves a discrepancy in the Datev CSV export.
Original PR description
With l10n_de_reports: - Configure a foreign currency with an exchange rate. - Configure a tax with multiple repartition lines. - Create a vendor bill in this foreign currency with this tax. - In the general ledger export the datev csv. In the datev csv the rate is wrong. In the method _l10n_de_datev_get_csv, we build a tax_amount dict. However when one tax has multiple lines, the amount is replaced and not added. opw-6010097
This update ensures that invoices only include timesheets that have been fully validated within Odoo. Previously, the system incorrectly included non-validated timesheets in invoices, leading to inaccurate billing. This fix corrects a bug related to invoicing policies and ensures data integrity.
Original PR description
**Steps to reproduce** - Settings: Timesheets > Invoicing policy = only validated TS. - Have a service product with an invoicing policy based on timesheets. - Create a sales order using this product. - From the SO, click on the "Recorded" smart button and create 2 timesheets. Validate only one of them. - Invoice the SO, using a timesheets period that includes both TS. - Notice that the quantity of the invoice line includes the non-validated timesheet. **Cause** The domain excluding non-validated timesheets provided by `_timesheet_compute_delivered_quantity_domain` is not considered since c3b6053b09222d4bd2237e7de589a63fbef118f1 **Change** Since the purpose of the previous fix was to exclude timesheets linked to an invoice with a date before the "Invoicing Switch Threshold", this can be achieved by tweaking the `timesheet_domain` slightly, similar to the `_timesheet_domain_get_invoiced_lines` domain. opw-6116670
This update fixes a bug that occurred when restaurant orders with active Fiskaly transactions were opened on multiple devices. Without proper data persistence, the system would attempt to create duplicate transactions, leading to errors with the Fiskaly API. This ensures smoother order processing and prevents disruptions for restaurant operations.
Original PR description
In a restaurant POS, when an order with an active Fiskaly transaction is opened on a second device, `transactionState` and `tx_revision` were not available (uiState is not persisted to the server), causing the new device to attempt creating a duplicate transaction with a stale revision, which resulted in a Fiskaly API error. opw-6147654
This update prevents portal users from seeing the 'View Timesheets' button on invoices when they don't have the necessary permissions. Previously, the system incorrectly displayed the button based on the presence of timesheets linked to the order, even if the user lacked access. This change ensures users only see timesheets they are authorized to view, improving data security and user experience.
Original PR description
sale: add sale order specific hook to extend page values ------ Allows adding custom data (e.g., timesheets) without overriding generic _get_page_view_values sale_timesheet: hide 'View Timesheets'…
sale: add sale order specific hook to extend page values
------
Allows adding custom data (e.g., timesheets) without overriding generic _get_page_view_values
sale_timesheet: hide 'View Timesheets' button for users without access
-------
Steps to Reproduce:
-----------------
- Create a product with the invoice policy set to Based on Timesheets
- Enable Project and Tasks on the order.
- Create and confirm a sale order using a portal user.
- Log timesheets on the related task.
- Create an invoice from the sale order.
- Log in as the portal user and open the invoice.
- Click the 'View Timesheets' button.
Issue:
-------------
The 'View Timesheets' button is shown to the portal user even though they don’t have access to view timesheets.
Root Cause:
------------
The timesheets are linked to the sale order, so the button appears based on the timesheet_count, but the portal user does not actually have permission to access those timesheets.
Fix:
-----------
We replaced the timesheet_count check with a check that verifies whether the user actually has access to any of the related timesheets.
task-4745519
Forward-Port-Of: odoo/odoo#209552This update resolves an issue where taxes were incorrectly removed from POS order lines during backend editing (returns/exchanges). The fix ensures that tax information is properly saved when a POS order is modified, preventing data loss and maintaining accurate tax calculations. This improves the reliability of the Point of Sale system.
Original PR description
The `tax_ids` field on `pos.order.line` is defined with `readonly=True`. When editing a POS order from the backend (e.g. during a return/exchange flow), the `_onchange_product_id` method correctly…
The `tax_ids` field on `pos.order.line` is defined with `readonly=True`. When editing a POS order from the backend (e.g. during a return/exchange flow), the `_onchange_product_id` method correctly sets `tax_ids` from the product, and the computed `tax_ids_after_fiscal_position` displays the mapped taxes in the UI. However, because `tax_ids` is readonly, the web client does not include it in the save payload. As a result, the taxes are silently dropped on save and `tax_ids_after_fiscal_position` recomputes to empty. Steps to reproduce: 1. Create and pay a POS order with a product that has taxes 2. Go to the backend (Point of Sale > Orders) and open that order 3. Initiate a return for the order 4. In the return order, add a new product (exchange scenario) 5. Observe that taxes are correctly shown on the new line 6. Click Save 7. The taxes disappear from the order line The fix adds `force_save="1"` to the `tax_ids` field in both the list and form views of `pos.order.line`, consistent with how `price_subtotal` and `price_subtotal_incl` are already handled in the same views. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update corrects a critical issue where the DIAN web service was incorrectly overwriting CRM contact information, leading to data loss and incorrect invoice delivery. The fix now intelligently handles email differences, preserving commercial contact details and giving users control over their data, preventing manual corrections.
Original PR description
The DIAN web service was overwriting partner names and emails with fiscal data, causing data loss for CRM contacts. The fiscal email often differs from the commercial one, and the overwrite broke the sales flow by sending invoices to the wrong address. Users had no standard workaround short of manually re-entering emails after every invoice generation. Instead of blindly overwriting, only update empty fields and create a child invoicing contact when the DIAN email differs from the existing one. Also remove the automatic onchange and periodic re-fetch triggers to leave existing data under user control. task-5912005