Friday, April 24, 2026
14 changes · 19.0
New functionality added to Odoo
This update adds functionality to generate reports required by Spanish tax authorities (AEAT) for the Modelo 180 tax return. It includes wizards for data export and submission, streamlining the process for businesses operating in Spain to comply with tax regulations. This improves reporting accuracy and simplifies the filing process.
Original PR description
**task**-5902868
Resolved issues and error corrections
This update fixes an issue where DIAN invoice states were incorrectly set to 'failed' due to expected status update failures. The fix ensures invoices successfully transmitted to DIAN receive the correct 'invoice_accepted' state, preventing problems like QR code generation errors. This improves the accuracy of DIAN invoice processing.
Original PR description
Before this commit, the logic that computes the invoice's DIAN state would rely solely on the state of the most recent document. The problem with that approach is that we will regularly send status…
Before this commit, the logic that computes the invoice's DIAN state would rely solely on the state of the most recent document. The problem with that approach is that we will regularly send status updates to DIAN after the invoice was successfully submitted via the status cron here: https://github.com/odoo/enterprise/blob/19.0/l10n_co_dian/models/account_move.py#L484 Those status updates are expected to fail a certain number of times (typically when no commercial status information is available, or in other terms when `l10n_co_dian_commercial_state` is still in `'pending'`). As a result, an invoice successfully transmitted to DIAN could still end up with a `l10n_co_dian_state` not set to `'invoice_accepted'`. This leads, among other things to a QR code that cannot be reprinted as the logic here: https://github.com/odoo/enterprise/blob/19.0/l10n_co_dian/models/account_move.py#L225 determines which template to render based on that state. The issue cannot be easily reproduced or tested as `l10n_co_dian.document` are generated almost only in prod scenarios. The most common setup where the error happens is for invoices that have 2 DIAN documents: 1. the first (oldest, bottom-most) one reflects acceptance of the invoice sending by DIAN (`state = 'invoice_accepted'`) 2. the second one (most recent, top-most), which reflects the latest query for commercial status update by the cron and which failed (`state = 'invoice_rejected'` and the error message reflects the lack of commercial events) opw-6108318 opw-5931442 opw-6034035
This update ensures that Brazilian tax documents generated for EDI comply with local law. It incorporates approximate tax values provided by Avalara, which are now included in the EDI payload regardless of whether specific tax information is available. This ensures accurate and compliant tax reporting for Brazil.
Original PR description
All fiscal documents are required by Brazil law to include the approximate value of fed, state, and city taxes that affect it. Avalara already provides back these values in their tax calculation response, we just missed sending it to the EDI. This commit takes the information from that response and adds it to the EDI payload to make sure that it is generated properly into the generated documents. We are required to always show this even if there are no informative taxes as such we combine it with the T&C sent already. task-5478059
This update corrects a bug where overtime work entries were incorrectly generated on previous days due to an issue with how work entries were being regenerated. The fix ensures accurate overtime calculations by considering employee timezones when searching for work entries. This prevents incorrect overtime payments.
Original PR description
How to reproduce: - Select an employee with an overtime ruleset and work entries based on attendances - Create attendance with an approved overtime - Go to "Work Entries" in Payroll, and regenerate the work entries for the following day of the attendance - A new overtime work entry is generated on the first day. Reason: Because of how regenerating work entries is done, the computed date for searching overtime lines took into account the previous day (i.e. regenerating a work entry for a tuesday in an UTC+1 timezone made it so the starting date was on monday at 23:00:00), and since the _read_group only looked at the date part of the time start without taking into account the hour, it included the overtime of the previous day. How it was fixed: The domain now takes into account the timezone of the employee to generate the domain for the _read_group to ensure the correct day is selected Task ID: 5899657
This update resolves a problem where CFDI (Mexican tax) generation for payslips would fail due to rounding discrepancies. The fix ensures payroll calculations are rounded to the required 2 decimal places before generating the CFDI XML, aligning with Mexican tax regulations. This prevents errors and ensures accurate tax reporting.
Original PR description
Currently, if the company is configured with a 4-decimal currency, the CFDI generation for payslips might fail with NOM111 and CFDI40119 errors. This occurs because the calculation of totals and subtotals uses the raw unrounded floats, which can cause penny differences when the XML template formats the individual lines to 2 decimal places. This commit forces all payroll concept amounts to be explicitly rounded to 2 decimal places before accumulating the totals. This ensures that the sum of the formatted XML nodes precisely matches the total and subtotal values reported in the CFDI. Accounting might require a higher decimal precision for the company's currency (e.g., 4 decimals for inventory). However, payroll CFDI stamping strictly requires 2 decimal precision. This fix isolates the payroll CFDI calculations from the company's currency settings.
This update fixes an issue where tax reports for international customers incorrectly used the company's VAT number instead of the fiscal position's foreign VAT ID. The change ensures that VAT reports accurately reflect the customer's tax ID, improving compliance and reporting accuracy for businesses operating across borders. This was triggered by a specific test case involving a Belgian fiscal position.
Original PR description
### Issue: When a fiscal position defines a `foreign_vat`, tax reports generated for that country still use the company's VAT number instead For example, with a Belgian fiscal position using…
### Issue: When a fiscal position defines a `foreign_vat`, tax reports generated for that country still use the company's VAT number instead For example, with a Belgian fiscal position using `BE010203040`, the generated BE VAT report uses the company VAT instead of `010203040` ### Cause: The report generation did not check whether there is a fiscal position with a `foreign_vat` matching the country of the report ### Note: The example above uses a Belgian fiscal position to reproduce the issue Starting from 19.0, this specific flow is blocked because Intervat is enabled in production mode by default A related fix makes the Intervat settings available in that case Until then, the issue can be reproduced by temporarily commenting out: https://github.com/odoo/enterprise/blob/5fb58a9b7b3ae89f87e84d4ba1fa3a16237d80ac/l10n_be_intervat/models/account_return.py#L15 ### Steps to reproduce: - Disable demo data and install `accountant` - Create a Fiscal Position "Belgium" (Country: Belgium, Foreign Tax ID: BE010203040) - Click the alert to install the Belgian taxes - Create and confirm an invoice for a Belgian customer: - Fiscal Position: Belgium - Any product with a Belgian tax - Invoice Date: 01/01/2026 - Open the Tax Report and select `VAT Return (BE)` for January - Click `Returns` and select the full year - Mark the December return as Completed from the three-dot menu - Review January and fill the missing company data (TIN: 1111111, phone and email) - Click `Validate -> Lock -> Submit` ### Before the fix: The generated XML uses the company VAT number (`1111111`) instead of the fiscal position foreign VAT (`010203040`). opw-6076540 Forward-Port-Of: odoo/enterprise#112610
This update addresses a critical issue where Odoo would crash when attempting to download a URL document alongside a spreadsheet. The fix ensures stable downloads of combined documents, improving the user experience and preventing data loss. This resolves a reported bug impacting document access.
Original PR description
Try to download a url document along with a spreadsheet. `onDownload` crash when trying to download a url document. Task: 5485662 Forward-Port-Of: odoo/enterprise#113645 Forward-Port-Of: odoo/enterprise#112513
This update fixes a reporting issue related to Hong Kong's IRD (IRD56B/F/G) salary reports. It adds adjustments for global reimbursements and deductions, ensuring that taxable income totals are accurately calculated. This improves the reliability of financial reporting for Hong Kong businesses using Odoo.
Original PR description
Added GLOBAL_REIMBURSEMENT and GLOBAL_DEDUCTION to the AmtOfSalary calculation for IR56B/F/G reports. This ensures adjustments are properly reflected in taxable income totals. task-6126661
This update resolves a crash that occurred when opening salary adjustments on mobile devices. The fix addresses a missing delete action in the mobile view and added a basic kanban view to ensure proper functionality. This improves the user experience for mobile users managing employee salaries.
Original PR description
Steps to reproduce ================== - Install hr_payroll - Use a mobile viewport - Go to Employees - Open a record - Switch to the Salary Adjustments notebook tab => TypeError: undefined is not an…
Steps to reproduce ================== - Install hr_payroll - Use a mobile viewport - Go to Employees - Open a record - Switch to the Salary Adjustments notebook tab => TypeError: undefined is not an object (evaluating 'props.activeActions.onDelete=this.onDelete.bind(this)' Cause of the issue ================== The SalaryAttachment2ManyField widget overrides the rendererProps to handle the delete action, but this isn't defined on mobile (because a kanban view is used) See https://github.com/odoo/odoo/blob/9f93f22ed5f6d5dbbafeb0a8c6fababdc2a65d45/addons/web/static/src/views/fields/x2many/x2many_field.js#L196-L212 Solution ======== Since there is no delete action on the kanban view, there is no need for an override. While we are at it, there was no kanban view defined. Thus a default view was used https://github.com/odoo/odoo/blob/138fad6d54a0b59885b1e5c712beb8f581c9555c/odoo/addons/base/models/ir_ui_view.py#L2835-L2846 It only contained the field description. Since that one is optional, records without a description were almost invisible.. Thus we also add a basic kanban view opw-6047295
This update resolves a technical issue that was limiting the efficiency of WhatsApp marketing automation. Previously, the system processed only a portion of related messages, now all relevant messages are processed, improving the speed and scalability of the automation workflow. This ensures a smoother and more reliable experience for users.
This update resolves an issue where attachments couldn't be downloaded from the Odoo mobile app (versions 18.4+). The fix ensures that absolute URLs are handled correctly, preventing errors and restoring the ability to download files from chatter. This improvement impacts mobile users' ability to access and share attachments.
Original PR description
In Odoo 18.4+, downloading attachments from the chatter is broken.
See: https://github.com/odoo/odoo/pull/200099
The `onClickDownload` function now passes an absolute URL to `downloadFile`.
The download function is implemented natively in the mobile apps.
The Android implementation always prefixes the provided URL with the
database origin (i.e.: `https://example.odoo.com`).
`download({url: "https://example.odoo.com/web/content"})` will try to
download `https://example.odoo.comhttps://example.odoo.com/web/content`.
This results in an UnknownHostException.
We can remove the origin from the url before calling the native method.
By doing this on the JS side, there is no need to update the Android app.
opw-6033150
Forward-Port-Of: odoo/enterprise#114832This update corrects a bug where the DIAN web service was incorrectly overwriting customer contact information (names and emails) with fiscal data, leading to data loss and incorrect invoice delivery. The fix now preserves commercial contact details, creating a separate invoicing contact only when the DIAN email address differs, giving users control over their data and preventing future issues.
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 Forward-Port-Of: odoo/enterprise#114017
This update corrects a flaw in how overtime entries are identified, which previously led to duplicate entries being created. The change ensures overtimes are accurately calculated based on the correct time range, regardless of timezone settings. This improves the reliability of overtime tracking and reporting.
Original PR description
This function searches for overtimes by the date of hr.attendance.overtime.line. This is simply a date and can lead to problems if not using the UTC timezone. Steps to reproduce: 1. Set timezone to…
This function searches for overtimes by the date of hr.attendance.overtime.line. This is simply a date and can lead to problems if not using the UTC timezone.
Steps to reproduce:
1. Set timezone to anything ahead of UTC (Europe/Chisinau) (Not required, but helps)
2. Create a new employee
3. Set the Contract
4. Set Work Entry Source to Attendances
5. Set the timezone of Working Hours to anything ahead of UTC (Europe/Chisinau)
6. Create an attendance for the employee that causes overtime (default anything over 8 hours)
7. Two work entries will be created. One for the default time and one for the overtime.
8. Create an attendance for the next day (no need for overtime)
9. Alter the Check Out time
10. Duplicate overtime work entry created for the day prior
Example:
Searching for Overtimes on April 17th.
date_start = datetime(2026, 4, 17, 0, 0)
date_stop = datetime(2026, 4, 17, 23, 59, 59, 999999)
Translated from Europe/Chisinau time (3 hours ahead):
date_start = datetime(2026, 4, 16, 21, 0)
date_stop = datetime(2026, 4, 17, 20, 59, 59, 999999)
start/stop times are cut to dates as overtime.line.date is just a Date:
start_naive.date() = date(2026, 4, 16)
end_naive.date() = date(2026, 4, 17).
Then, given the domain:
('date', '<=', end_naive.date()),
('date', '>=', start_naive.date()),
Result: all overtimes on April 16th and 17th returned.
This commit aims to change the function to truly return overtimes between the given datetimes (start_dt, end_dt)
opw-5952995This update resolves an issue where the minimum IS (Insurance Savings) amount was incorrectly calculated in the Swiss payroll module. The fix ensures accurate IS calculations for employees, aligning with Swiss tax regulations. This improves payroll accuracy and compliance for businesses using the Odoo Enterprise solution.
Original PR description
Forward-Port-Of: odoo/enterprise#114463