Daily updates from Odoo
Monday, June 29, 2026
13 changes · 18.0
Enhancements to existing features
This change lets operators adjust how long unused database connections stay open. It helps reduce memory usage in environments with limited resources by closing idle connections more aggressively when needed.
Original PR description
In memory-scarce environments, the default 10-minute idle timeout may keep too many backend connections alive. Allow operators to override it via the ODOO_DB_MAX_IDLE_TIMEOUT environment variable to evict idle connections more aggressively. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#271785
Polish invoices sent outside KSeF now include a QR code and the KSeF reference number on the PDF. This helps recipients quickly verify and access the invoice information in line with local invoicing requirements.
Original PR description
While communicating outside KSeF, invoices must have a QR Code and their KSeF number displayed. QR Code content spec is available here: https://github.com/CIRFMF/ksef-api/blob/main/kody-qr.md task-6334901 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Resolved issues and error corrections
This fix ensures that once an appointment is canceled, it no longer prevents the available capacity of a resource from being reduced. As a result, users can update resource availability without running into an error caused by bookings that are no longer active.
Original PR description
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type using resources 3. Create a resource with capacity `4` 4. Book an appointment on that resource with reserved capacity `4` 5.…
Steps to reproduce: 1. Install `appointment` 2. Create an appointment type using resources 3. Create a resource with capacity `4` 4. Book an appointment on that resource with reserved capacity `4` 5. Try to reduce the resource capacity 6. Cancel the appointment 7. Try again to reduce the resource capacity Current behavior: - Reducing the resource capacity raises: `The capacity used can not be lesser than the capacity reserved` Issue: - Canceled appointments still prevent lowering the resource capacity, even though the booking is no longer active. Cause: - Canceling an appointment only archives the related `calendar.event`, it does not remove the corresponding `appointment.booking.line`. That booking line keeps its previous `capacity_reserved` value, while `capacity_used` is a stored computed field depending on the resource capacity. When the resource capacity is reduced after cancellation, the archived booking line is recomputed with the new lower resource capacity and this violates the SQL constraint Solution: - For inactive booking lines: Set `capacity_used = capacity_reserved` opw-6036053
Spreadsheets now block inserting pivots that rely on unsupported relationship fields, avoiding errors caused by non-numeric record IDs. Users will see the insert option disabled with a helpful message, so they know why the pivot cannot be added.
Original PR description
Prevent inserting a pivot into a spreadsheet when it contains groupbys on relations whose IDs are not numeric (e.g. account.root uses string IDs), as the pivot table engine does not support them. The insert button is now disabled with an appropriate tooltip. Task: 6023622
This update prevents the Balance Sheet report from crashing when an analytic account is selected. It removes an unnecessary check that was causing the system to loop until it hit a recursion error, improving report reliability for users working with analytic accounting.
Original PR description
Steps to reproduce ================== - Activate Analytic Accounting. - Go to Accounting > Accounting > Reconcile. - Open Studio. - Add a new many2many field. - Set Journal Item as the related model. - Go to Reporting > Balance Sheet. - Select an analytic account. => RecursionError: maximum recursion depth exceeded Cause of the issue ================== Calling `self.env['account.move.line'].fields_get()` will cause a recursion error. `account.report::_prepare_lines_for_analytic_groupby()` calls `account.move.line::_where_calc()` which in turns calls _prepare_lines_for_analytic_groupby again Solution ======== It turns out we don't actually need to retrieve the groupable attribute, thus bypassing the error. opw-6129149
This fix ensures card payments made in Point of Sale are marked as paid once the session is closed and the payment has been reconciled. It prevents customer payments from remaining stuck in the “In progress” status in Accounting, improving payment tracking and reducing manual follow-up.
Original PR description
**Steps to reproduce:** - Go to the PoS, make a sale, pay by card - Close the session - Go to the accounting module, customer, payment - The payment is stuck in the "In progress" stage **Why the fix:** The bank payments' state were never taken into account and never changed so it stayed in the 'in_process' state. The _compute_state() function in the account module that is supposed to take care of this does nothing for the bank payments from the PoS, because it only checks if the liquidity is equal to zero, which does not happen in this context. It should happen when we manually matching payments and that's how it's used in the Account module, but it's not compatible with the PoS session closure. We now change the payment's state to paid if the payment has been reconciled at the session's closure. opw-5438029
Instagram posts with images will now fail cleanly instead of causing the server request to crash when the network times out. This improves reliability and gives users a clearer failure message, including guidance to use a smaller image when the issue is likely caused by image size or processing time.
Original PR description
Making an Instagram containing an image can crash the server with an unhandled `ReadTimeout` instead of marking the post as failed. ### Cause When creating a media container, Odoo passes a URL pointing to its own server and Instagram fetches the image from it server-side before responding. The timeout therefore covers network latency, Instagram's download speed from the Odoo server, and image processing time, making it prone to being exceeded. When it is, `requests` raises a `ReadTimeout` which is unhandled, leading to a raw RPC error instead of a clean `state='failed'`. ### Fix Catch the network errors and mark the post as failed instead of letting them crash the request. Timeouts get a message suggesting a smaller image, since they are usually caused by Instagram fetching and processing a large image server-side. Any other request error falls back to a generic message. opw-6015997
The Czech VIES report XML now includes the taxpayer city and missing representative name fields when needed, and no longer includes the email address. This helps the report pass official validation and reduces the risk of filing errors with the tax authority.
Original PR description
**PROBLEM** For VIES report, the xml should not contains the email. The city of the tax payer is missing, and while it's not strictly require, it can modify the tax regime of the payer, so we need to include it in the xml. There is missing fields in the case the company is an individual (zast_jmeno, zast_prijmeni). **STEP TO REPRODUCE** 1. Create an invoice to a EU partner, don't forget to set the transaction code on the invoice line (unhide the field). 2. Go to the VIES reports, and generate the xml. 3. Upload it to https://mojedane.gov.cz/pmd/epo to validate and see the errors. documentation: https://mojedane.gov.cz/dpr/adis/idpr_pub/epo2_info/popis_struktury_detail.faces?zkratka=DPHSHV opw-6190983
The website language selector now includes descriptive alternative text for flag images when the flag is the only visual cue. This makes the selector easier to use with screen readers and gives search engines better context.
Original PR description
Steps to reproduce: 1. Enable the language selector in the website header. 2. Enable the "Inline" and "Flag" options. 3. Inspect the flag images rendered in the inline variant. Issue: Flag images in the list items have an empty `alt=""` attribute in "Flag only" mode, where the flag is the sole visual indicator of the language, making the selector inaccessible to screen readers and providing no context for search crawlers. Expected behavior: Inline + Flag should have a descriptive ALT tag since there is no adjacent text or code to identify the language, the flag is not decorative. opw-6246464
This fix makes the revenue shown in Timesheets > Reporting calculate correctly for prepaid service orders billed by days. It prevents revenue figures from jumping to incorrect values when time entries are added, and keeps discounts reflected properly in the report.
Original PR description
Steps to reproduce 1. Create a service product: Invoicing Policy = Prepaid/Fixed Price, Track Service = Timesheets, Unit of Measure = Days 2. Confirm a Sales Order with 2 days of that product at…
Steps to reproduce 1. Create a service product: Invoicing Policy = Prepaid/Fixed Price, Track Service = Timesheets, Unit of Measure = Days 2. Confirm a Sales Order with 2 days of that product at 800/day 3. Register 1 hour on the generated task 4. Open Timesheets > Reporting, add the "Timesheet Revenues" measure Issue `timesheet_revenues` in `timesheets.analysis.report` was computed per analytic line as `(SOL.price_subtotal / SOL.qty_delivered) * (unit_amount * sol_uom.factor / ts_uom.factor)` (https://github.com/odoo/odoo/blob/16f170619d9cc5fd86529a3f17e349da37607f73/addons/sale_timesheet/report/timesheets_analysis_report.py#L42-L44). `SOL.qty_delivered` is a stored float rounded to the day UoM precision (0.01d). For 1 hour timesheeted, qty_delivered = 1/8 = 0.125d rounds to 0.13d, so the formula yields (1600 / 0.13) × (1/8) = 1538.46 instead of the correct 100. Because `qty_delivered` is recomputed each time a timesheet is added, all existing rows shift their revenue figure with every new entry. Additionally, using `price_subtotal / qty_delivered` as the per-unit rate ignores any line discount: the rate derived from a discounted subtotal divided by a delivered quantity that differs from the ordered quantity is not the effective price per day. For prepaid lines, the effective per-unit rate is `price_subtotal / product_uom_qty` — the ordered quantity is stable and the subtotal already reflects any discount — multiplied by the timesheet hours converted to the SO line UoM. opw-6150555
Deleting a field could incorrectly trigger an access error for unrelated website-form settings, even when the field being removed was not connected to them. This fix ensures the check runs with the right access level so field deletion works normally for users.
Original PR description
# How to reproduce - Install the Website module - Install another module that has atleast one model with one html field, sanitize=Flase or sanitize_form=False and groups - Remove the field's group…
# How to reproduce - Install the Website module - Install another module that has atleast one model with one html field, sanitize=Flase or sanitize_form=False and groups - Remove the field's group from the current user - Enable dev mode - Go to Settings > Technical > Database Structure > Models - Pick any model (e.g. sale.order.line) - Add a field to that model & Save - Delete the added field & Save > Note : Significantly harder to reproduce since : https://github.com/odoo/odoo/commit/9a336bbb94b0a4266d84f7554c024c3abd2d1e7c I am not sure a field as mentionned in the steps exists # The problem An access error is raised for the module wich access rights were removed, even if the module is not linked in any way with the picked model # Cause of the issue Deleting the field will endup calling the `unlink()` method of `BaseModel` on the `ir.model.fields` record. This function triggers all `@api.delete` methods defined on the model : https://github.com/odoo/odoo/blob/5538132d9d14c4cc5031fc50ac0388ad2ab0fc92/odoo/models.py#L4548-L4552 This will call the this method : https://github.com/odoo/odoo/blob/5538132d9d14c4cc5031fc50ac0388ad2ab0fc92/addons/website/models/website_form.py#L153-L154 That was introduced by : https://github.com/odoo/odoo/commit/c0a827519844ec43537e4487f6abe358bb82ba9a Which prevents a field from being deleted if it is actively used in any website form. But this method does a search on every model return by `_get_html_fields` which may contains models that are not accessible by the user, so we get an access error. # Proposed solution Since `_check_if_used_in_website_form` should perform the same independently from the user, we can do the search in sudo opw-6231951 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix removes an incorrect validation that blocked invoices when more than one pension fund tax was set on the same line. It now matches the Italian e-invoicing rules, so affected invoices can be sent and printed without errors.
Original PR description
### Steps to reproduce the issue: 1. Download Accounting and l10n_it_edi_withholding 2. Switch to IT company 3. Create 2 taxes with a Pension fund type set (in Advanced Options) 4. Create an invoice…
### Steps to reproduce the issue: 1. Download Accounting and l10n_it_edi_withholding 2. Switch to IT company 3. Create 2 taxes with a Pension fund type set (in Advanced Options) 4. Create an invoice and set on the same line the 2 taxes created 5. Click on send and print and see the error: Invoices must have at most one Pension Fund tax set per line. (even if it's not true) ### Cause of the issue: The following function check how many taxes we have per line but this limit is incorrect because it is accepted by the Italian electronic invoicing specifications to have also more than 1 tax. https://github.com/odoo/odoo/blob/bd095fe286930acc54d85bdf7f92af15569f5b82/addons/l10n_it_edi/models/account_move.py#L1268-L1273 ### Reference documentation: 1. [Art. 10 della Legge n. 183_2011, successivamente integrato dal D.L. n. 1_2012 (art. 9-bis)..pdf](https://github.com/user-attachments/files/29056003/Art.10.della.Legge.n.183_2011.successivamente.integrato.dal.D.L.n.1_2012.art.9-bis.pdf) 2. Following image: <img width="823" height="580" alt="estrattoEppi" src="https://github.com/user-attachments/assets/e79be16f-651e-467a-84f4-8400185ceea4" /> opw-6264685 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269456
This update fixes invoice sending for Guatemala when customer or product names contain accented or special characters. It ensures the XML sent to Infile uses the correct format, preventing rejected invoices and corrupted certified documents.
Original PR description
**Steps to reproduce:** * Install the **l10n_gt_edi** module. * Configure a Guatemalan company with valid Infile credentials in the settings. * Create a product or customer with special characters…
**Steps to reproduce:**
* Install the **l10n_gt_edi** module.
* Configure a Guatemalan company with valid Infile credentials in the settings.
* Create a product or customer with special characters (e.g. `ñ`, `á`, `é`) in their name.
* Create a customer invoice containing this product/customer.
* Confirm the invoice to trigger the EDI send to the SAT (Infile).
**Observed behavior:**
* Infile intermittently rejects the invoice due to validation errors, or accepts it but the resulting certified XML has truncated or malformed text exactly where the special characters were located.
**Cause:**
* Odoo uses the `requests.post()` library to send the XML payload to Infile. By default, `requests` encodes string payloads using `latin-1` unless told otherwise.
* Because the request was missing the explicit `Content-Type: application/xml` header and the XML string was not explicitly encoded to `utf-8` before sending, Infile parsed the payload using an incorrect encoding. This caused it to drop or misinterpret special characters, leading to validation failures or corrupted XML content.
**Fix:**
* Explicitly include the `'Content-Type': 'application/xml'` header in the request to Infile.
* Explicitly encode the `xml_data` payload to `utf-8` (`xml_data.encode('utf-8')`) before passing it to `requests.post()` to guarantee the correct encoding is sent over the wire.
opw-6315654