Daily updates from Odoo
Tuesday, July 22, 2025
19 changes · 18.0
Enhancements to existing features
Employees can now manually check in and check out from attendance kiosk mode when working across multiple companies. The kiosk also limits manual employee selection to the companies the user is allowed to access, making attendance tracking more usable and accurate in multi-company setups.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Manual check-in / check-out on attendance kiosk mode is not allowed Desired behavior after PR is merged: - Give possibility to user to make check-in / check-out on attendance kiosk mode - Display employees for allowed company on manual selection mode in the kiosk
Users can now resend modified vendor bills for Bizkaia Batuz reporting without cancelling and recreating the bill first. This simplifies corrections for customers and auditors while keeping the previous valid submission if the resend is rejected.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update allows employees to manually check in and check out from attendance kiosk mode in multi-company setups. It also limits the employee list in manual selection mode to companies the user is allowed to access, making kiosk use clearer and safer for organizations with multiple companies.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Manual check-in / check-out on attendance kiosk mode is not allowed Desired behavior after PR is merged: - Give possibility to user to make check-in / check-out on attendance kiosk mode - Display employees for allowed company on manual selection mode in the kiosk --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change makes the contact form easier to adapt for different business needs, such as new contact categories or multi-level company structures. Existing behavior is intended to stay the same, while partners and implementers gain more flexibility to tailor how contacts, addresses, and parent-child relationships appear.
Original PR description
Description of the issue/feature this PR addresses: The current contact UI is partly inconsistent regarding the child/parent behavior and also it is not flexible extensible regarding new contact…
Description of the issue/feature this PR addresses: The current contact UI is partly inconsistent regarding the child/parent behavior and also it is not flexible extensible regarding new contact types like "Home office Contact", which is a "Contact" but has a different Address, and also an "Delivery Address" is in my opinion not an individual, but neigther a company. Therefore I suggest to de-couple the different topics and add new variables. These variables can be customized by overwriting the "_compute_contact_type" method according to the users needs. e.g. you could add a new individual type "home office contact" which is an individual, but also has an editable address. By changing can_be_child to True on Company contacts, you could create multi-level company structures. In this patch, the current behavior is not changed, but gives better possibilities for customizations. Current behavior before PR: The forms are hardly customizable regarding new contact types. Desired behavior after PR is merged: The UI can be more customizable and using is_address_readonly, is_individual, can_be_parent and can_be_child instead of linking the type directly. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The expiration message shown in the Enterprise home menu now better reflects the updated timing, where expiration is tied to a date 15 days after the next invoice date. This helps customers understand when action is needed and encourages earlier payment instead of waiting until the last minute.
Original PR description
Previously, the database expiration date was set to the same date as the expiration field, which is now defined as 15 days after the next invoice date. Issue: Users tend to wait until the last minute to pay. This improvement aims to better handle expiration timing and encourage timely payments. TaskID: 4384877
Resolved issues and error corrections
The Today filter in Timesheets and the Attendance report now correctly limits results to records from the current day. This helps users rely on daily time and attendance views without seeing records from other dates.
Original PR description
In this bug, the today filter in timesheet is not written correctly. To reproduce the bug: 1- Create a db with timehseet installed 2- In timesheet app, and add records with different dates 3- Add Today filter 4- The filtered records are not only from today 5- The bug is also reproducible in attendance report In the fix, the filters which are used both in timesheet and report, are fixed. opw-4936780
Paid online self-ordering orders now appear correctly in the POS ticket list with the proper paid status. This prevents staff from seeing the wrong action and helps them refund paid orders instead of trying to load them again.
Original PR description
**Steps:** - Configure POS with self-ordering via QR code and online payment. - Open a session and place a paid order through self-ordering. - In the POS UI, open the TicketScreen and apply the "Paid Orders" filter. **Issues:** - The paid order does not show the "Paid" tag. - The "Load Order" button appears instead of the "Refund" button. **Cause:** - The paid order retains an outdated uiState, with `uiState.locked` incorrectly set to false. **Fix:** - Use `finalized` instead of `uiState.locked` to determine order state on TicketScreen. - Remove the unused `locked` property from `uiState` in posOrder. Task: 4745869 Related: odoo/enterprise#86684
This change restores proper scrolling on mobile screens when editing a record, such as changing a contact's country. It prevents users from getting stuck because two parts of the page were competing for scrolling, making forms easier to use on phones.
Original PR description
Steps to reproduce ================== - Use a mobile viewport - Go to contact - Open a record - Edit the contry - Try to scroll => Nothing happens Cause of the issue ================== Both the `.o_content` and the `.modal-body` are scrollable, so we need to scroll twice to go to the bottom. Solution ======== We revert https://github.com/odoo/odoo/commit/7d7aedc6149a170db2c73b1f62872bd35786c5ca opw-4851373
This change keeps a job application and its linked candidate assigned to the same company, reducing access errors for recruiters in multi-company setups. It also assigns website-created candidates to the job posting's company when appropriate and prevents a form warning from crashing when a page element is missing.
Original PR description
The problem: With the introduction candidate/applicant models in 18.0 a subtle bug got introduced. The core of the bug is that most of the field on an application are related/inherited from the…
The problem: With the introduction candidate/applicant models in 18.0 a subtle bug got introduced. The core of the bug is that most of the field on an application are related/inherited from the candidate of that application except company_id. This means that both models have a company_id and those are not synced. This can become an issue in a multi company issue where a candidate is assigned to company A while their application is in company B. The recruiter in company B will then get constant access errors because they don't have write permission on a record(candidate) in a different company. The fix: This PR aims to fix this by doing 2 things. 1. Every time the company on a application gets changed the company of the candidate is also updated. This still means that if the company of the candidate is manually changed we will have this issue again but there's no way around that (that can be implemented in stable). 2. Makes sure that candidates created through the website are assigned to the company of the job posting or no company, preventing a mismatch from happening. There was also trace-back happening on the job application form on odoo.com as a warning message div is missing there for some reason so I added a simple condition to check if that div exists to prevent the trace-back. task-4350838 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update keeps the company assigned to a job applicant and the related candidate aligned, reducing access errors for recruiters working across multiple companies. It also ensures candidates created from website job applications use the job posting's company when appropriate and prevents a website form warning from causing a crash.
Original PR description
The problem: With the introduction candidate/applicant models in 18.0 a subtle bug got introduced. The core of the bug is that most of the field on an application are related/inherited from the…
The problem: With the introduction candidate/applicant models in 18.0 a subtle bug got introduced. The core of the bug is that most of the field on an application are related/inherited from the candidate of that application except company_id. This means that both models have a company_id and those are not synced. This can become an issue in a multi company issue where a candidate is assigned to company A while their application is in company B. The recruiter in company B will then get constant access errors because they don't have write permission on a record(candidate) in a different company. The fix: This PR aims to fix this by doing 2 things. 1. Every time the company on a application gets changed the company of the candidate is also updated. This still means that if the company of the candidate is manually changed we will have this issue again but there's no way around that (that can be implemented in stable). 2. Makes sure that candidates created through the website are assigned to the company of the job posting or no company, preventing a mismatch from happening. There was also trace-back happening on the job application form on odoo.com as a warning message div is missing there for some reason so I added a simple condition to check if that div exists to prevent the trace-back. task-4350838 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Italian electronic credit notes now show tax amounts as negative values in the generated XML, matching refund behavior and Italian EDI requirements. This helps prevent incorrect electronic invoicing data and potential compliance issues when sending tax integrations.
Original PR description
**Issue** When generating the XML for a credit note, the <Imposta> field (tax amount) appears as a positive value, whereas it should be negative in compliance with Italian EDI standards. **Steps to…
**Issue** When generating the XML for a credit note, the <Imposta> field (tax amount) appears as a positive value, whereas it should be negative in compliance with Italian EDI standards. **Steps to Reproduce** 1. Install the Accounting module and Italian localization. 2. Create and confirm a vendor bill using a service-type product. 3. Reverse the bill into a credit note. 4. On the credit note, go to the Journal Items tab. 5. Assign tax 22% S RC and confirm. 6. Click Send Tax Integration. 7. Inspect the generated XML: <Imposta> appears as positive. **Root Cause** The XML generation logic for tax lines does not consider the type of the accounting move. As a result, for credit notes (in_refund or out_refund), the tax amount (<Imposta>) remains positive, even though it should be negative to reflect the refund nature of the document. **Fix** The tax amount is inverted for credit notes to ensure the XML reflects the correct financial direction. This distinction prevents misreporting, as positive values in a refund context would contradict e-invoicing standards. Ticket [link](https://www.odoo.com/odoo/project/967/tasks/4831091) opw-4831091
Odoo now completes all pending internal field updates even when those updates create additional records that also need processing. Property field updates are also handled with the right permissions, reducing unexpected access errors during routine system operations.
Original PR description
The existing implementation was incomplete when some recomputation creates a new record that has fields to recompute. The latter fields were not taken into account when recomputing "all fields". We also adapt the flushing loop in the same way. This fix revealed a related issue. Properties fields have a compute method, which is a technical artifact to update their default values when the many2one to their definition record is modified. For some weird reason, this method re-triggers its computation on the record. And in some of those cases (when the record becomes inaccessible) the second recomputation crashes (`AccessError`). The fix consists in computing the field in sudo mode.
Customer records can no longer be saved with clearly invalid phone numbers, email addresses, or website links. This helps keep contact data accurate and reduces follow-up issues caused by unusable customer information.
Original PR description
Purpose :
Prevent saving invalid contact data when creating or updating partners.
Fixes: #217386
Changes
1. Server-side validation in res.partner:
Phone/Mobile must match: ^[+\d][\d\s().-]{5,}$
Email must match: ^[^@\s]+@[^@\s]+\.[^@\s]+$
Website must:
Start with http:// or https://
Contain a valid domain and top-level domain (TLD)
2. Unit tests:
Added test_partner_validation.py
Validates:
Invalid/valid phone numbers
Invalid/valid email addresses
Invalid/valid website URLs
3. CLA:
Signed the Individual Contributor License Agreement
Added doc/cla/individual/hamzaahmedhussein.mdCredit notes sent using BIS Billing 3.0 now place discount information in the correct XML order. This prevents validation errors when issuing electronic credit notes with early payment discounts, helping ensure smoother e-invoicing processing.
Original PR description
### Issue: The structure of the bis3 XML for a credit note with an early discount is invalid. ### Steps to reproduce: - On a partner set the E-Invoicing format to "BIS Billing 3.0" - Create an…
### Issue: The structure of the bis3 XML for a credit note with an early discount is invalid. ### Steps to reproduce: - On a partner set the E-Invoicing format to "BIS Billing 3.0" - Create an invoice for this partner, confirm, send - Create a credit note from this invoice - Add a payment term with early discount to the credit note - Confirm and send to BIS Billing - The generated XML has a bad structure: https://peppol-tools.ademico-software.com/ui/document-validator ### Cause: The field `AllowanceCharge` should be after `Delivery`, `PaymentMeans` and `PaymentTerms` ([doc](https://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-CreditNote-2.1.xsd)). UBL_20 template adds `AllowanceCharge` before `TaxTotal` ([code](https://github.com/odoo/odoo/blob/ffd9c0f96bda1bad1ad2059d9be2fae54b60eace/addons/account_edi_ubl_cii/data/ubl_20_templates.xml#L570)). UBL_21 template adds `Delivery`, `PaymentMeans` and `PaymentTerms` before `TaxTotal` again ([code](https://github.com/odoo/odoo/blob/ffd9c0f96bda1bad1ad2059d9be2fae54b60eace/addons/account_edi_ubl_cii/data/ubl_21_templates.xml#L67)) As the UBL_21 template inherits frome the UBL_20 template it gets triggered after so `AllowanceCharge` ends up before `Delivery`, `PaymentMeans` and `PaymentTerms`. ### Solution: UBL_21 template now adds `Delivery`, `PaymentMeans` and `PaymentTerms` before `AllowanceCharge`. opw-4812408 Forward-Port-Of: odoo/odoo#218316
Point of Sale now calculates prices correctly when products have selectable variants with extra charges. This prevents customers from being charged duplicated or incorrect variant surcharges, especially when mixing dynamic and configurable product options.
Original PR description
When you have product with dynamic variants using extra price, the price added to the cart is not correctly computed. Steps to reproduce: ------------------- * Create a product with a dynamic variant…
When you have product with dynamic variants using extra price, the price added to the cart is not correctly computed. Steps to reproduce: ------------------- * Create a product with a dynamic variant (2 values), price 1 * Add an extra price for each value, ex 10, 20, 100 * Make a sale order with the first two variant value * Open shop * Add the product to the order 3 times, 1 for each value > Observation: Prices are respectively: 21, 41, 111 Why the fix: ------------ In the shop only `product.product` are shown. When selecting the product with variant, the product has a `lst_price` of 11 which corresponds to the price of the `product_template` plus the extra price. This value is normal but cannot be used in the computation of `get_price` as we also provide the value for `extra_price`. We can use `list_price` instead of `lst_price` as it does not include the information related to variants. Why do we see 2 different behaviors before this fix? When selecting a variant option, if the product with this variant already exist, the lst_price already contains the information about the selected variant. Which explains why, in the end, the price has twice the extra price of the variant selected. When the product with the variant does not exist, the price has one time the price of the selected variant plus one time the price of the first product with variant the is created. Configurable products were also having issues when mixing multiple variants, especially when one of them had creation mode set to "always". When exiting the configuration popup the payload does not include the extra price coming from "always" attributes. depending on what the other choices were we would have an extra price, meaning we would compute the price with `list_price` but the extra price did not include the information about the "always" variant if it had one. Using `list_price` is enough for the initial problem but only when the selected DYNAMIC variant has an extra price otherwise the code does not pass through the condition asking for extra price. In such case there is no extra price from the payload and it's still computing the price with `lst_price`, which includes information about the extra price of the variant that was already sold previously. Thus we make sure that we compute with `list_price` whenever a configurable product is added. opw-4815555
Odoo now avoids a crash when an accounting localization template code is missing during company setup or localization installation. Instead, users receive a clear message prompting them to check that the correct localization module is installed, making setup failures easier to understand and resolve.
Original PR description
The system would crash with a KeyError when attempting to load a chart of accounts using a template code that does not exist in the chart template mapping. This happened during the setup of a new company or while installing localization modules. **Steps to Produce:-** 1. Install the `Accounting` module. 2. Navigate to `Settings > Users & Companies > Companies`. 3. Create a new company, but do not select a country for it. 4. Switch to the newly created company. 5. Go to `Accounting > Configuration > Settings and set Iraq as the Fiscal Localization`. 6. Attempt to save the changes. **Error:-** `KeyError: 'iq'` **Solution:-** - Check if `template_code` exists in the chart template mapping. - Raise a proper `UserError` with a clear message if it doesn't, guiding the user to verify the correct localization module is installed. **Sentry - 6613287121, 6613283263, 6712436024** I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Mexican POS global invoices now use the journal's configured issuing address when available, instead of defaulting to the company address. This ensures the official XML shows the correct place of issue for cash sales invoiced globally.
Original PR description
Steps to reproduce: [l10n_edi_extended] - setup up a mexican company - create a journal in which you define an issued address (different from your company) - setup your pos with newly created journal - Open a pos session - add a product - pay in cash with no customer - in the backend go into the orders - select the newly created order in the list view and click on the action "create global invoice" - in the order > cfdi: download the created xml Issue: The "Lugar de expedicion" will have the zip code of the company and not from the issued address one Cause: We only check for issue adress in account_move but not from orders. Global invoice is not only for invoices but also to account for sale (orders) made with petty cash Solution: We don't want to create a bridge module only for that. We check if the field is defined on `account.journal` opw-4802389
This fixes an issue where non-admin employees saw an access denied message when entering a Colombian NIT on a contact. The certificate check now runs with the needed permissions, so contact creation works smoothly while keeping the process unchanged for users.
Original PR description
When entering a Colombian Tax ID (NIT), the onchange checks `company.l10n_co_dian_certificate_ids`, which reads `certificate.certificate` records , resulting in an “Access Denied” popup. This changes Wrap the certificate lookup in `company.sudo()` so that the NIT VAT‐onchange can fetch the DIAN certificate records. Steps to Reproduce: 1. Install the l10n_co_dian module and switch to CO company 2. Create a user with only the “Internal User” (base.group_user) group—do not grant Administration/Settings. 3. Log in as that non-admin user. 4. Go to Contacts -> Create. 5. Set Country = Colombia and Identification Type = NIT. 6. Enter any VAT (NIT) number and leave the field. 7. “Access Denied” error for certificate.certificate. opw-4936604
This fixes an error that could occur when two users tried to confirm the same loan at the same time. The loan confirmation process now handles overlapping actions more safely, reducing interruptions for accounting users.
Original PR description
This error occurs when multiple users attempt to `Confirm` the same account loan simultaneously from different browsers. Steps to reproduce: --- - Install `account_loans` module - Give 'Demo' user the rights of `Administrator` for accounting - Login with Demo in another browser - Open the same record in two separate browsers and attempt to `Confirm` it in both Traceback: --- ValueError: Expected singleton: account.move(4421, 3033) At [1], filtered(...) can return multiple records. Accessing `.state` directly on a multi-record set raises a ValueError because Odoo expects a singleton (only one record) when accessing a field directly. [1]- https://github.com/odoo/enterprise/blob/1474a85084a860e307302387bcb24015aa486663/account_loans/models/account_loan_line.py#L72-L75 sentry-6316959067