Monday, September 21, 2026
25 changes · saas-19.1
Resolved issues and error corrections
This fixes a crash that could occur when opening image attachments linked to products or other records. Users can now view these attachments without hitting a client error, improving reliability in the attachment workflow.
Original PR description
### Steps to Reproduce: 1. Go to Sales > Products and select a product 2. Upload an image for the product 3. Go to Settings > Technical > Attachments 4. Click on one of the image attachments 5.…
### Steps to Reproduce: 1. Go to Sales > Products and select a product 2. Upload an image for the product 3. Go to Settings > Technical > Attachments 4. Click on one of the image attachments 5. Observe the Client Error: "Uncaught Promise > Invalid props for component 'Many2One': 'domain' is not a function" ### Issue: In `attachment_patch.js`, the domain for the `Many2OneReferenceField` is overridden when an attachment is linked to another attachment. The patch calculates the domain and incorrectly assigns a static Array (using `.toList()`) to `props.domain`. In Odoo 19, the `Many2One` component enforces strict prop validation (caught when Owl's debug mode is enabled during testing), causing a crash because it expects the domain prop to be a function. ### Solution: We can just wrap the resulting domain Array in an arrow function so that it evaluates dynamically when the `Many2One` component mounts. Also added a unit test to explicitly cover this patched scenario. The test manually applies a local patch to `Many2OneReferenceField.prototype` to accurately replicate the environment and verify the fix. opw-6563954 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287951
The Barcode app now includes selected inventory location details when updating stock data, even if that location was not preloaded. This prevents errors when users add products to locations outside the standard stock hierarchy, making inventory workflows more reliable.
Original PR description
### Steps to Reproduce: 1. Duplicate an Inventory Location so that you have one that is NOT a sublocation of any other location 2. Go to the Barcode module and click "Add Product" 3. Choose a product…
### Steps to Reproduce: 1. Duplicate an Inventory Location so that you have one that is NOT a sublocation of any other location 2. Go to the Barcode module and click "Add Product" 3. Choose a product 4. For the location, select the new location 5. Click Confirm and observe the Traceback ### Issue: In the Barcode module, selecting a location that falls outside the stock locations hierarchy causes a traceback. The initial frontend cache is designed to only preload sublocations of the primary stock location. When a user selects an un-cached location, the subsequent quant data fetch assumes the location is already cached and omits it from the payload, which causes an `UncaughtPromiseError` when the UI attempts to rebuild. ### Solution: We can update `get_stock_barcode_data_records` in `stock.quant` to explicitly include the `stock.location` data in the returned payload. This mirrors how products and lots are handled, ensuring the frontend always receives the necessary location records even if they were excluded from the initial startup cache. opw-6469721 Forward-Port-Of: odoo/enterprise#128147
This fixes an error that could appear when users completed the website setup flow for an online store. The configurator now handles the website address correctly during installation, allowing setup to finish without an unexpected traceback.
Original PR description
Since [1], the registry is loaded without a request context, so a module installation now goes through the fallback of `get_current_website` that reads the url stored on the current thread. That…
Since [1], the registry is loaded without a request context, so a module installation now goes through the fallback of `get_current_website` that reads the url stored on the current thread. That value is a full url, not the `domain:port` the method expects, so IDNA-encoding it splits it on the dots of the path rather than on those of a host name and raises `UnicodeError` as soon as one of the resulting parts reaches 64 characters. The thread url is now reduced to its `domain:port` part. Steps to reproduce: - Start the server locally on a database with demo data, served on `http://localhost:8069`, with only `Website` installed - Create a new website - In the configurator, choose `I want an online store` - Go through each step - After the last step, a traceback appears while loading the accounting demo data: the configurator should apply without error [1]: https://github.com/odoo/odoo/commit/1ae80f9fb18c9c9e67fe0367b94f0d882c711570 Also reported here: https://github.com/odoo/odoo/issues/286432 Forward-Port-Of: odoo/odoo#288931
New US companies using AvaTax will now receive the correct tax accounts when their accounting setup is created without demo data. This prevents invoices from generating AvaTax tax lines without an assigned account, helping ensure smoother tax calculation and accounting entries.
Original PR description
Steps to reproduce: - Create a DB and Install `Accounting` without demo data - In Companies > Create New Company with `US` Localization - Switch to US Company > Add `Avatax` Credentials - Create a…
Steps to reproduce: - Create a DB and Install `Accounting` without demo data - In Companies > Create New Company with `US` Localization - Switch to US Company > Add `Avatax` Credentials - Create a Product with `Avatax Category` - Create a Invoice > In Other Info `Fiscal Position` - `Automatic Tax Mapping (AvaTax)` > `Compute Taxes` - In Taxes check newly created tax does not have a account ID Issue: AvaTax fiscal position created from chart templates have empty `avatax_invoice_account_id` and `avatax_refund_account_id` fields on a fresh database without demo data. Problem: Both fields use [defaults] based on `company.account_sale_tax_id`. During initial CoA loading, `_load_data()` creates the AvaTax fiscal position before `_post_load_data()` sets `company.account_sale_tax_id`. The defaults therefore resolve to an empty recordset and no tax account is assigned. This issue does not occur in databases with demo data because the demo company is already loaded, so `company.account_sale_tax_id` is already set when the AvaTax fiscal position is created. Solution: Set the invoice and refund accounts explicitly in the AvaTax fiscal position template to ensure they are correctly assigned during CoA loading. [defaults]: https://github.com/odoo/enterprise/blob/de17c107651394025e9a3d5cbed8d81bcfc2e76d/account_avatax/models/account_fiscal_position.py#L9-L13 opw-6347128 Forward-Port-Of: odoo/enterprise#132005 Forward-Port-Of: odoo/enterprise#126604
Project dashboards now show only the portion of an expense assigned to each project instead of the full expense on every related project. This prevents overstated project costs when one expense is shared by percentage across multiple projects, improving profitability reporting accuracy.
Original PR description
## Steps to reproduce: - Install Project and Expenses modules - Create an expense for 100 euros - Confirm the expense and post its journal entries - Set analytic distribution for two different projects each for 50% - Notice the project dashboard for both projects is stating 100 euros ## Cause: When fetching the expenses profitability items we set the amount as the whole amount billed for the expense. ## Fix: We fetch the analytic distribution and multiply the amount by the percentage allocated for the project. opw-6457156 Forward-Port-Of: odoo/odoo#289023 Forward-Port-Of: odoo/odoo#287653
Users can now click amounts in aged receivable or payable reports when using an "Open On" date filter without hitting an error. This restores access to the underlying journal item details from the report, helping finance teams investigate balances smoothly.
Original PR description
Currently, an error occurs when a user clicks on a cell in the aged partner balance report. **Steps to Reproduce:** - Install the `account_reports` module. - Go to `Invoicing` > `Reporting` >…
Currently, an error occurs when a user clicks on a cell in the aged partner balance report. **Steps to Reproduce:** - Install the `account_reports` module. - Go to `Invoicing` > `Reporting` > `Partner Reports` > `Aged Receivable`. - Click on the `date filter`, select `Open On`, and set `any date`. - Click on any `amount` in the `Total Aged Receivable line`. `TypeError: the JSON object must be str, bytes or bytearray, not dict` After the [recent commit] that adds the context to the action, when preparing the action to open the journal items corresponding to the selected cell in the aged partner balance report, the context is added to the action [1]. When an "Open On" date is set, the code attempts to convert the context using json.loads() before adding the search_default_open_on key to it [2]. but, the context is already a dictionary, which raises the error [3]. This commit ensures that, since the action context is already a dictionary, the search_default_open_on key and its value are added directly to the context. [recent commit]: https://github.com/odoo/enterprise/commit/9678a1b987a5aa87922b72d972dd7f73a689afee [1]- https://github.com/odoo/enterprise/blob/8fceecea541564a9877babfc31b7e0bf896573a6/account_reports/models/account_aged_partner_balance.py#L357-L359 [2]- https://github.com/odoo/enterprise/blob/8fceecea541564a9877babfc31b7e0bf896573a6/account_reports/models/account_aged_partner_balance.py#L362 [3]- https://github.com/odoo/enterprise/blob/8fceecea541564a9877babfc31b7e0bf896573a6/account_reports/models/account_aged_partner_balance.py#L361 sentry-7685491449
The live chat widget now avoids offering “Ask a Human” when no human agent is actually available in the selected channel. It also turns the related notification into a chat message, making the experience clearer and preventing important information from being hidden by the chat window.
Original PR description
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created…
This commit aims to adapt the tests for this bugfix. How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. enterprise PR: https://github.com/odoo/enterprise/pull/125673 task: 6148037 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#278834
The online shop now blocks combo products from being added to the cart unless every required choice is completed. This prevents customers from checking out with incomplete combo selections, even if browser controls are bypassed.
Original PR description
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to…
Steps to reproduce: =================== 1. Publish a combo product whose choices each offer more than one item, so the combo configurator is shown. 2. On the shop, open the product and click "Add to Cart". 3. Select an item for one combo choice only. The dialog's "Add to cart" button stays disabled. 4. Remove the `disabled` attribute from that button with the browser developer tools and click it. => The combo is added to the cart with one of its choices unanswered, and the order can be paid in that state. Root cause: =========== The incomplete selection is only prevented on the client side, by disabling the button until every choice has been answered. Server side, `/website_sale/combo_configurator/update_cart` only rejects a completely empty selection, never that the selected items cover every choice. Fix: ==== Reject the request unless the selected combo items cover exactly the combo choices of the product. Comparing the set of choices rather than counting the items also rejects a selection answering the same choice twice while leaving another one unanswered. opw-6478837 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286989 Forward-Port-Of: odoo/odoo#283465
The live chat now hides the “Ask a Human” option when the selected channel has no available human agents, preventing visitors from choosing a handoff that cannot be fulfilled. If a handoff cannot proceed, the message is shown directly in the chat instead of as a page notification, so visitors can still see the response even when the chat window covers notifications.
Original PR description
How to reproduce: - Create livechat channel with 0 members. - In Rules, add an agent to the channel. - In website configuration, set the created livechat channel as the default live chat channel. - Go to website, click bottom right bubble to start livechat. - Resize the Window on your screen so that the chat window obscures the top right of the page (where you get notifications) - Press 'Ask a Human' Current behavior: - 'Ask a Human' is available (despite having 0 agants in the channel -> will never reach a human) - Notification is obscured by the chat window (when it overlaps with the top right) Expected: - Remove 'Ask a Human' button, when there are no human (agents) in the livechat channel - Change the notification to a chat message. task: 6148037 community PR: https://github.com/odoo/odoo/pull/278834 Forward-Port-Of: odoo/enterprise#125673
Products with multiple variants are now only marked as sold out when every variant is out of stock. This keeps the Add to Cart option visible when at least one variant can still be purchased, preventing missed sales and customer confusion.
Original PR description
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the…
Steps to reproduce: =================== 1. Create a storable product with two variants and untick "Continue selling when out-of-stock". 2. Leave the first variant at 0 in stock, put some stock on the second one. 3. try the "Add to Cart" option of the shop page. => The product has no add to cart button, as if it were sold out, while its second variant can be bought. Reordering the variants so that the one in stock comes first brings the button back. Root cause: =========== `product.template._is_sold_out()` delegates to `product_variant_id`, which is `product_variant_ids[:1]`, so a whole template is declared sold out on the sole basis of its first variant. `_website_show_quick_add()` then hides the button for the template, whatever the stock of the other variants. The helper has looked at that single variant since it was written: - [1] added it to hide the button of sold out products. Fix: ==== Consider the template sold out only when all of its variants are. The check stops at the first variant in stock, so a product that can be bought still costs a single stock lookup. [1]: https://github.com/odoo/odoo/commit/4ae197cc770cec2058e1f113209c4f93a4a0f4b2 opw-6520052 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287882 Forward-Port-Of: odoo/odoo#286781
Employees without HR access can now see one-off work locations in the calendar, matching the behavior for recurring work locations. This prevents incomplete availability information when teams view colleagues' schedules.
Original PR description
**Steps to reproduce** - With a user having HR rights, create an exceptional work location for a user (click on the top bar of one of the days in the calendar, where work locations are displayed, and do not check "repeat every". - Open the calendar app with a user having no HR rights, in the sidebar, add the user with a non-recurrent work location. Notice that the work location is not visible, unlike recurring ones. **Cause** Recurring work locations are defined on the public employee (`*_location_id` type fields) and are readable by all users. Non-recurring work locations are `hr.employee.location` records and the `homeworking_own_rule` rule restricts read operations for non-HR users. opw-6190464 Forward-Port-Of: odoo/odoo#266380
Fixed an issue that could block users from opening the rental order lines schedule after all product variants were deleted. The schedule now opens safely by avoiding a missing default product reference, reducing interruptions for rental teams.
Original PR description
Currently, an error occurs when opening the Rental Order Lines Schedule view. **Steps to Reproduce:** - Install the `sale_stock_renting` module. - Go to `Settings` and enable `Variants`. - Go to…
Currently, an error occurs when opening the Rental Order Lines Schedule view. **Steps to Reproduce:** - Install the `sale_stock_renting` module. - Go to `Settings` and enable `Variants`. - Go to `Rental` > `Products` and create a product. - In the `Attributes & Variants` tab, add an attribute with two values and save. - Delete all variants using the `Variants` smart button or from `Inventory` > `Products` > `Product Variants`. - Return to the `Product` and click the `In Renting smart button`. `IndexError: list index out of range` When a product template is created, a product variant is automatically generated if the product has no attributes. Deleting this variant also deletes the product template [1]. By explicitly creating attribute values, a dynamic product variant is generated. Deleting this variant does not delete the product template because of the dynamic attribute [2]. When the In Renting button is clicked, it it going to open the rental order lines Schedule view and adds default_product_id to the action context. Since no product variants exist anymore, accessing the default product variant raises an error [3]. This commit ensures that default_product_id is not added to the context when the product template has no product variants [1]: https://github.com/odoo/odoo/blob/1f70b81eeebcc62b64c18772915e2f4696e189e7/addons/product/models/product_product.py#L486 [2]: https://github.com/odoo/odoo/blob/1f70b81eeebcc62b64c18772915e2f4696e189e7/addons/product/models/product_product.py#L478-L480 [3]- https://github.com/odoo/enterprise/blob/98ec35f3f17a2b706ad4fcc4d6161d12d05eadfd/sale_renting/models/product_product.py#L69 [4]: https://github.com/odoo/odoo/blob/942d3892c33e7ccdcfec260c3da37095069b699e/addons/stock/models/product.py#L635-L643 [5]: https://github.com/odoo/odoo/blob/942d3892c33e7ccdcfec260c3da37095069b699e/addons/stock/models/product.py#L601-L612 sentry-7637715724 Forward-Port-Of: odoo/enterprise#126356
When the France PDP integration receives a status it does not recognize, it now records the actual status code instead of showing a blank or unclear value. This makes support and troubleshooting easier when unexpected invoice lifecycle statuses are received.
Original PR description
Currently we just log `None` in case we receive a lifecycle with an unsupported (on community side) status. After this commit we log the status code at least. task-None before fix <img width="711" height="120" alt="image" src="https://github.com/user-attachments/assets/1f939cf4-17b7-49cb-8b31-5cc594fdeadd" /> after fix <img width="700" height="116" alt="image" src="https://github.com/user-attachments/assets/1b8b3853-f2da-4426-9c49-1d135d1c7824" /> Forward-Port-Of: odoo/odoo#286429
Fixes a purchase order issue that could cause an error when editing only the unit price on a confirmed order after migration. The change ensures the related purchase notification templates are properly updated, reducing disruption for purchasing teams.
Original PR description
In this commit: eadf127 the `track_po_line_template` template was added in a `noupdate` file. The commit specifies: “put their declaration in no update when not done if template has no technical code or complex dependency on underlying code;” However, this is not actually the case for the two templates in this file. This did not cause any error in v17, but errors started appearing later because of #254602, which modified this code and the related Python code, leading to an error when no product quantity is changed. Steps to reproduce in a 19.3 database migrated from v19: - Create a purchase order with one product line and confirm it. - Only change the unit price of that line. - Error: "Error rendering template: ..." While this can also be considered a migration issue, I think the simpler and more logical solution is to remove the noupdate from this file. opw-6518729 Forward-Port-Of: odoo/odoo#288463
This fixes an issue where child contacts could show an incorrect EU VAT validation result when processed before their parent company. Parent company VAT checks are now completed first, so related child contacts inherit the correct validation status and business records stay accurate.
Original PR description
**Description of the issue/feature this PR addresses:** When a batch recompute of `vies_valid` contains both a parent (commercial) partner and a child sharing the same VAT, and the child happens to…
**Description of the issue/feature this PR addresses:**
When a batch recompute of `vies_valid` contains both a parent (commercial) partner and a child sharing the same VAT, and the child happens to be iterated before its parent, the child ends up with `vies_valid=False` instead of the parent's real, freshly-checked value.
**Current behavior before PR:**
`_compute_vies_valid` loops over `self` in whatever order the batch happens to have:
```python
for partner in self:
...
if partner.parent_id and partner.parent_id.vies_vat_to_check == partner.vies_vat_to_check:
partner.vies_valid = partner.parent_id.vies_valid
continue
status = partner._check_vies_iap()
partner._update_vies_status(status)
```
`Field.compute_value()` removes the whole batch from the "to compute" queue *before* running this loop (it does so upfront, in case the method does not assign every record). So when the loop reaches a child and reads `partner.parent_id.vies_valid` to reuse it, that field is no longer marked "to compute" for the parent, and reading it just returns whatever is currently cached/stored — which, if the parent has not been processed yet in this same loop, is still the old/default value. The child copies that stale value and, being a stored field, keeps it forever: nothing re-triggers its computation afterwards.
Example:
```python
parent = env['res.partner'].create({'name': 'Parent Co'})
child = env['res.partner'].create({'name': 'Child Address', 'parent_id': parent.id})
child.vat = 'BE0477472701' # queues the child's compute first
parent.vat = 'BE0477472701' # queues the parent's compute second
child.vies_valid # False, even though the VAT is valid
parent.vies_valid # True
```
**Desired behavior after PR is merged:**
Every partner without a parent is always computed before any child that may reuse its value, regardless of the batch's original order — so the child correctly ends up with the parent's real `vies_valid`.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#287351
Forward-Port-Of: odoo/odoo#279927Invoice imports now handle small rounding differences when matching fixed taxes, especially on lines with quantities above one. This helps prevent valid invoices from failing or creating incorrect tax matches because of tiny calculation differences.
Original PR description
When importing an invoice with fixed taxes, if the line has a quantity of more than 1, sometimes, due to rounding errors, searching for the fixed tax fails to find it, as the values needed to exactly match, a new search method was added to give a margin of +/- 0.01 when searching for valid taxes. task-id-6307992 Forward-Port-Of: odoo/odoo#287948 Forward-Port-Of: odoo/odoo#271842
This fixes a manufacturing test that could fail depending on the server timezone. The change makes automated checks more stable without changing user-facing manufacturing behavior.
Original PR description
**PROBLEM**
In `test_generate_serial_button_sequence()` we generate a serial number based on the day of the year. In ir_sequence, we use the time based on the environment timezone, but in the test, we don't use any timezone. This can lead the assertion to fail, since the day of the year can differ with the timezone used.
**REPRO STEPS**
1. edit the freeze_time in the test to `freeze_time('2024-01-15T23:00:00')`
If your timezone is UTC+2, then the time according to your timezone will be `2024-01-16T01:00:00`
So, without in UTC+0, it's the 15th day of the year, but in UTC+2 it's already the 16th.
Remove the timezone in the last assertIn() (ie, remove the fix).
(if your timezone is different, adjust the freeze_time accordingly)
2. run the test and see it fails.
runbot-237780
Forward-Port-Of: odoo/odoo#285647Product option pills in the sales configurator now keep consistent spacing when they wrap onto multiple lines. This prevents crowded rows and makes product choices easier to read and select.
Original PR description
Pill-style attribute values used Bootstrap's list-inline/list-inline-item, which only sets margin-right between items. When pills wrapped onto a new line, the rows touched with no vertical gap. Fix: Switch the pill list to a flex container with gap-2, matching the spacing website_sale already uses for its own attribute-value lists, so wrapping rows get the same gap as pills on the same row. opw-6584507 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#289176
The Timesheet Grid now only marks public holidays that belong to the user's current company. This prevents holidays from other companies from incorrectly greying out working days, helping teams record time accurately in multi-company setups.
Original PR description
Issue: - Public holidays from other companies were incorrectly included in the Timesheet Grid view of the current company. As a result, days corresponding to those holidays appeared greyed out.…
Issue: - Public holidays from other companies were incorrectly included in the Timesheet Grid view of the current company. As a result, days corresponding to those holidays appeared greyed out. Cause: - The `grid_unavailability` method relies on the `_get_valid_work_intervals` function to fetch all unavailability data at once. When fetching data for multiple employees, this function also retrieves public holidays from all companies. Fix: - The method no longer uses the data returned by `_get_valid_work_intervals` for company unavailable days. Instead, it now always makes a separate, direct call via the `get_company_unavailable_dates()` function. This ensures that only holidays relevant to the current company are considered in the grid view. The company is also passed in the domain of `_work_intervals_batch`, which otherwise returns the leaves of every company when no resource is given. Steps to reproduce: - 1. Create Company A and Company B. 2. In Company B, create a public holiday on Tuesday. 3. Switch back to Company A. 4. Open the All Timesheets Grid view from Company A. Expected behavior: - - The grid column for Tuesday should not be grey for Company A users. Current behavior: - - The grid column for Tuesday is grey, incorrectly showing it as a time-off day. task:4492966 Forward-Port-Of: odoo/enterprise#132030 Forward-Port-Of: odoo/enterprise#88495
Fixed an appointment scheduling issue where staff-created bookings could overbook a shared resource and later block customers from completing website bookings. The change keeps resource capacity calculations valid, helping appointment availability stay reliable across backend and online booking flows.
Original PR description
**Steps to reproduce:** - Install Appointment app - Create an appointment type based on resources, auto-assigned, with two shareable resources linked together - Set the first resource's capacity to 3…
**Steps to reproduce:**
- Install Appointment app
- Create an appointment type based on resources, auto-assigned, with two shareable resources linked together
- Set the first resource's capacity to 3 and the second resource's capacity to 4
- From the backend Gantt view, create a booking for 2 people on the first resource
- Create a second booking for 2 people on the same resource, at the same date and time
- First resource is now overbooked with reserved capacity of 4 out of 3
- Try to create an appointment from the website
- Error: "The capacity reserved should be positive."
**Issue:**
When bookings are created from the backend gantt view, the selected resource can be overbooked even if another linked resource still has available capacity.
Then when trying to book an appointment the new booking lines will trigger this constraint:
```py
_check_capacity_reserved = models.Constraint(
'CHECK(capacity_reserved >= 0)',
"The capacity reserved should be positive.",
)
```
This is caused by the negative values in:
```py
booking_line_values = []
if appointment_type.schedule_based_on == 'resources':
capacity_to_assign = asked_capacity
for resource in resources:
resource_remaining_capacity = resources_remaining_capacity.get(resource)
new_capacity_reserved = min(resource_remaining_capacity, capacity_to_assign, resource.capacity)
capacity_to_assign -= new_capacity_reserved
booking_line_values.append({
'appointment_resource_id': resource.id,
'capacity_reserved': new_capacity_reserved,
'capacity_used': new_capacity_reserved if resource.shareable and appointment_type.resource_manage_capacity else resource.capacity,
})
```
**Fix:**
Avoid negative remaining value in resource booking when computing available slots.
Note: Tried to take the capacity already used by overlapping bookings into account when assigning resource booking lines from the backend gantt view. And also force linked_resources booking when trying to book more
than the total capacity to properly dispatch as many slots as possible. But it was breaking `appointment_google_reserve` tests.
opw-6503147
Forward-Port-Of: odoo/enterprise#131752
Forward-Port-Of: odoo/enterprise#130520Fixes a mobile checkout issue where the cart summary could cover address fields when the Android keyboard was open. This makes it easier for customers to complete checkout details on mobile devices and reduces friction during purchase.
Original PR description
The mobile summary element hides the address form on Android Steps to reproduce: 1. Install eCommerce 2. On android (or using browserstack) and without being logged in, go to the eCommerce 3. Add any…
The mobile summary element hides the address form on Android Steps to reproduce: 1. Install eCommerce 2. On android (or using browserstack) and without being logged in, go to the eCommerce 3. Add any product to the cart and go to checkout 4. Click on "Checkout" to enter the address details 5. Open the keyboard by clicking inside the first input 6. The virtual keyboard opens, enter a name and click on "Next" in the virtual keyboard 7. Do this a couple of times: the cart summary element doesn't scroll and the input field is hidden behind it Similar problem happens when you scroll a bit after clicking in the first input: the cart summary element displayed at the bottom is still visible (on top of the virtual keyboard) which hides part of the page and makes it difficult to navigate the page and fill in the details Issue: `sticky-bottom` keeps the element at the bottom of the screen Solution: Remove class `sticky-bottom` from `o_mobile_summary` when we click in one of the input opw-6500503 Forward-Port-Of: odoo/odoo#287464
Confirmed sales orders now count toward a customer's credit limit even when products have not yet been delivered. This helps prevent additional orders from being accepted without warning when the customer has already committed beyond their approved credit amount.
Original PR description
### Steps to reproduce Set a credit limit of 1.000 on a customer, then: 1. Create a Sale Order for a product with Invoice Policy on **Delivered Quantities**, over that amount → the warning shows.…
### Steps to reproduce
Set a credit limit of 1.000 on a customer, then:
1. Create a Sale Order for a product with Invoice Policy on **Delivered Quantities**, over that amount → the warning shows. Good.
2. Confirm it, and deliver nothing.
3. Create a second order for the same customer → the warning never shows, even though the customer is already over the limit.
### Solution
The ordered quantity is what the customer committed to, delivered or not. Using `qty_to_invoice` instead of `uom_qty_to_consider` or `qty_delivered`
Introducing invoice_status in sales domain for `_compute_credit_to_invoice`: `untaxed_amount_to_invoice` still follows the invoicing policy, while `amount_to_invoice` no longer does. On a confirmed order for a delivery-based product with nothing delivered:
```
line.untaxed_amount_to_invoice = 0 # still gated by qty_delivered
line.invoice_status = 'no'
order.amount_to_invoice = 2000 # fixed by this PR
```
The domain filters on the first one, so the order is dropped from the search before its amount is ever read and `credit_to_invoice` stays at 0.`'no'` is the stored marker for a confirmed line that is not invoiceable yet, which is exactly what the first clause misses.
ticket: [6480211](https://www.odoo.com/odoo/project/967/tasks/6480211)
Forward-Port-Of: odoo/odoo#288659
Forward-Port-Of: odoo/odoo#285578Creating an onsite event from the Onsite action now uses the current user's employee record when no employee is explicitly provided. This prevents errors and ensures the new event is visible in the expected kanban view.
Original PR description
The Onsite action only sets the hr_skills_event_add_employee key in its context, without any employee. Reading default_employee_id directly then raised a KeyError. Even before that, no attendee was added, so the new event did not match the domain of the action and stayed hidden in the kanban view. We now fall back on the employee of the current user. taskid-6361432 Forward-Port-Of: odoo/odoo#288241
The Time Off Ledger now shows each employee's expected hours based on their actual weekday schedule instead of using a weekly average. This makes reports more accurate for employees with shorter or longer scheduled days, such as reduced Friday hours.
Original PR description
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to…
The Time Off Ledger report shows the same "Expected Hours" value for every working day of an employee, even when their working schedule assigns different hours to different weekdays. ### Steps to reproduce: 1) Install hr_holidays_attendance. 2) Create/assign a working schedule where daily hours vary across the week (e.g. Monday-Thursday 8.5h, Friday 5h). 3) Go to Attendance > Reporting > Time Off Ledger, filter on that employee. ### Observed behavior: "Expected Hours" is identical on every day (the schedule's average hours/day), regardless of which weekday the row is for. ### Expected behavior: "Expected Hours" should match the hours actually scheduled for that specific weekday, so a shorter Friday shows less than a full Monday. ### Root cause: `Time off Ledger` is a SQL view. Its `_select()` builds `expected_hours` from `rc.hours_per_day` at [1], i.e. `resource.calendar.hours_per_day`, a field explicitly labelled **"Average Hour per Day"** and is computed via [_get_hours_per_day] as `hours_per_week / days_per_week`, a single flat value for the whole calendar. The per-weekday hours are available in `resource.calendar.attendance` it stores `dayofweek`, `hour_from`/`hour_to` and a computed `duration_hours` per line, and can legitimately differ per weekday (e.g. Monday 8h vs Friday 4h) at [2]. The report's own [_cte_cal_workday()] CTE already reads this table to decide whether a weekday is a working day (`cal_workday`, joined as `cw` in `_from()`), but discarded the actual hours, keeping only `(calendar_id, dayofweek)`. `_select()` then fell back to the calendar-wide average via `rc.hours_per_day` instead of the per-day value. Example: calendar with Monday 8h and Tuesday 4h -> `hours_per_day` averages to 6h; the report showed 6h on both Monday and Tuesday instead of 8h and 4h respectively. [1]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L256 [_get_hours_per_day]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar.py#L703-L707 [2]- https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/resource/models/resource_calendar_attendance.py#L15-L31 [_cte_cal_workday()]: https://github.com/odoo/odoo/blob/5568f6e472e2e53bc2931e744421015b0f0f3550/addons/hr_holidays_attendance/report/hr_leave_attendance_report.py#L82-L89 ### Fix: `_cte_cal_workday()` now aggregates `SUM(duration_hours)` per `(calendar_id, dayofweek)` from `resource_calendar_attendance`, instead of just selecting the distinct keys. `_select()` reads this per-weekday value (`cw.hours_per_day`) instead of the calendar-wide `rc.hours_per_day` for both `expected_hours` and `difference_hours`. **opw-6425167** Forward-Port-Of: odoo/odoo#280664
The HTML editor now refreshes the font size shown in the toolbar after users remove formatting from selected text. This avoids misleading toolbar values and prevents extra hidden formatting from being added when changing font sizes, making editing content more predictable.
Original PR description
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar.…
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar. ### Description of the issue/feature this PR addresses: - The font-size class was removed from the block element, but the font-size input still displayed the value associated with the removed class. - The toolbar did not show the correct font size for default block classes (like `o_default_font_size`). - Applying a new font size inside default block classes created nested spans instead of splitting them. - The "Remove format" button remained enabled even when selection had no custom formatting (only default block classes). ### Desired behavior after PR is merged: - The font-size input is updated after removing the font-size class and correctly displays the font size of the resulting block element. - Update the `getFontSizeDisplayValue` utility to find correct CSS variable dynamically to also find the font size of default block classes. - Add default font size classes (like `o_default_font_size` and headings) to `format_class_predicates` resource in `font_plugin.js`. This allows them to be split and replaced instead of creating nested spans. task-6321166 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272023