Daily updates from Odoo
Wednesday, July 8, 2026
60 changes · saas-19.1
New functionality added to Odoo
This update enhances Odoo's payment processing capabilities by adding support for PayNow payments in Singapore, as well as additional currencies (SGD and USD) and card brands (JCB and AMEX). These changes broaden Odoo's payment options and cater to a wider range of customer preferences and regional markets.
Original PR description
This commit expands Xendit support to include the Singaporean market and additional card brands. The following changes were made: - Added support for the PayNow (SGQR) payment method. - Added SGD and USD to the list of supported currencies. - Added JCB and AMEX to the supported card brands (available for some markets). - Updated the base payment provider data for Xendit to include PayNow. Task-5964309 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274769 Forward-Port-Of: odoo/odoo#253542
Enhancements to existing features
Hong Kong payroll data is now included in the automatic payroll update process. This helps keep standard, unmodified salary rules up to date, reducing manual maintenance and improving payroll reliability for Hong Kong users.
Original PR description
Currently, the "Payroll: Update data" cron doesn't work for HK payroll as we never set up the _get_data_files_to_update. We can set up the list of data files to keep up to date to better support our users by automatically keeping non-edited salary rules up to date. task-6360339 Forward-Port-Of: odoo/enterprise#122777
The Azerbaijani Manat (AZN) now uses its official symbol, ₼, in the system. This improves display accuracy for users working with Azerbaijani currency and helps ensure printed and on-screen documents look correct.
Original PR description
This commit updates the base currency symbol for the Azerbaijani Manat (AZN) to its official Unicode character '₼'. Related Upgrade PR: https://github.com/odoo/upgrade/pull/10107 task-6112867 Forward-Port-Of: odoo/odoo#262471
When users resize tables in the editor, the horizontal scrollbar now appears on the table itself instead of expanding the whole editing area. Existing resized tables are also updated automatically, so the behavior is consistent when reopening content. This makes table editing smoother and avoids layout issues in the editor.
Original PR description
#### Description of the issue this PR addresses: - Resized tables stored their width on the `table` element. When such tables became wider than the editable area, a horizontal scrollbar appeared on the main editable. #### Desired behavior after PR is merged: - Width handling is now moved to `tbody` so the scrollbar stays on the table itself instead of the editable. - This PR also updates already resized tables when loading the editor to ensure they follow the same behavior. task-5123011 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272977
Creating backorders for large receipts is now much faster and less likely to fail when many stock operations are involved. This improves validation times for high-volume warehouse transactions and helps prevent system timeouts and database memory errors.
Original PR description
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of…
## The Problem Validating receipts with a large number of operations by creating a backorder was timing out due to $O(N*M)$ filtering inside `_get_qty_received_without_self`, where N is the number of moves in the picking, and M is the number of moves being processed for valuation. This happened while computing price units for moves one by one: each move filtered all picking moves to find the ones with the same `product_id`. Another issue was a PostgreSQL `"memory exhausted"` error caused by generating a large number of OR'ed conditions, equal to the number of processed moves. ## The Solution The massive filtering was fixed by filtering moves of the `purchase_line` instead of the `picking`, which is typically associated with only a few moves. This is still correct as the loop just after already ignores moves that don't have the same `purchase_line_id` of `self` anyways. The PostgreSQL error was fixed by grouping moves by `location_dest_id` and generating one condition per location using an `in` clause, which is typically much smaller than generating one condition per move. ## Benchmark Benchmark on a customer database, validating a receipt with 10k+ operations by creating a backorder: ```text Time: timeout -> 6 min ``` OPW-6272667 Forward-Port-Of: odoo/odoo#273555 Forward-Port-Of: odoo/odoo#269350
This update makes the tax supply date available for German accounting flows. It helps businesses record the correct date for tax reporting, reducing manual work and the risk of inconsistencies in local compliance processes.
Original PR description
Forward-Port-Of: odoo/odoo#272461
Resolved issues and error corrections
The Turkish Central Bank currency provider now uses the official selling rate instead of averaging buying and selling rates. This improves accounting accuracy for Turkish transactions and better aligns import valuation with local customs requirements.
Original PR description
## Short fix summary: The TCMB (Central Bank of Turkey) provider computed the exchange rate as an average of the buying and selling rates (`2 / (ForexBuying + ForexSelling)`). This is inaccurate for real accounting flows and does not follow Turkish customs regulation (Customs Law No. 4458, Art. 30), which requires the Central Bank's selling rate for goods import valuation. This now uses the selling rate (`ForexSelling`) only. task-6227500 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#122770
Shopfloor users can now see employee profile photos instead of generic placeholder images when viewing or changing operators. This makes it easier for manufacturing teams to identify the right person during shopfloor operations.
Original PR description
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data -…
Currently when a shopfloor operator modifies the operator, operator images are not visible and only place holder images are visible. ## Steps to produce: - Install Manufacturing with demo data - Login as Marc Demo - Open shopfloor ## Observed Behavior: Operator is unable to see their own profile picture and when you press the Edit Operator the operator is not able to see their as well as other operators images as well. ## Root cause: This issue occurs because by default an operator's user does not have access right for `Employees` in Human Resources section as they are not an HR officer nor an administrator. So when the user tries to access the image url that is meant for users with those access rights (HR officer / Administrator) at [1] and at [2]. The system returns a placeholder image instead of the employee image since the operator does not have access to hr employee model but does have access to hr employee public as per this [commit](https://github.com/odoo/odoo/commit/c9ca3761464413327d2beb697553a3ccd7eef4d1) [1]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/dialog/mrp_employee_dialog.js#L16 [2]- https://github.com/odoo/enterprise/blob/5fe2fb44f4c8a938390343f7cdc90674cbf09391/mrp_workorder/static/src/mrp_display/employees_panel.xml#L9-L10 ## Solution: Allowing operators to see images can be done by modifying the links to use the public model instead of the private model. This will allow operators see other operators based on images and should improve user experience. | Before | |--------| | <img width="1865" height="844" alt="image" src="https://github.com/user-attachments/assets/c28f4209-0a60-486a-bfec-2a4db39cd946" /> | | <img width="995" height="899" alt="image" src="https://github.com/user-attachments/assets/7541edd9-6cc9-42d1-b5c5-39e0740d9a93" /> | | After | |--------| | <img width="1881" height="903" alt="image" src="https://github.com/user-attachments/assets/df3f8357-5cc2-4688-83dd-9a009fd56957" /> | | <img width="1221" height="862" alt="image" src="https://github.com/user-attachments/assets/c9d72b07-e1ad-43e4-85f5-00d8f1db9095" /> | opw-6321989 Forward-Port-Of: odoo/enterprise#121751
Belgian CODA bank statement imports no longer fail when the file contains a type 4 block. This prevents an import error and lets users process affected bank files normally.
Original PR description
### Issue: After the fix in commit (https://github.com/odoo/enterprise/commit/3ef8ae7a6b8eb362e18c74dfab9aadce792b5dc2), importing a CODA file containing a type 4 block raises a traceback ### Cause: That commit introduced `communication_struct_by_ref_move`, which iterates over all lines and accesses `line['communication_struct']` Type 4 lines are not assigned a `communication_struct` value by the parser in `_get_coda_file_statements` Accessing the key directly raises a `KeyError` in `_get_coda_final_statements` in `communication_struct_by_ref_move` ### Steps to reproduce: - Install `l10n_be_coda` - Switch to the BE company - Create a Bank Journal with account `BE33737018595246` - Go to the Accounting Dashboard and import a CODA file containing a type 4 block (Like the one on the ticket) Before the fix, a traceback is raised on import opw-6363148 Forward-Port-Of: odoo/enterprise#123222
Pasting document links into an empty message no longer adds an unnecessary blank line at the start. This keeps shared document messages cleaner while still separating links from any existing text.
Original PR description
Before this commit, adding document links always prepended a line break before the generated links. When the composer was empty, this resulted in messages starting with an unnecessary blank line. This commit only inserts a line break when the composer already contains text, avoiding the extra spacing while preserving the separation between existing content and pasted links. task-[5947683](https://www.odoo.com/odoo/project/1519/tasks/5947683) Forward-Port-Of: odoo/enterprise#120952
Timesheets now mark public holidays and approved personal time off based on each employee's own working schedule instead of always using the company's default schedule. This helps employees and managers see accurate unavailable days when entering or reviewing time.
Original PR description
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different…
Steps to Reproduce --- - Create two different working schedules with different public holidays - Assign employees to specific working schedules - Set company default working schedule to a different schedule - Login as employee with specific working schedule - Navigate to Timesheets app -> My Timesheets - Observe public holidays and personal time-off displayed in the timesheet grid Issue --- - The timesheet grid displays unavailable dates (public holidays, weekends) from the company's default working schedule instead of the employee's assigned working schedule. - Personal time-off requests are not reflected as unavailable dates in the timesheet grid. Current Behaviour --- - Public holidays shown are always from the company's default working schedule, ignoring employee-specific working schedule assignments. - Employee's approved time-off requests don't appear as unavailable in the timesheet. Expected Behaviour --- - Public holidays should display based on the employee's assigned working schedule, with company schedule as fallback only when no specific schedule is assigned. - Employee's personal time-off requests should appear as unavailable dates. - This should align with Time Off app behavior. Fix --- - Included employee-specific work interval calculation with personal time-off requests. - Added support for contract-based calendar changes and calendar validity periods. - Implemented proper fallback when valid intervals are not found. task-4997080 Forward-Port-Of: odoo/enterprise#95458
Lazada order synchronization no longer updates individual item statuses when they differ from the overall order status. This prevents delivered orders from failing to sync when a later item cancellation would otherwise reduce quantities below what has already been delivered.
Original PR description
Lazada stores order statuses at the item level. When an item is canceled, we mirrored this by decreasing the ordered quantity on the sale order line. But if the item was already delivered, decreasing the quantity below the delivered amount is forbidden and raises a `UserError`, which aborts the whole order sync:
```python
File ".../sale_stock/models/sale_order_line.py", line 420, in _update_line_quantity
raise UserError(_('The ordered quantity of a sale order line cannot be decreased below the amount already delivered. [...]'))
```
In practice, item-level statuses only differ from the order status in exceptional cases. Stop syncing statuses at the item level and assume the entire order shares a single status, which avoids the quantity decrease and the resulting traceback.
opw-6267730
Forward-Port-Of: odoo/enterprise#122851Shopfloor barcode scanning now correctly opens the related product action even when the component is tied to a manufacturing operation. This prevents operators from scanning a valid product barcode and seeing no response, reducing confusion and improving work order flow.
Original PR description
When scanning a product barcode in the shopfloor, it would not simulate a click on the product if the corresponding BoM line was linked to an operation. Steps to reproduce: ------------------- * Create a BoM for product A with a BoM line for product B and link it to any operation. * Create a WO for product A and confirm it * Open the shopfloor with the WO and scan the barcode of product B > Observation: Nothing happens Why the fix: ------------ We simulate the onClick for the product even if the move is not marked manual_consumption. opw-6268665 Forward-Port-Of: odoo/enterprise#122337
Previously paused checks for rental stock valuation and Kenyan electronic stock reporting have been restored and updated. This helps ensure stock values now align with vendor bill amounts, reducing the risk of incorrect reporting or inventory valuation going unnoticed.
Original PR description
*: sale_stock_renting, l10n_ke_edi_oscu_stock Re-enable and adapt the tests skipped to fast merge the valuation refactoring made in 08b62a4bbcc6f9a391b2cc00a621ef4c76100229. The stock IO now values the receipt from the vendor bill, so the shared purchase fixtures `l10n_ke_edi_oscu` need to match the values provided in `l10n_ke_edi_oscu_stock` see for instance: https://github.com/odoo/enterprise/blob/ce68644f97ac28568b9497a18079a4ad5ce4a125/l10n_ke_edi_oscu/tests/expected_requests/save_purchase_2.json#L11-L13 Forward-Port-Of: odoo/enterprise#122857
This fix prevents an error when EasyPost returns an empty tracking record while processing shipment tracking links. Users can continue working with deliveries without being interrupted by a system traceback in this edge case.
Original PR description
The PR https://github.com/odoo/enterprise/pull/111833 handled the specific case when the tracker data is missing from the EasyPost response, however in certain cases `tracker` key exists, but it has a `None` value, which leads to a traceback when trying to access the stock move:
```
File "/home/odoo/src/enterprise/18.0/delivery_easypost/models/easypost_request.py", line 392, in get_tracking_link
public_url = shipment.get('tracker', {}).get('public_url')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'
```
This commit provides a fallback to avoid getting the error from the side of the user.
opw-6270242
Forward-Port-Of: odoo/enterprise#119400The website now shows the exact configured percentage discount for subscription products when prices are displayed with tax included. This prevents customers from seeing a lower discount, such as 4% instead of the intended 5%, due to rounding precision issues.
Original PR description
Steps to reproduce: 1. Install eCommerce and Subscriptions. 2. Create a 21% Excluded tax. 3. Create a subscription product with a price of 45 with 21% tax and enable "Accept One-Time" in the…
Steps to reproduce: 1. Install eCommerce and Subscriptions. 2. Create a 21% Excluded tax. 3. Create a subscription product with a price of 45 with 21% tax and enable "Accept One-Time" in the Recurring Prices tab. 4. Publish the product on the website under the Sales tab. 5. Create a pricelist for 6 months recurring with two lines: - If min quantity is 0, then 0% discount - If min quantity is 2, then 5% discount 6. Set "Display Product Prices" to "Tax Included" in the Settings. 7. Open the product on the website, select the 6-month plan, and increase quantity to 2. Issue: The discount percentage displayed on the website shows 4% instead of the configured 5%. Why this happens: In `_get_additionnal_combination_info`, the discount is reverse-calculated from the tax-included price vs the tax-included sales price. When the 21% tax is included to both prices, it introduces a floating-point precision loss (4.9954..%), which floor() then truncates to 4%. Fix: When the pricelist rule uses 'percentage' discount, read `percent_price` directly from the pricing rule instead of reverse-calculating from tax-adjusted prices, as it represents the exact discount percentage the merchant configured with no floating-point involvement. opw-6224735 Forward-Port-Of: odoo/enterprise#121654
Popover content now looks more consistent and easier to use in dark mode. Secondary buttons are more clearly visible as actions, and forms inside popovers better match the surrounding popover styling.
Original PR description
Before this commit, content like form & secondary button rendered inside popovers had inconsistent styling in dark mode: - secondary buttons did not stand out properly from the popover background, making them look like plain text blocks rather than actionable buttons; - forms rendered inside popovers (such as the multi-create popover) kept their default background, which visually clashed with the popover background. This commit fixes these issues by: - adding dedicated secondary button background colors for popovers, including hover state; - aligning form backgrounds inside popovers with the popover background. task-6249985 Forward-Port-Of: odoo/enterprise#121631
The Balance Sheet report now avoids a crash that could happen after adding a custom Journal Item field in Studio and filtering by analytic account. This keeps financial reporting accessible for users working with analytic accounting and customizations.
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 Forward-Port-Of: odoo/enterprise#122244 Forward-Port-Of: odoo/enterprise#116251
DHL deliveries no longer fail when customer or sender addresses use regions with one-character province codes, such as some areas in Spain, Argentina, or China. The system now sends DHL the accepted full province format, helping users validate and create shipments successfully.
Original PR description
Steps:
- Install delivery_dhl_rest
- Create a new customer with barcelona as address
- Create a new Delivery
- Set DHL
- Validate de delivery
- Validation error #/customerDetails/receiverDetails/postalAddress/provinceCode: expected minLength: 2, actual: 1
DHL requires `provinceCode` to be at least 2 characters. Several countries in `res.country.state` data use single-character codes (e.g. ES: B, M, A…; AR: C, B, S…; CN: 京, 沪…). This caused API validation errors when shipping from or to addresses in those regions.
Add `PROVINCE_CODE_MAP`, a dict keyed by `(country_ISO2, state_code)`, mapping each offending code to its ISO 3166-2 form (e.g. ('ES', 'B') -> 'ES-B'). Both `_get_consignee_vals` and `_get_shipper_vals` now look up the map before sending `provinceCode`, falling back to the raw code for countries not in the map.
links: https://developer.dhl.com/api-reference/mydhl-api-dhl-express#shipments
opw-6341745
Forward-Port-Of: odoo/enterprise#122138Easypost shipping documents now use the sale order currency when it is available, instead of defaulting to the company currency. This helps international shipments show the correct invoice currency and reduces errors in customs paperwork.
Original PR description
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a…
Issue ----- When shipping internationally with Easypost, the currency on the commercial invoice does not always match the one of the sale order. Steps to reproduce ----- - Install Easypost - Create a new pricelist using a different currency from the company - Create a SO - Some product with a weight & HS code - Customer must be in another country from company (for commercial invoice) - Use the new pricelist - Add easypost delivery - Confirm SO - Validate linked picking > Commercial invoice uses company currency instead of SO's Cause ----- The currency being sent to Easypost is retrieved from the package in https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L146 The package object is actually created by calling the carrier's `_get_packages_from_picking` method https://github.com/odoo/enterprise/blob/4c540f450d4de8b59b871662123f85ed54cca2a9/delivery_easypost/models/easypost_request.py#L266-L270 Solution ----- We could be fixing this in `stock_delivery` by creating the package with the correct currency when calling `_get_packages_from_picking`. The problem with this approach is that this might negatively affect other carrier services, as discussed in https://github.com/odoo/odoo/pull/268224. Instead, we can apply a band-aid fix to take the currency from the picking's sale in the `delivery_easypost` module, which is the only one where the problem was reported. ----- Ticket: opw-6224883 Forward-Port-Of: odoo/enterprise#123083
Marketing Automation now shows the correct reason when a campaign activity is cancelled because a record no longer matches the campaign filter. This prevents users from seeing a misleading “Record deleted” message when the underlying customer or lead record still exists.
Original PR description
`sync_participants` calls `action_set_unlink` on every participant whose record is no longer in the campaign domain, and `action_set_unlink` writes "Record deleted" on each scheduled trace. The…
`sync_participants` calls `action_set_unlink` on every participant whose record is no longer in the campaign domain, and `action_set_unlink` writes "Record deleted" on each scheduled trace. The removed bucket also contains records that still exist but no longer match the campaign filter, so the cancelled trace dialog shows "Record deleted" even when the record was only filtered out. In `sync_participants`, the to_remove participants are split between those whose record still exists in the database (filtered out by the campaign domain) and those whose record was actually deleted. `action_set_unlink` accepts an optional `trace_message` argument, defaulting to "Record deleted", and the filtered-out batch passes "Record no longer matches campaign filter" so the cancelled trace dialog reflects the real cause. Steps to reproduce: 1. Install Marketing Automation and CRM. 2. Open Marketing Automation, create a campaign on Lead with filter Stage = New. 3. Add a begin activity to the workflow. 4. Open CRM, create a Lead in the New stage. 5. Back in the campaign, click Generate Participants. 6. In the CRM pipeline, drag the Lead from New to Qualified. 7. Back in the campaign, click Generate Participants again. 8. Open the Participants smart button, click the participant for the moved Lead. 9. Click the cancelled activity in the workflow timeline. => The activity dialog shows "Error message: Record deleted" although the Lead still exists. Ticket [link](https://www.odoo.com/odoo/project/49/tasks/6251264) opw-6251264 Forward-Port-Of: odoo/enterprise#118692
This fix ensures the Frontdesk app includes the needed scheduling component for its timeline-style planning view. It prevents missing dependency issues so users can access the view without installation or loading errors.
Original PR description
runbot-237869
The POS now shows longpolling as the default IoT connection type instead of WebRTC. This avoids misleading users when their IoT setup does not support WebRTC, while still allowing WebRTC to appear once it is actually used.
Original PR description
Before this commit, the connection status for the IoT HTTP service was defaulting to "webrtc", at least until the first IoT call is made. This would lead to WebRTC being shown as the connection type in the POS even when using a new IoT image that doesn't support it. After this commit, we use "longpolling" as the default status, which is supported by all IoT images. If WebRTC is being used, that status will be set after the first action is performed. task-6366366 Forward-Port-Of: odoo/enterprise#123445
This update preserves the expected behavior of file navigation in Knowledge after a Chrome browser change. It prevents a minor compatibility issue where scrolling actions could return a different result than before, helping keep the user experience stable.
Original PR description
Since Chrome 150, scrolling methods like `scrollIntoView()` return a Promise instead of `undefined`. This commit adds block braces to ensure the action returns `undefined` and keeps the same behavior as before. Reference: - https://chromestatus.com/feature/5082138340491264 - https://chromium.googlesource.com/chromium/src/+/50f3e3d0a9bc02aad8b8161dbdd59046991dd2c7 runbot-941309 Forward-Port-Of: odoo/enterprise#123231 Forward-Port-Of: odoo/enterprise#123031
The Peru Profit and Loss report now includes Other Operating Income when calculating gross profit. This ensures gross profit, operating profit, pre-tax result, and net profit reflect these income entries accurately.
Original PR description
**Steps to reproduce:** 1. Install `l10n_pe` and switching to the Peru company. 2. Create and post a journal entry with a line on account 7520000 (Other Operating Income). 3. Open the Profit and Loss report (PE). 4. The amount appears correctly under "Other operating income" (`PE_PNL_A_5`). 5. "Gross profit", "Operating profit" , "Result before taxes" and "Net Profit" do not change when this amount is added or removed. **Issue:** The "Other operating income" line is excluded from the Gross Profit calculation, and consequently from Operating Profit and every downstream total in the PE Profit and Loss report. **Why this happens:** Gross Profit (`PE_PNL_A_4`) balance expression uses the aggregation with formula `PE_PNL_A.balance - PE_PNL_A_3.balance`, which doesn't include `PE_PNL_A_5.balance` as a term opw-6283907 Forward-Port-Of: odoo/enterprise#123063
The French Intrastat export warning links now open only the journal entries related to missing required values. This helps users quickly find and correct the affected records instead of sorting through unrelated entries.
Original PR description
Steps to reproduce: 1. Have a French company with intrastat report module installed 2. Create and validate a bill to another EU country, without filling out at least one of the required intrastat fields 3. Go to the intrastat report, and export it as XML DEBWEB2 4. In the export wizard, click on the internal links on the warning messages Issues: 1. In the Intrastat report in French localization, when there are missing values detected in the export, the Export Wizard shows internal links that lead to every journal entries - instead of showing only the relevant entries. The warning banner on the report uses the action action_invalid_code_moves which has a domain to limit what is shown on the view form. However in the method _fill_value_errors there was no domain. opw-6215339 Forward-Port-Of: odoo/enterprise#117997
Grid views grouped by a selection field now show the readable label in the list title when users open a cell's details. This avoids exposing internal technical values and makes the screen clearer for end users.
Original PR description
When grouping a grid view by a selection field and clicking on the cell magnifier, the list title showed the technical name (e.g. non_billable) instead of the display name (e.g. "Non Billable"). This commit adds a condition specifically for selection fields, ensuring that their display names are used. task-5980035 Forward-Port-Of: odoo/enterprise#122303 Forward-Port-Of: odoo/enterprise#120894
This fixes blank US check printing so the payment stub lines appear consistently, matching pre-printed check behavior. It also adjusts the blank check layout so the bottom section fits on a single page, reducing printing issues and wasted checks.
Original PR description
See individual commits. task-6359599 Forward-Port-Of: odoo/enterprise#123144
Opening the restriction fields on appointment slots no longer causes an error. This fixes a broken form interaction so users can manage appointment slot restrictions without interruption.
Original PR description
Clicking the "Restrict to User" or "Restrict to Resources" field on a slot crashed with:
invalid input syntax for type integer: "appointment_type_id.staff_user_ids"
The field domain was a quoted string instead of a list, so it was passed through as a literal value. Remove the domain: it never filtered anything and only broke the form.
opw-6349497
Forward-Port-Of: odoo/enterprise#122651Pakistan’s currency symbol (PKR) is now shown before the amount instead of after it. This makes currency formatting match common market practice and improves the clarity of displayed amounts for users in Pakistan.
Original PR description
This commit updates the symbol position of Pakistan's currency (PKR) to `before amount`, as previously it was displayed `after amount`, which is not the market practice; as shown over [here](https://drive.google.com/file/d/14pcqXTZSR0BygBXGvgQ6oCLRW0-4dYUh/view?usp=drive_link). This is a backport of [PR](https://github.com/odoo/odoo/pull/269204) task-6236452 Forward-Port-Of: odoo/odoo#271499
This change corrects overtime totals for employees on flexible weekly schedules when public holidays are involved. It ensures leave time is calculated properly across time zones, so extra hours are shown accurately instead of being overstated.
Original PR description
Currently, flexible weekly overtime deducts the raw leave interval duration. ## **Steps to reproduce:** - Install hr_holidays and hr_attendance - Create an employee with flex 40h/week working…
Currently, flexible weekly overtime deducts the raw leave interval duration. ## **Steps to reproduce:** - Install hr_holidays and hr_attendance - Create an employee with flex 40h/week working schedule. - Employee profile>setting>Default Ruleset>Employee schedule Rule and set `If the worked hours on a`: `week`. - Create a public holiday on Monday. - Record daily 8h from Tue to Sat (12 AM to 8 AM). ## **Observed Behavior:** Attendance List View computes "Worked Extra Hours" incorrectly as 20:30h ## **Expected Behavior:** "Worked Extra Hours" should be computed as 8h ## **Root Cause:** In [_get_daterange_overtime_undertime_intervals_for_quantity_rule](https://github.com/odoo/odoo/blob/53448e5445c8bcbf12126bf27bd675f4b9883d05/addons/hr_attendance/models/hr_attendance_overtime_rule.py#L290-L342), the system manually calculates overtime for flexible employees by deducting `schedule['leave']` durations from the expected working hours at [1]. However, for global public holidays, the system mishandles the timezone conversion within this schedule dictionary. Because public holiday intervals are stored and processed using UTC datetimes before being converted to the employee's local timezone, converting it to the employee's local timezone causes the holiday hours to shift and overlap into the next calendar day. As a result, the `schedule['leave']` calculation incorrectly thinks the employee had time off on normal working days, which throws off the final overtime amount. [1]: http://github.com/odoo/odoo/blob/53448e5445c8bcbf12126bf27bd675f4b9883d05/addons/hr_attendance/models/hr_attendance_overtime_rule.py#L304-L307 ## **Fix:** Replace the manual leave subtraction logic with the existing `_get_expected_hours_from_contract` method. This method naturally handles global public holidays and computes attendance intervals safely across different timezones without shifting hours into the wrong day. **opw-6259328,6284145** Forward-Port-Of: odoo/odoo#269293
The Project smart button on sales orders now works even when the order has no order lines. This fixes a situation where a linked project was visible but clicking it did nothing, making it easier for users to access the related project directly.
Original PR description
Steps to reproduce: - - Create a sale order. - Link a project using the Project field. - Confirm the sale order. - Click on the Project smart button. Issue: - The Project smart button is displayed since the sale order has a linked project. However, clicking on it does nothing. Cause: - A sale order without order lines can still have projects linked through the project_id field. The action should not assume that no order lines means there are no projects to display. Solution: - Remove the unnecessary order line check and allow the existing logic to open the linked projects. task-6209658 Forward-Port-Of: odoo/odoo#270752
Invoices sent through Nilvera could get stuck in an intermediate “Unknown” state and stop being checked again, even though Nilvera later updates them to a final result. This change makes Odoo keep polling those invoices until the final status is received, reducing manual follow-up and preventing invoices from remaining unresolved.
Original PR description
## Short fix summary:
Nilvera reports `Unknown` as a normal, transient `StatusCode` value (their own e-Archive API docs
list the enum as `unknown`/`waiting`/`succeed`/`error`) right after a document is sent, before their
daily batch resolves the final status. But `_cron_nilvera_get_invoice_status`'s search domain only
matches `l10n_tr_nilvera_send_status in ('waiting', 'sent')`, so once an invoice lands on `unknown` it
is never polled again — even after Nilvera later resolves the real status on their side. This adds
`unknown` to that domain so these invoices keep getting polled until Nilvera reports a final status.
task-6328589
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#274494
Forward-Port-Of: odoo/odoo#274311The mailing editor’s snippet picker now appears in the correct place again when an AI chat is open. This prevents the editor from getting stuck and lets users continue saving, discarding, and adding content normally.
Original PR description
When an AI chatbox is active, all non-error dialog overlays are set to be behind the chatbox through their z-index. This causes an issue where the dialog overlay that adds new snippets to a mailing is placed behind the fullscreen mailing editor, preventing its use and freezing the use of some commands (save & discard). This commit restores the snippet dialog's z-index to its original value. Steps to reproduce: - Create a new mailing - Select a builder-enabled theme (such as Events Promo) - Open a new AI chat by clicking the AI icon in the top right - Open the fullscreen editor - Click on the Headers block category task-6321624 Forward-Port-Of: odoo/odoo#274715
This change restores the previous reservation check so packs are handled correctly when inventory reservations are freed and reassigned during validation. It prevents some stock moves from being lost in the process, which helps ensure the correct picking is updated and stock operations stay accurate.
Original PR description
This reverts commit 5d70f75f1d27577ee4e2121497ce477cfa6cda53. `free_reservation` is called once per move line to validate. The goal is to unlink potential move lines that have the same reservation. After finding them, a force re-reservation is triggered. The idea of the previous commit was to call `check_entire_pack` (caused by the re-reservation) only once and not at each move line `free_reservation`. The issue is the stock move that has been unreserved then re-reserved are lost in the process and only the picking that had at least one move line validated are actually calling `check_entire_pack`. 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 Forward-Port-Of: odoo/odoo#273813 Forward-Port-Of: odoo/odoo#273658
This change fixes an unreliable automated test in the messaging app. The test now matches how the product really works, so it should stop failing randomly and give more dependable results for future updates.
Original PR description
The `bus subscription is refreshed when channel is joined` test is sometimes failing. This test doesn't make sense: it opens the command palette and wait for a subscription to be made. However, a subscription is only done when needed (opening the thread or being a member of the channel). The step was satisfied by luck. This commit fixes the test to reflect production code: the subscription is made once the channel is opened. runbot-941462 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 Forward-Port-Of: odoo/odoo#274741
The print button on customer invoices is changed back to its original primary style. This reduces confusion and keeps the button appearance consistent unless the invoice has already been sent, where the secondary style still applies.
Original PR description
In task-6269645, the print button on customer invoices was set to secondary instead of primary. This is a mistake and it's confusing, so it's being reverted in this commit because it only needs to be secondary if the move is sent. task-6357618 Forward-Port-Of: odoo/odoo#273953
This fix ensures products tracked by lots get the right cost when their inventory comes from purchases with different prices. As a result, both the lot cost and the product's standard price are calculated accurately, which improves stock valuation and financial reporting.
Original PR description
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots…
This PR is needed for the fix of https://github.com/odoo/odoo/pull/272411 **Problem:** lot's standard price are not correct when the product is fifo and move have different values and multiple lots **Steps to reproduce:** - product fifo tracked and valued by lots - 20 IN @ 100 (all in lot 1) - 10 IN @ 10 (5 in lot 1 and 5 in lot 2) - on the product form click on the lot/serial number smart button and select lot 1 **Current behavior:** the average cost of lot1 is 64 back on the product form the standard price is 55 **Expected behavior:** the average cost of lot 1 should be 20 * 100 (from move1) + 5 * 10 (from move 2) / 25 = 2050 / 25 = 82 the standard price of the product should be 2100 / 30 = 70 **Cause of the issue:** Because the product is fifo, to compute the avg_cost of the lot we call _run_fifo() https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/stock_lot.py#L47 which calls _run_fifo_get_stack() to get the fifo stack specific to this lot. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 1) run_fifo_get_stack() stores the on hand quantity (for the lot if a lot is given as param) in fifo_stack_size and, as long as there is moves and fifo_stack_size>0, adds move (starting from the last one in date) to the stack and removes the quantity of the move from fifo_stack_size. It then returns the moves stack and the remaning quantity on the first move of the stack (for the rest we know it's the full quantity) https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L612-L618 Inside run_fifo_get_stack(), to do this, because we're only considering the quantities from this specific lot we should only remove the quantity from the move that went in lot, but currently we're removing the quantity from the entire move. https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L615-L618 So, at the first iteration of the while loop (for the move with 10 quantities), instead of doing fifo_stack_size(25) -= 5, we do fifo_stack_size(25) -= 10 The next move is the last one, so it's the one on which remaining_qty_on_first_stack_move will be based on. remaining_qty_on_first_stack_move will be the minimum between the move's quantity and the fifo_stack_size. So because the fifo_stack_size is now wrongfully 15 instead of 20 that's the value that will be returned by _run_fifo_get_stack. So inside run_fifo(), qty_on_first_move will be 15 instead of 20 https://github.com/odoo/odoo/blob/456026b5ef99388b1cf5bdd78cee8d1ad3d51304/addons/stock_account/models/product.py#L545 Issue 2) Another issue is that inside _run_fifo when calling _get_valued on the move, we don't use the lot parameter. So we use the entire quantity of the move instead of the quantity specific to the lot. https://github.com/odoo/odoo/blob/b07ff5843ee87741b293d9e67f72a77a2ed2ed88/addons/stock_account/models/product.py#L561-L562 And we use the full value of the move instead of the pro rata of the value for the quantity specific to the lot As a consequence, inside _run_fifo the computation for the fifo_cost will be 15 (because of issue1) * 100 $ [first iteration of the while loop] \+ 10 (because of issue 2) * 10$ [second iteration of the while loop] = 1600$ Instead of 20 *100 + 5 *10$ = 2050$ Therefore the avg_cost of the lot is wrong and the standard price of the product will also be false. side note: those two issues balance each other if the price unit of the moves are the same needed for PR of opw-6311341 Forward-Port-Of: odoo/odoo#273728
This update changes the website title snippet so its text alignment can be adjusted directly in the editor. It removes a styling limitation that previously blocked users from changing alignment, making layout edits easier and more flexible.
Original PR description
`s_title_form` comes with the `text-center` utility class which forbids edition through the web_editor, it needs to use inline-style instead. task-6149380 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#263334
This change updates a system setting so the payment proxy uses demo values instead of test values. It helps keep the environment consistent with the intended setup and avoids mismatched configuration for current users.
Original PR description
The system parameter is already brought to demo in account_peppol module. But the current users are not for pdp. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272462
This update adds a backup check for Mollie payments so confirmation can still be detected even if the real-time server connection is slow or unavailable. As a result, customers should no longer need to use the Force Done button in most cases and may only wait a few seconds for the payment to complete.
Original PR description
Due to the unreliability of the bus during peak server times, clients were missing the websocket payment confirmations from the backend. This meant they had to use the Force Done button to confirm the payment. This commit adds a polling mechanism similar to that used for Viva.com, which polls the backend directly every 5 seconds to check the status of the payment. This means that instead of being blocked, the client should only experience at most a 5 second delay, even when the websocket isn't working. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274713
When a Point of Sale order line quantity was changed during a refund, the system was not applying the order’s fiscal position when recalculating taxes and prices. This fix ensures the correct tax mapping is used, so refunded amounts stay consistent and accurate.
Original PR description
When changing the quantity of a pos order line the fiscal position set on the order was not used when recomputing the line price and taxes. Steps to reproduce: ------------------- * Create a tax with 15% rate and another with 10% rate * Create a fiscal position that maps the 15% tax to the 10% tax * Setup a PoS to be able to use that fiscal position * Open the PoS, add a product with the 15% tax, set the fiscal position and validate the order * Refund the order in the backend and change the quantity of the line from -1 to 0 and back to -1. > Observation: The price is not the same as before Why the fix: ------------ The fiscal position was not applied when recomputing the line's price and taxes. opw-6253311 Forward-Port-Of: odoo/odoo#274463 Forward-Port-Of: odoo/odoo#270135
Credit notes imported from UBL/XML now keep the line price and tax amounts positive when they should be, instead of being incorrectly flipped negative. This fixes incorrect totals on Belgian credit note imports and prevents undercharging or over-deducting tax amounts.
Original PR description
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as…
Steps to reproduce: 1. Install l10n_be and switch to BE company 2. Upload the XML document (found in ticket chatter) into the Accounting application as a Credit Note. Issue: - The line is imported as a negative value which is corrected with a rounding line. - The 6% tax rate is applied to the negative invoice line, resulting in a negative tax amount being deducted from the total (e.g., 449.32 + (-26.96) = 422.36) instead of being added (449.32 + 26.96 = 476.28) Expected behavior: price_unit, quantity and the related tax amounts should all be positive, matching a normal in_refund/out_refund line. Why this happens: - In `_import_ubl_invoice_line_add_price_unit_quantity_discount`, `BaseQuantity` was multiplied by file_document_sign, unlike `PriceAmount` from the same node which is left untouched. This flips price_quantity to -1, which later flips price_unit to negative when `price_unit = price_subtotal / price_quantity`. opw-6310442 Forward-Port-Of: odoo/odoo#271148
The Registration Desk now refreshes automatically whenever the Registration Summary dialog is closed, no matter how it is dismissed. This keeps attendee status and changes visible immediately, reducing confusion and avoiding manual page reloads.
Original PR description
**Current behavior before PR:** Closing the Registration Summary dialog by pressing **Escape** or clicking outside the dialog does not refresh the Registration Desk view. As a result, the attendee state is not reflected until the view is manually reloaded. **Desired behavior after PR is merged:** The Registration Desk view is refreshed whenever the Registration Summary dialog is closed, regardless of whether it is closed using the **Close** button, by pressing **Escape**, or by clicking outside the dialog. This ensures the attendee information is always updated in both the Kanban and List views. Task - [#6333829](https://www.odoo.com/odoo/project.task/6333829) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272699
This fix stops counted inventory quantities from being cleared to zero when users move between lines without typing a new value. It ensures the quantity is only updated after a manual entry, reducing accidental data loss during physical inventory counts.
Original PR description
Versions -------- - 19.0+ Steps ----- 1. Open Physical Inventory; 2. click on a line; 3. click on a different line. Issue ----- Counted quantity automatically gets set to 0. Cause ----- Commit 3187030 changed the counted quantity widget to enable mutli-line edit. Part of this was done by ignoring the `onInput` hook, and always updating the counted quantity `onBlur`, making it so that the value is set to zero when clicking away, regardless of manual input. Solution -------- Use a `hasInput` state which gets set to `true` on user input. If not `true`, don't update the counted quantity on blur. opw-6365084 Forward-Port-Of: odoo/odoo#274364
This update fixes a flaky automated test in the Discuss full flow by making the scheduled activity dates consistent in every time zone. It matters because it prevents random test failures and helps keep the messaging activity counter working reliably during validation.
Original PR description
The avatar card tour asserts the systray activity counter, which only counts activities whose state is today or overdue. The test scheduled its activities without an explicit deadline, so…
The avatar card tour asserts the systray activity counter, which only counts activities whose state is today or overdue. The test scheduled its activities without an explicit deadline, so activity_schedule fell back to context_today on the class environment, whose superuser has tz Europe/Brussels with demo data. When the test runs between 22:00 and 00:00 UTC, that deadline is tomorrow from a UTC point of view. The state of an activity is however computed in the timezone of its assigned user, and hr_user is created without one, falling back to the server date (UTC). Its activities were therefore planned instead of today, the counter stayed empty and the tour timed out. The admin iteration kept passing because demo data gives admin the same Brussels timezone as the environment that computed the deadline, which is why only half the runs failed (both occurrences at 23:56 and 23:31 UTC). Schedule the activities with a deadline one week in the past instead: an old deadline is overdue in every timezone, whatever timezone the scheduling environment or the assigned user has, making the counter deterministic at any time of the day. https://runbot.odoo.com/odoo/error/941407 Forward-Port-Of: odoo/odoo#274679
This fix allows users to delete an expense even when it has a linked attachment. It removes an error that could block a normal expense-management task and helps keep the workflow smooth.
Original PR description
To reproduce: - Create an expense - Add an attachment - Try to delete the expense --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274710
Scheduled messages can now accept images dropped into the message body and files added with the attachment button. This fixes a bug that prevented users from editing scheduled messages reliably, avoiding errors and making the scheduling workflow work as expected.
Original PR description
Steps to reproduce: 1. Create a log note or send a message on any record and open the full composer. 2. Set a date in the future to schedule the message for later and click schedule. 3. Click on the…
Steps to reproduce: 1. Create a log note or send a message on any record and open the full composer. 2. Set a date in the future to schedule the message for later and click schedule. 3. Click on the "Edit" button of the newly scheduled message. 4. Try dragging and dropping an image into the body, or add a file as an attachment using the button. Issue: - Dragging and dropping an image into the form does nothing. - Trying to add a file as an attachment using the button triggers a traceback: `TypeError: Cannot read properties of undefined (reading 'resId')` Why this happens: - The failure when adding explicit file attachments occurs because the `model` and `res_id` fields were omitted from the `mail.scheduled.message` form view layout in the commit 3b985d2. Without these field declarations, the `mail_composer_attachment_selector` widget cannot determine the record metadata parameters, causing the upload to crash. - The failure of the drag-and-drop mechanism occurs because the scheduled message edit view uses the default `FormController` class instead of `MailComposerFormController` which is used in the `mail_compose_message` view. Consequently, the underlying `useCustomDropzone` is never instantiated on the view, leaving drop events unhandled. Fix: 1. Specify `js_class="mail_composer_form"` to the scheduled message form view definition tag to handle drag and drop, as well as adding the missing fields 2. Assign the `resIds` variable based on the message type since it is defined as `res_id` instead of `res_ids` in `mail.scheduled.message`. opw-6273260 Forward-Port-Of: odoo/odoo#268515
This fix removes an access error that could appear when using the subcontracting flow. It helps users complete the related manufacturing steps without running into permission-related interruptions.
Original PR description
opw-6316136 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 Forward-Port-Of: odoo/odoo#271739
This fix ensures sales order lines show the right delivered quantity when a POS order is later refunded. Previously, refunded quantities were counted twice, which could make delivered quantity drop below zero instead of returning to the correct value.
Original PR description
Step to reproduce: - create a SO with a order line - settle it in pos, notice in SO line, qty_delivered is 1 - refund the pos order - notice, in SO qty_delivered is -1 , not 0 Cause: - After commit [1] , `pos_order_line_ids` now includes order and refund lines - while the `_prepare_qty_delivered` relied on fact that refund lines are not part of `pos_order_line_ids` - due to this, quantity was reduced twice (refund amount are considered twice) [1] https://github.com/odoo/odoo/commit/a12db424a6986a58d1a328fd311078994ac17aee Fix: - in the compute, we now seperate refund and order lines and thus compute works perfectly opw-6290161 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269996
This update resolves an issue where deleting a field in a model caused an access error, even if the model wasn't directly linked to the website. The fix ensures the search for used website forms is performed with elevated permissions, preventing the error.
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 Forward-Port-Of: odoo/odoo#274332 Forward-Port-Of: odoo/odoo#265781
This update fixes an unexpected accrual of holiday days that occurred when carryover balances were applied. Previously, accruals happened inconsistently, leading to confusion. Now, accruals are correctly triggered only at the start, end, or level transition of a period, ensuring accurate holiday balance tracking.
Original PR description
## Issue Currently, an extra accrual happens on carryover date, but it shouldn't. Indeed, accrual only happened at the start - end of a period, or on level transition. ## Reproducing steps Let's take an accrual plan with a level that adds 2 days/month on the 15th of the month. The carryover takes place at the beginning of the year. So we will have this: - 2025-11-01 -> creation of the allowance, 0 days available - 2025-11-15 -> 1 day (only 15 days are counted, so only half of the days are added) - 2025-12-15 -> 3 days (1 full month elapsed) - 2026-01-01 -> 4 days (as the carryover triggers an accrual) -> this event causes confusion as the accrual seems to “come out of nowhere” - 2026-01-15 -> 5 days - 2026-02-15 -> 7 days task-5432188 Forward-Port-Of: odoo/odoo#272349 Forward-Port-Of: odoo/odoo#245201
This update resolves a problem where PDF invoices generated by the Nilvera system were not being correctly saved or displayed in the Odoo system. The previous code incorrectly handled the PDF data, resulting in a base64-encoded string instead of the actual PDF file. This change ensures the correct PDF bytes are stored, allowing for proper preview and download functionality.
Original PR description
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session…
`_l10n_tr_nilvera_add_pdf_to_invoice` writes the response from `client.request('GET', '.../pdf')` directly into `ir.attachment.raw`. The Nilvera client sets `Accept: application/json` on the session and calls `response.json()` by default, so the returned value is a Python `str` holding the base64-encoded PDF body, not raw binary bytes.
The previous code wrote to the base64-aware `datas` field, which auto-decoded its input. An earlier fix switched to `raw` to work around a `binascii.Error` from Python 3.14's stricter base64 validation in the `datas` auto-decode path. That switch silently changed what ends up on disk (`datas` decodes its input, `raw` does not)
Storing that string in the binary `raw` field encodes it as UTF-8, so the file on disk ends up as the literal ASCII of the base64 text. The attachment is served as `application/pdf` but the browser receives base64 ASCII and cannot preview or download the PDF.
Call `b64decode(response)` before storing so the attachment contains the actual PDF bytes.
OPW-6302803
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
Forward-Port-Of: odoo/odoo#274501
Forward-Port-Of: odoo/odoo#270759This update ensures that Nilvera e-invoicing submissions are successful when invoices are created in foreign currencies (like USD). Previously, the system failed to include the necessary Turkish Lira (TRY) exchange rate in the invoice XML, causing rejection by the Nilvera provider. Now, the system automatically calculates and adds this critical exchange rate, ensuring seamless integration with Nilvera.
Original PR description
Description of the issue/feature this PR addresses: This PR resolves an integration failure with the Nilvera e-invoicing provider when a company’s primary currency is configured to a foreign currency…
Description of the issue/feature this PR addresses: This PR resolves an integration failure with the Nilvera e-invoicing provider when a company’s primary currency is configured to a foreign currency (e.g., USD) rather than the local currency (TRY). Nilvera strictly requires a valid exchange rate relative to Turkish Lira (TRY) to be included inside the XML nodes of every posted invoice utilizing a foreign currency. Functions affected: def _add_invoice_exchange_rate_nodes(self, document_node, vals): def _l10n_tr_get_currency_conversion_rate(self, invoice): Current behavior before PR: When generating an invoice where both the company's main currency and the invoice currency are foreign (e.g., USD), the system does not calculate or embed a TRY conversion/exchange rate into the invoice payload. Because this mandatory local currency reference mapping is missing, Nilvera rejects the invoice submission. Desired behavior after PR is merged: For every invoice processed via the Nilvera localization, the system will explicitly calculate and inject the exchange rate between the active invoice currency and TRY into the posted document nodes, regardless of what the underlying company's primary currency is set to. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270531
This update corrects a display issue in the journal entry preview. Previously, omitting currency information resulted in incorrect amounts being shown in both the credit and debit columns. Now, the preview accurately reflects the credit value, ensuring consistent and reliable financial reporting.
Original PR description
In _move_dict_to_preview_vals(), when no currency is provided, the credit column falls back to the line's debit value, so any caller omitting currency_id would show the debit amount in both columns of the journal entry preview. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274594 Forward-Port-Of: odoo/odoo#269335
A bug in the website builder was causing images to fail to load, leading to slow updates. This fix addresses an issue where the system incorrectly handled image sources, resulting in a 404 error. The update ensures images load correctly, improving the website builder's performance and user experience.
Original PR description
Commit [1] introduced a `headResponseCache` for images' src, later used in commit [2], which introduced `getFetchedMimetype`. While the former guards against an empty/undefined src, it is not the…
Commit [1] introduced a `headResponseCache` for images' src, later used in commit [2], which introduced `getFetchedMimetype`. While the former guards against an empty/undefined src, it is not the case of the latter. `headResponseCache.read`, which runs a `fetch`, is called within a try/catch, but it is still awaited: with an undefined src, it returns a 404 after stalling the thread for at least 1s. The bug can be seen from the website builder: - Drop a text/image snippet - Open your dev tools on the "network" tab - Click on the image => a failed fetch (404) appears and blocks the builder from being updated quickly. It happens because the element (in this case the `section` of the snippet) is neither an `img`, nor an element with a parallax, nor an element with a background-image, and `getImageSrc` returns an undefined src. [1]: https://github.com/odoo/odoo/commit/bf377f3d1c58aaeb39624700b3e4754d7a6d384b [2]: https://github.com/odoo/odoo/commit/b96a0769eeecd2e6ec14cc7a73105f8dfdb8842e task-6247171 Forward-Port-Of: odoo/odoo#274641
This update corrects a bug where an attendance record was incorrectly created when an employee took time off. The fix ensures that absences are accurately reflected in attendance data, preventing misleading log notes and ensuring proper tracking of employee time.
Original PR description
# How to reproduce - In the settings, enable Absence Mangement - Create an employee with a Contract - Create a Time off for that employee for yesterday - Manually run the scheduled action…
# How to reproduce - In the settings, enable Absence Mangement - Create an employee with a Contract - Create a Time off for that employee for yesterday - Manually run the scheduled action "Attendance: Detect Absences for employees" - Go to the attendance dashboard for that employee # The issue An attendance with no overtime was created for yesterday for that employee with a log note saying "This attendance was automatically created to cover an unjustified absence on that day." However, the absence was justified as the employee took a time off. # Cause of the issue When running the `_cron_absence_detection` cron job, we create "empty" attendances for the employees that were absent yesterday. If those attendance's `overtime_hours` are 0, then we unlink them : https://github.com/odoo/odoo/blob/a73428187112b3948a11810abae2a3c82c9c7bcd/addons/hr_attendance/models/hr_attendance.py#L659-L666 But, since `check_in` and `check_out` cannot be the same, we cannot really create an empty attedance. We instead create an attendance of 1 second : https://github.com/odoo/odoo/blob/a73428187112b3948a11810abae2a3c82c9c7bcd/addons/hr_attendance/models/hr_attendance.py#L652-L653 This will create an overtime of 0.003 seconds if there was a leave that day (which is our case). This duration will be reflected in the attendance's `overtime_hours`. The issue is that we simply do `== 0` when trying to find the attendances without overtime, so we don't unlink them. The issue was introduced by : https://github.com/odoo/odoo/commit/8d7859a569d9ac7303ca0b9be6c56496be14c544 Because `round(0.003, 3)` => 0 but `round(0.003, 4)` => 0.003 The issue is not present in 18.0+ because we don't create overtime if the duration is `float_is_zero(overtime_duration, 2)` : https://github.com/odoo/odoo/blob/39cce855aa27aa4af9225a61a3e1425383a9f49f/addons/hr_attendance/models/hr_attendance.py#L405 opw-6321883 Forward-Port-Of: odoo/odoo#272089
This update resolves an issue where the expiration confirmation wizard incorrectly displayed 'False, False' instead of the entered lot name for incoming receipts. The fix ensures the system uses the lot information from the receipt when a lot ID hasn't been created yet, providing accurate product and lot name details in expiry warnings.
Original PR description
From saas-18.4, the expiration confirmation wizard can be triggered not only from expired lots, but also from stock move lines whose `removal_date` has passed. For incoming receipts, tracked products…
From saas-18.4, the expiration confirmation wizard can be triggered not only from expired lots, but also from stock move lines whose `removal_date` has passed. For incoming receipts, tracked products use the `lot_name` field when the user is entering the lot. The corresponding `lot_id` is only created later once the receipt is validated. As a result, it is possible for the expiration confirmation wizard to be displayed before the lot exists. In this situation, it attempts to display the product and lot information using `lot_id`, which is still empty, causing the message to show "False, False" instead of the actual lot name entered by the user. It should use the move line information as a fallback when no `lot_id` has been created yet so it still displays the correct product and lot name. Steps to reproduce 1. Enable Product Expiry. 2. Create a storable product with: - Tracking: By Lots - Use Expiration Date: enabled - Removal Time > 0 3. Create a receipt for the product. 4. Open Detailed Operations. 5. Enter a new lot number in the Lot/Serial Number field. 6. Ensure the removal date is in the past and validate the receipt. Related Tickets: opw-6303140 Forward-Port-Of: odoo/odoo#273970 Forward-Port-Of: odoo/odoo#273143
This update corrects a bug where the Peppol demo mode wasn't correctly applied to databases that had previously been neutralized. Previously, the system defaulted to production mode, potentially leading to incorrect document registration on the live Peppol network. This fix ensures the demo mode is set automatically during database neutralization, preventing misconfiguration.
Original PR description
When Peppol is installed on a database that was already neutralized (ex: a staging database where the feature is enabled after the neutralization happened), the account_peppol.edi.mode parameter is not set: data/neutralize.sql only runs at neutralization time, not when the module is installed afterwards. The demo/ data that also sets this parameter is not loaded on databases without demo data (real production/staging databases). As a result, _get_peppol_edi_mode() falls back to 'prod' and the neutralized database registers and sends documents against the live Peppol network. Steps to reproduce: - Neutralize a database on which Peppol is not installed yet - Install the account_peppol module - Open the Peppol settings / registration wizard: the mode is Production instead of Demo Force the demo mode in the pre_init_hook when the database is neutralized, mirroring data/neutralize.sql opw-6307710 Forward-Port-Of: odoo/odoo#273458 Forward-Port-Of: odoo/odoo#273019
International payments processed through Redsys were failing due to incorrect formatting of state codes in the payment process. This update dynamically builds the EMV3DS payload, only including the state code if one is available, allowing these payments to now process successfully. This resolves a critical issue impacting international sales.
Original PR description
### Description of the issue/feature this PR addresses: International production payments processed via Redsys are failing with error code 9754 (SIS0754). This rejection occurs because the EMV3DS (3D…
### Description of the issue/feature this PR addresses: International production payments processed via Redsys are failing with error code 9754 (SIS0754). This rejection occurs because the EMV3DS (3D Secure 2.0) payload is sending the billAddrState field with an invalid ISO code format for non-Spanish customers or customers without a state configured. ### Current behavior before PR: The _redsys_prepare_merchant_parameters method hardcodes the billAddrState key into the DS_MERCHANT_EMV3DS dictionary payload. If self.partner_state_id.code is missing or empty, Odoo sends an empty/falsy value. Because Redsys enforces strict EMV3DS format validation, it rejects the entire transaction for having an invalid state format rather than simply ignoring the empty value. ### Desired behavior after PR is merged: The DS_MERCHANT_EMV3DS dictionary is now constructed dynamically. The billAddrState key is only appended to the payload if a valid state code actually exists for the partner. RedSys allows this field to be optional, so omitting the key entirely when unavailable causes Redsys to skip the validation for that specific field, allowing certain international payments to process successfully. opw-6237764 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#270899