Monday, September 7, 2026
22 changes · saas-18.4
Security fixes and vulnerability patches
Point of Sale self-invoicing now prevents unauthorized changes to existing customer records and checks that required invoicing details are present before creating invoices. This protects customer data while still allowing legitimate users to complete invoices for themselves or related contacts.
Original PR description
Before this commit: ------------------- - During self-invoicing, a public user could create a new customer or update the current order's customer data by submitting the self-invoicing form, without any access rights validation. - For logged-in users (portal or internal), invoice generation could proceed even when the user or the selected customer lacked the required invoicing information. After this commit: ------------------- - During self-invoicing, a public user can create a new customer for the order, but cannot modify the existing customer linked to the order. - For logged-in users (portal or internal), required customer information is validated before generating an invoice. Customer data can only be updated when the customer is the logged-in user's partner or a child contact of that partner. Task-6272660 Forward-Port-Of: odoo/odoo#283470 Forward-Port-Of: odoo/odoo#270112
Enhancements to existing features
Swiss QR invoices can now be created even when a company or customer address is missing a street or building number, reducing avoidable interruptions during invoicing. Users are informed through the Send & Print popup when customer address details are incomplete, and batch processing continues for valid invoices instead of being fully blocked by one error.
Original PR description
In Switzerland, the street and building number is not required to generate a valid QR invoice. To minimize friction, errors are no longer raised if they are missing from the address of the company or the customer during the creation of the QR Code. Instead, an info banner is now displayed in the Send & Print popup indicating to the user that the addresses of some of the customers are missing and allow the user to browse through those customers. task-4349496 opw-4317153 Forward-Port-Of: odoo/odoo#271534
Resolved issues and error corrections
The GST token refresh process now correctly extends the existing token validity without replacing the stored token with an unrelated response value. This prevents incorrect token data from being saved and helps keep Indian GST reporting access working reliably.
Original PR description
Previously, `_cron_refresh_gst_token` updated the value of `l10n_in_gstr_gst_token` when refreshing the GST token using `response.get('txn')`.
However, the response received during a token refresh is: `{'status_cd': '1', 'status_desc': 'If previous Auth Token is found'}`
The GST token itself remains unchanged during a refresh; only its validity is extended. Therefore, writing `l10n_in_gstr_gst_token` with `response.get('txn')` is unnecessary and incorrect.
This commit removes that write operation.
Forward-Port-Of: odoo/enterprise#130546
Forward-Port-Of: odoo/enterprise#130313The accounting logo now appears correctly in tax return activities. This makes the activity view clearer and more consistent for users working with accounting tasks.
Original PR description
Before PR: - Accounting logo was not visible in Tax return activities. After PR: - Accounting logo is visible in Tax return activities. task-6463584
The Send to eTransport button is now shown when a stock transfer is ready as well as when it is completed. This helps Romanian eTransport users submit required transport information at the right time without waiting for final completion.
Original PR description
Currently, the Send to eTransport button on `stock.picking` is only visible when picking is done. This PR fixes this behaviour and makes it visible when picking is ready or done both. task-5930984 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#257321
Peruvian electronic invoices now include cash rounding in the final amount due sent to SUNAT. This prevents mismatches where the invoice showed a rounding adjustment but the payable total still used the pre-rounding amount.
Original PR description
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in…
Steps to reproduce ------------------ 1. On a peruvian company, set a cash rounding method on an invoice 2. Post the invoice and generate the SUNAT UBL 2.1 XML -> the rounding is in `PayableRoundingAmount` but `PayableAmount` still has the amount before rounding! Why it's happening ------------------ The generic code computes `PayableAmount` from `amount_residual`, and Peru overrides it to be the total tax included of the XML minus the prepaid amounts, because the residual can not be used there. Then odoo/odoo@b847552872ad changed the meaning of the totals. The cash rounding line is not part of the base lines anymore, its amount is kept aside in `cash_rounding_base_amount_currency` and the totals do not contain it anymore. The generic code stays correct because `amount_residual` already has the rounding inside but the Peru total using `tax_inclusive_amount_currency` is now the amount before rounding and this is what ends up in the `PayableAmount`! The fix ------- Add the cash rounding amount when computing the `PayableAmount`. opw-6509677
The emoji picker in Discuss now remains stable when users select emojis during a search and then clear the search text. This prevents an unexpected error from interrupting chat usage and keeps the picker behavior consistent.
Original PR description
Steps to reproduce: - open Discuss, open any chat, open the emoji picker (no 'Frequently used' emojis) - search a term and select emojis without closing the picker (shift+click on desktop, plain…
Steps to reproduce:
- open Discuss, open any chat, open the emoji picker (no 'Frequently used'
emojis)
- search a term and select emojis without closing the picker (shift+click on
desktop, plain click on mobile)
- clear the search with backspace
=> traceback: 'Cannot read properties of null (reading
`getBoundingClientRect`)' in adaptNavbar().
This happens because when we clear the search input it calls
`highlightActiveCategory()`, which sets `categoryId` to the topmost category of
the grid, which is now the 'Frequently used' category (sortId 0), added to the
picker since the emojis we just picked updated the recent state. To update the
navbar, `currentNavbarPanel` then looks for the panel holding it in
`emojiNavbarRepr`, but that representation is only built in `adaptNavbar()`,
which runs on mount and from the `ResizeObserver` only, so it was built without
the 'Frequently used' category and no panel contains it. It returns undefined,
the navbar renders empty, its size change wakes the `ResizeObserver`, and
`adaptNavbar()` crashes on querySelector('.o-Emoji').getBoundingClientRect()`.
This commit solves the issue by rendering the `recentEmojis` from a snapshot
taken when the picker is opened, so they are not added to the picker while the
while `emojiNavbarRepr` does not contain their category id.
partial backported PR: https://github.com/odoo/odoo/pull/281104
Task-[6204249](https://www.odoo.com/odoo/project/1519/tasks/6204249)
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286551
Forward-Port-Of: odoo/odoo#284372Fixed an issue where embedded document actions for journal entries could be removed by the automatic cleanup when users were working in a different company. This helps multi-company users keep their document folder shortcuts intact and avoids unnecessary reconfiguration.
Original PR description
Step to reproduce: - You must have at least 2 companies with an account Journal - Create a New Journal Entry actions (child or parent) - Embed it to a folder - Set your company on a different one than the journal's one - Run the Garbage collector cron (Base: Auto-vacuum internal data) - The embed action has been removed The cause of this is that in the `_get_base_server_actions_domain` method in `documents_account` module there is a check on company to avoid using/running the actions when not in the right company. But the garbage collector don't need to have this check. Task-6147618 Forward-Port-Of: odoo/enterprise#122821
Restored or copied databases that are neutralized now complete that safety step before background scheduled jobs can start. This reduces the risk of automated actions running too early on a copied or restored environment, helping avoid unintended emails, integrations, or other scheduled processing.
Original PR description
Before this commit: If you restore a backup or duplicate a database via /web/database/manager with "Neutralize" enabled, the cron workers had a small window between the creation of the Registry and the neutralization of the database where they could try to execute crons. Since neutralize_database does not require a full registry to run (it only does raw SQL operations), we can run it before the Registry creation (which has the effect, among other things, of making the cron workers aware of the new database). opw-6517819 Forward-Port-Of: odoo/odoo#286532
Invoice PDFs for Guatemala and Uruguay now show the correct identification label for customers when a non-default identification type is used. This prevents confusion by matching the displayed label to the customer's selected tax or ID document type.
Original PR description
Currently invoice reports use the label related to the company's country for the partner's vat number, however in the case of latin america countries, a new field l10n_latam_identification_type_id is…
Currently invoice reports use the label related to the company's country for the partner's vat number, however in the case of latin america countries, a new field l10n_latam_identification_type_id is added to partners that allows the use of different identification types. This results in invoice reports showing the identification number next to an incorrect label. This commit fixes the issue for Guatemala and Uruguay by overriding the vat label in their respective invoice template to match the selected identification type. Steps to reproduce: - Install a latam localisation (Uruguay or Guatemala) - Change company to that country's Company - Create a partner located in said country, ensure the identification number is set to something else than the default one - Create an invoice for the partner, confirm it, and then Send it - In the pdf generated for the invoice you will see that under the partner's address the "vat number" has the wrong label opw-6087359 related to: https://github.com/odoo/odoo/pull/260159 Forward-Port-Of: odoo/enterprise#122512
French Point of Sale invoices now download as regular invoices instead of pro-forma documents when e-invoicing validation issues occur. This keeps sales flowing for customers with incomplete data while avoiding an incorrect invoice document being given to the customer.
Original PR description
## Steps to reproduce: - Create a customer with France E-Invoicing (UBL2.1) as eInvoice format in a French company - Go to the PoS, select that partner - Make a sale and ask for an invoice - The…
## Steps to reproduce: - Create a customer with France E-Invoicing (UBL2.1) as eInvoice format in a French company - Go to the PoS, select that partner - Make a sale and ask for an invoice - The downloaded invoice will be a pro-forma invoice ## Why the fix: The pro-forma should not be used here, it is because it is used as a fallback when we get an error while trying to print the invoice. https://github.com/odoo/odoo/blob/4a508586970e44367bbdbbb3cbe88ffb5a1eadb7/addons/account/models/account_move.py#L6187-L6204 As we get an error while trying to send the data with this setup, it goes to the fallback and prints a pro-forma invoice, even though this should not be the case, a regular invoice would do. This happens because when an error is found, we do not populate invoice_pdf_report_id, so it goes to the fallback. We now check if there are any errors in the order, and if there are and the customer requests an ubl_21_fr invoice, we just print the invoice as it is, without going to the pro-forma fallback, as this is not the intended flow. With this fix, we now have the same flow as we do in the sales module, that allows the sale even if the customer has missing data. It will just print the invoice and allow the sale but won't send anything to external entities. opw-6428369 Forward-Port-Of: odoo/odoo#281420
On mobile, the editor toolbar is now hidden when a full-screen image preview is opened, so users can access the preview controls without obstruction. The mobile keyboard is also dismissed when opening the preview, making the viewing experience cleaner and easier to use.
Original PR description
When displaying the full screen image preview lightbox on mobile, the toolbar remains displayed. Because of this, the toolbar of the lightbox cannot be accessed. This commit hides the toolbar when a lightbox is displayed. task-6370220 Forward-Port-Of: odoo/odoo#274949
Guest shoppers who enter a valid EU VAT number during checkout now have their VAT status checked immediately. This allows eligible cross-border EU orders to receive the correct 0% intra-community VAT rate instead of being charged domestic VAT.
Original PR description
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting →…
# Description **Steps to Reproduce** 1. Install Belgium Localization, then go to **Settings → Accounting/Invoicing**. 2. Enable **Verify VAT Numbers** (`vat_check_vies`). 3. Go to **Accounting → Configuration → Fiscal Positions** and confirm or create an **Intra-Community** fiscal position with: - Detect Automatically (`auto_apply`): enabled - VAT Required (`vat_required`): enabled - Country Group: EU, no specific country configured 4. Open an incognito/private browser window and make sure the session is unauthenticated. 5. Go to the website's `/shop` page and add any product to the cart. 6. Proceed to checkout until reaching the Address step (`/shop/address`). 7. Enter a delivery address in an EU country different from the company's country (e.g. company in Belgium, delivery address in the Netherlands). 8. In the VAT Number field, enter a real, valid, VIES-registered VAT number corresponding to the delivery country (e.g. a valid NL VAT number for a Netherlands address). 9. Click **Save Address / Continue** and proceed to the Payment step (`/shop/payment`). 10. Check the tax applied to the delivery line and the resulting order total. **Issue** The delivery product and the overall order are taxed at the standard/domestic VAT rate instead of the expected 0% intra-community rate — even though the customer provided a valid, VIES-registered EU VAT number matching the delivery country. **Root Cause** In `base_vat`, `res.partner.create()` unconditionally removes `vies_valid` from the ORM's pending computation queue via `env.remove_to_compute()`, relying on a subsequent `write()` to trigger the actual VIES check. This holds for the standard backend flow, where creation is followed by a `write()` — but the website guest checkout flow differs: - `website_sale` creates the guest partner through `_create_new_address()`. - The partner is created via a single `create()` call, with no follow-up `write()`. - `_compute_vies_valid()` is therefore never triggered. - `vies_valid` remains permanently unset (`NULL`), despite a VAT number being provided. Downstream, `account.fiscal.position._get_vat_required_valid()` reads this unset value as falsy, so the Intra-Community fiscal position's `vat_required` condition fails and is rejected in favor of another applicable position (e.g. EU B2C or Domestic). **Solution** After partner creation, explicitly trigger `_compute_vies_valid()` when the partner has a VAT number and the operation is not part of a file import (`import_file` context) — performing the VIES check immediately instead of relying on a `write()` that guest checkout never issues. **Result** Guest customers providing a valid EU VAT number now get `vies_valid` computed immediately at creation. Fiscal position detection correctly identifies the Intra-Community position, and the expected 0% VAT treatment is applied to the delivery and order. OPW: 6522992 Forward-Port-Of: odoo/odoo#286201
Users can now download attachments opened in the file viewer from Discuss channels without seeing an error. The change uses the correct download method for these files, restoring a common workflow for sharing and saving images or attachments in conversations.
Original PR description
Description of the issue/feature this PR addresses: Downloading an attachment from the file viewer fails with a `405 Method Not Allowed` error when the attachment belongs to a Discuss channel. I've…
Description of the issue/feature this PR addresses:
Downloading an attachment from the file viewer fails with a `405 Method Not Allowed` error when the attachment belongs to a Discuss channel.
I've already submitted a ticket to Odoo: #6430586
Steps to reproduce (on a 18.0 runbot):
1. open Discuss and send an image in a channel
2. click the image to open the file viewer
3. click the download button (either the one in the header or the one in the bottom
toolbar)
The server rejects the request:
```
POST /discuss/channel/1/image/519861?filename=image.png&unique=32647b0f&download=true 405
```
and the user gets a `RPC_ERROR: Arbitrary Uncaught Python Exception` dialog reporting `405 Method Not Allowed`.
Cause: `download()` always issues a POST request, while the routes serving the attachments of a discuss channel only allow GET:
* `/discuss/channel/<int:channel_id>/attachment/<int:attachment_id>`
* `/discuss/channel/<int:channel_id>/image/<int:attachment_id>`
so the request never reaches the controller. Downloading the very same attachment from the attachment card in the conversation still works, because that one is a plain anchor navigation (GET).
This is a regression from fb152985f4b8 ("[FIX] web: download FileViewer files via blob helper"), which routed the file viewer download through `download()` in order to honor the filename sent by the server in the `Content-Disposition` header.
Only 18.0 is affected: saas-18.1 and saas-18.2 do not have the commit that introduced the regression, and from saas-18.3 on, the `urlRoute` override was dropped and channel attachments are served through the standard `/web/content` and /web/image` routes, which are not restricted to GET.
The download is still sent with POST on those branches though, hence forward-porting this up to master.
Current behavior before PR:
Downloading a Discuss channel attachment from the file viewer raises a 405 error and the file is not downloaded. Images and other file types are equally affected.
Desired behavior after PR is merged:
The file is downloaded, keeping the filename advertised by the server. The download is performed with a GET request through `downloadFile()`, which still goes through the blob helper, so the fix of fb152985f4b8 is preserved. This is already the way a file is downloaded from its url in `readonly_file.js`.
Added a test that downloads an image attachment of a channel from the file viewer and asserts the request is a GET on the channel attachment route. It fails before this fix with `POST /discuss/channel/1/image/1`.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#279957
Forward-Port-Of: odoo/odoo#279232This fix makes Odoo's test tools handle Windows file paths correctly. It allows developers and partners using Windows to run automated tests reliably, reducing setup friction and avoiding platform-specific failures.
Original PR description
### Description of the issue/feature this PR addresses Since February 2026, changes were made in `/tests/common.py` that were probably never tested on anything else than Linux or Mac machines. Since…
### Description of the issue/feature this PR addresses
Since February 2026, changes were made in `/tests/common.py` that were probably never tested on anything else than Linux or Mac machines. Since that time, Windows users like me (sorry) are not able to run the Odoo tests anymore on their machine. Because the problem remains to this day, I thought I'd submit a PR to fix this permanently.
I have not checked versions older than 18, but it seems the problem does not occur in Odoo 19 anymore, as the code in `/tests/common.py` has been greatly improved.
### Current behavior before PR
Running `odoo-bin` with the `--test-enable` flag never works on Windows, because the simple check on [this line](https://github.com/odoo/odoo/blob/18.0/odoo/tests/common.py#L979) fails due to Windows using backslashes instead of forward slashes in file paths.
It results in the following error (and a whole lot more afterwards as a result of that):
```
2026-08-19 10:28:44,868 48604 INFO odoo-local odoo.tests.common: C:\Apps\Python312\Lib\unittest\mock.py:1581:__enter__ setting Users._crypt_context to <function TransactionCase.setUpClass.<locals>._crypt_context at 0x000002C48DD2C540>
Stack (most recent call last):
File "C:\Program Files\JetBrains\PyCharm 2024.1.4\plugins\python-ce\helpers\pydev\pydevd.py", line 2391, in <module>
main()
File "C:\Program Files\JetBrains\PyCharm 2024.1.4\plugins\python-ce\helpers\pydev\pydevd.py", line 2372, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Program Files\JetBrains\PyCharm 2024.1.4\plugins\python-ce\helpers\pydev\pydevd.py", line 1640, in run
return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
File "C:\Program Files\JetBrains\PyCharm 2024.1.4\plugins\python-ce\helpers\pydev\pydevd.py", line 1647, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Program Files\JetBrains\PyCharm 2024.1.4\plugins\python-ce\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "odoo-bin", line 8, in <module>
odoo.cli.main()
File "[REMOVED]\odoo\odoo\cli\command.py", line 76, in main
o.run(args)
File "[REMOVED]\odoo\odoo\cli\server.py", line 182, in run
main(args)
File "[REMOVED]\odoo\odoo\cli\server.py", line 175, in main
rc = odoo.service.server.start(preload=preload, stop=stop)
File "[REMOVED]\odoo\odoo\service\server.py", line 1510, in start
rc = server.run(preload, stop)
File "[REMOVED]\odoo\odoo\service\server.py", line 644, in run
rc = preload_registries(preload)
File "[REMOVED]\odoo\odoo\service\server.py", line 1414, in preload_registries
registry = Registry.new(dbname, update_module=update_module)
File "[REMOVED]\venv\Lib\site-packages\decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
File "[REMOVED]\odoo\odoo\tools\func.py", line 97, in locked
return func(inst, *args, **kwargs)
File "[REMOVED]\odoo\odoo\modules\registry.py", line 118, in new
odoo.modules.load_modules(registry, force_demo, status, update_module)
File "[REMOVED]\odoo\odoo\modules\loading.py", line 485, in load_modules
processed_modules += load_marked_modules(env, graph,
File "[REMOVED]\odoo\odoo\modules\loading.py", line 365, in load_marked_modules
loaded, processed = load_module_graph(
File "[REMOVED]\odoo\odoo\modules\loading.py", line 284, in load_module_graph
test_results = loader.run_suite(suite, global_report=report)
File "[REMOVED]\odoo\odoo\tests\loader.py", line 118, in run_suite
suite(results)
File "C:\Apps\Python312\Lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "[REMOVED]\odoo\odoo\tests\suite.py", line 43, in run
self._handleClassSetUp(test, result)
File "[REMOVED]\odoo\odoo\tests\suite.py", line 175, in _handleClassSetUp
super()._handleClassSetUp(test, result)
File "[REMOVED]\odoo\odoo\tests\suite.py", line 65, in _handleClassSetUp
currentClass.setUpClass()
File "[REMOVED]\odoo\odoo\tests\common.py", line 1076, in setUpClass
cls.startClassPatcher(cls._crypt_context_patcher)
File "[REMOVED]\odoo\odoo\tests\common.py", line 443, in startClassPatcher
mock = patcher.start()
File "C:\Apps\Python312\Lib\unittest\mock.py", line 1624, in start
result = self.__enter__()
File "C:\Apps\Python312\Lib\unittest\mock.py", line 1581, in __enter__
setattr(self.target, self.attribute, new_attr)
File "[REMOVED]\odoo\odoo\tests\common.py", line 985, in metamodel_setattr
_logger.runbot(
File "[REMOVED]\odoo\odoo\netsvc.py", line 376, in runbot
self.log(logging.RUNBOT, message, *args, **kws)
```
### Desired behavior after PR is merged:
The test suite runs without problems on Windows.
Btw, it would probably be better to use `pathlib` instead as a general improvement for all places in the code where local paths are being handled, but that's out of scope here now.
---
- [x] I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#284532
Forward-Port-Of: odoo/odoo#283210The spreadsheet template dialog in Documents now keeps the search bar visible even when no templates match a search. It also prevents a crash when users choose to add a custom filter, making template selection smoother and more reliable.
Original PR description
- templates searchbar disappear when the search has no match - Clicking "Add a custom filter' in the searchbar crashes Task-6526322 Forward-Port-Of: odoo/enterprise#130376 Forward-Port-Of: odoo/enterprise#130096
This fixes an unnecessary warning that appeared while users were creating a new CRM stage. The warning now only appears when changing an existing stage in a way that may trigger extra opportunity recalculations, reducing confusion during setup.
Original PR description
Changing whether a CRM stage is won may trigger the recomputation of its opportunities. An onchange warning was added to inform users about this potentially expensive operation. However, the warning was also displayed when creating a stage because the onchange was triggered while initializing the form. Fix: Only display the warning when editing an existing stage, as it's useless to show this warning when creating a new stage. Task-6424174 Forward-Port-Of: odoo/odoo#284679
This fixes blog content styling so text marked as bold is visibly distinct, even when the surrounding text uses a lighter font weight. It helps editors and readers see intended emphasis consistently across blog posts and font choices.
Original PR description
Problem: When a parent element applies a `font-weight: 300` to its content, a child `<strong>` tag defaults to `font-weight: bolder`, which resolves to a computed font weight of `400`. For certain font families, weight `400` is visually identical to `300`, leaving no visual distinction for bold text. Cause: `<strong>` tags relied on relative weight boosting (`bolder`), which only increases the parent weight from `300` to `400` instead of applying explicit bold weight. Solution: Explicitly set `font-weight: bold` on `strong` for `.o_wblog_read_text` Steps to reproduce: - Go to /blog - Open any blog - Open editor. - Select some text from the content of the blog. - Apply Bold. - Observe that there is no visual difference. opw-6460699 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282667
French Flow 10 e-reporting data is now checked and shortened where needed before submission. This prevents one invalid invoice or company detail from causing an entire report to be rejected by the French public platform.
Original PR description
Some Flow 10 values are generated without applying the length and format constraints expected by the PPF. Long free-text values, oversized VAT numbers, and invalid address data can therefore cause an entire report to be rejected. Limit product names and invoice notes to their allowed lengths and normalize country codes. Validate the declarant SIREN, VAT number lengths, and address values before sending so affected journal entries are marked as errors and excluded from the report. No Task id Forward-Port-Of: odoo/odoo#286817 Forward-Port-Of: odoo/odoo#286536
The Mexican POS self-invoicing test was updated to match the new process where public users can no longer change customer details during self-invoicing. This helps keep automated checks aligned with the intended customer data protection flow without changing day-to-day business functionality.
Original PR description
Before the related pr commit: - Public users could update customer data during the self-invoicing flow. - The test_qr_code_receipt_mx test relied on this behavior when updating customer data. After the ref commit: - Public users can no longer update customer data during self-invoicing. - Update test_qr_code_receipt_mx to create a new partner with the required customer data when the order is not linked to a customer. Related PR: odoo/odoo#283470 Task-6272660 Forward-Port-Of: odoo/enterprise#129948
Cancelled retail orders and order lines are now stored and clearly marked as cancellations when sent for German fiscal certification. This helps ensure Fiskaly receives the right cancellation information, reducing compliance and reporting issues for affected point-of-sale users.
Original PR description
In this commit: --------------- - We now store cancelled retail orders on the server and send the `storno` flag as `true` for cancelled lines and orders to ensure proper handling in Fiskaly. task: 6326042 Relataed PR: https://github.com/odoo/odoo/pull/277648 Forward-Port-Of: odoo/enterprise#129201 Forward-Port-Of: odoo/enterprise#120410
Creating multiple Helpdesk teams with website forms no longer creates duplicate Help menus on the website. Teams now reuse the existing website menu, keeping site navigation clean and avoiding confusion for visitors.
Original PR description
Currently on creating new helpdesk team everytime a new website menu is created. ### **Steps to Reproduce:** 1) Install website_helpdesk 2) Navigate to `Helpdesk>Configuration>Helpdesk Team`. 3)…
Currently on creating new helpdesk team everytime a new website menu is created. ### **Steps to Reproduce:** 1) Install website_helpdesk 2) Navigate to `Helpdesk>Configuration>Helpdesk Team`. 3) Create 2 helpdesk team with `Website Form` option enable. 4) Navigate to Website. ### **Observed Behavior:** Two Help menus are created. ### **Expected Behavior:** Multiple menus should not be created. ### **Root Cause:** The menu creation logic relies on the following [condition](https://github.com/odoo/enterprise/blob/b66097122ba3a758734ac6fb2b26579c35cb72c2/website_helpdesk/models/helpdesk.py#L111-L112) `team_count_by_website` is built from `_read_group(..., ['website_id'], ...)`, which keys its result by the `website_id` *recordset*, not its id. Looking it up with `team_count_by_website.get(website.id, 0)` therefore always misses and falls back to `0`, so `team_count <= 1` is always `True` regardless of how many teams already exist for that website. The only thing left guarding menu creation is `any(team.website_menu_id for team in teams)`, which only looks at the teams in the current create/write call, not every team on that website. So saving a second team in a separate call always creates another menu. ### Fix: Make the website menu a resource shared by every team with the website form enabled on a given website, instead of "owned" by whichever team created it: - Before creating a new menu, look up other teams (active or archived) that already point to a menu, matched through the `website_menu_id` relation between teams rather than a hardcoded `/helpdesk` URL, so a customized menu URL doesn't cause a duplicate to be created. Reuse that menu when found. - Only delete a menu once no team (active or archived) still references it, checked before removing a team's own reference. **opw-6303846** Forward-Port-Of: odoo/enterprise#130207 Forward-Port-Of: odoo/enterprise#121120