Sunday, August 30, 2026
4 changes · master
Resolved issues and error corrections
Saving Live Chat preferences now keeps previously selected expertise and language tags when users add more tags later. This prevents silent loss of user profile information and makes preference updates reliable.
Original PR description
Before this commit, saving the Live Chat section of "My Preferences" twice in a row dropped the tag(s) picked on the first save: adding a second Live Chat Expertise or Spoken Language tag and saving…
Before this commit, saving the Live Chat section of "My Preferences" twice in a row dropped the tag(s) picked on the first save: adding a second Live Chat Expertise or Spoken Language tag and saving again left only that new tag, silently discarding the one saved earlier.
Steps to reproduce (runbot v19.3):
1. Install the Live Chat app (im_livechat).
2. Go to your user preferences ("My Preferences").
3. Under the "Live Chat" section, add a tag to "Live Chat Expertise" or "Spoken Languages" and save.
4. Edit the profile again, add a second tag to the same field, and save.
5. Observe that the first tag disappears, leaving only the newly added tag.
More generally, this affects any non-stored, user-writeable many2many field (compute + inverse) that also depends on context — as livechat_expertise_ids/livechat_lang_ids do, via `depends_context('uid')`, since their value is per-user. This happened because `Many2many.write_real()` read the field's "old" value through `.sudo()` to diff it against the new one. For such a field, that sudo read lands in a different cache bucket than the one being written. That read also happened, incidentally, every time the field's own compute method assigned itself, at which point the record was protected against recomputation; hitting that protection through the unrelated sudo bucket cached an empty value there instead of recomputing it. The next real write then read that stale, empty bucket as the "old" value and applied the new tag on top of nothing, so the previous tag(s) were lost.
This commit fixes the by making `write_real()` now only reads through `.sudo()` when the field is actually `store`d, i.e. backed by a real relation table where bypassing access rights to see the full relation is meaningful. A non-stored field no longer creates that second cache bucket, so old and new values are always read from the same place the write goes to.
opw-6450877
Forward-Port-Of: odoo/odoo#285406
Forward-Port-Of: odoo/odoo#285224Peruvian electronic invoices no longer get stuck when SUNAT has registered them but the confirmation receipt is not ready yet. Odoo now keeps retrying automatically until the receipt becomes available, reducing manual follow-up and failed invoice processing delays.
Original PR description
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError),…
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError), even though SUNAT actually finishes registering the document on its side a moment later. - Odoo retries sending the same invoice (either automatically through the EDI cron, or manually). SUNAT now replies with a "document already exists" SOAP fault (code 1033/4000), since it processed the previous attempt. - Odoo tries to recover from this by fetching the CDR through getStatusCdr, but SUNAT has not finished generating it yet, so the lookup also fails. Cause of the issue: _l10n_pe_edi_post_invoice_web_service() already has recovery logic for error codes 1033/4000: it calls _l10n_pe_edi_retrieve_cdr() to fetch the CDR and treat the invoice as sent. But when that lookup itself fails (CDR not generated yet), the resulting error keeps the 'blocking_level' set to 'error' from the original SOAP fault. Documents with blocking_level 'error' are excluded from the automatic EDI cron retries (see account.edi.document._cron_process_documents_web_services), so the invoice gets stuck needing a manual retry, which can lose the same race against SUNAT again and again. Solution: When the CDR can't be retrieved yet after a 1033/4000 duplicate error, mark the result as 'blocking_level': 'warning' instead of leaving it at 'error'. This keeps the invoice eligible for the automatic EDI cron retries, so Odoo keeps polling SUNAT until the CDR becomes available, instead of requiring manual intervention every time this race is lost. opw-6393231 Forward-Port-Of: odoo/enterprise#128497 Forward-Port-Of: odoo/enterprise#125053
This fixes a calendar sync issue where editing the first event in an Outlook recurring series could cause all future occurrences in Odoo to be deleted and recreated with the same details. Recurring meetings now keep individual changes, such as attendees on one occurrence, without incorrectly applying them to the entire series.
Original PR description
The stored rrule of a recurrence embeds a DTSTART line based on the recurrence dtstart (the smallest start among its events). When the first occurrence is moved in Outlook, the Odoo dtstart no longer…
The stored rrule of a recurrence embeds a DTSTART line based on the recurrence dtstart (the smallest start among its events). When the first occurrence is moved in Outlook, the Odoo dtstart no longer matches that DTSTART, but the stored rrule is not recomputed at that point (it only depends on the pattern fields). On a later sync touching the seriesMaster (e.g. after adding an attendee to the moved occurrence), the recurrence values are written again and the rrule is reserialized with a DTSTART based on the moved occurrence. _write_from_microsoft() took this new rrule string as a pattern change and reapplied the recurrence: all occurrences were deleted and recreated as copies of the moved one, spreading its specific data (e.g. the newly added attendee) to the whole series. Ignore the DTSTART line when comparing the rrule before and after the write: a DTSTART-only difference does not reflect any pattern change in Outlook. An actual pattern change (FREQ, UNTIL, INTERVAL, ...) still reapplies the recurrence as before. Steps to reproduce: 1. In Outlook, create a recurring event 2. In Odoo, run the calendar sync 3. In Outlook, move the first occurrence of the recurrence (shift its start and stop time by 30 minutes) 4. In Odoo, run the sync 5. In Outlook, add an attendee to that same first occurrence 6. In Odoo, run the sync All the occurrences are deleted and recreated as copies of the first one, and the attendee ends up on every occurrence instead of one. opw-5129848 Forward-Port-Of: odoo/odoo#284747 Forward-Port-Of: odoo/odoo#269549
This fix prevents duplicate calendar entries when an Outlook event that was already synced is changed into a recurring meeting in Outlook. The existing single event is now correctly replaced by the recurring series, keeping calendars cleaner and avoiding confusion for users.
Original PR description
When a single event already synced with Outlook is turned into a recurring event directly in Outlook, Microsoft reuses the event in place: the seriesMaster keeps the iCalUId of the former single event, while each occurrence gets a fresh id/iCalUId. On resync, a seriesMaster is only matched against calendar.recurrence, never against calendar.event. As no recurrence exists yet, it is handled as a brand new recurrence whose occurrences are created from scratch. The original single event, which shares the master's iCalUId and the first occurrence's timeslot, is then left untouched. Drop that pre-existing single event when building a new recurrence from an inbound seriesMaster, since it is now represented by the recurrence itself. opw-5129848 Forward-Port-Of: odoo/odoo#285196 Forward-Port-Of: odoo/odoo#271787