From 4e6e411458785310669657251f37f6e686a018a7 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Wed, 5 Aug 2026 08:28:15 +0200 Subject: [PATCH 1/3] [T3339] FIX: gate B2S letter translation on detected content language - FIX: Trust the letter's detected language over the field-office stamp; fail safe to translation when it can't be determined --- .../models/correspondence.py | 11 ++++++++--- sbc_compassion/models/correspondence.py | 12 ++++++++++++ sbc_translation/models/correspondence.py | 9 ++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/partner_communication_compassion/models/correspondence.py b/partner_communication_compassion/models/correspondence.py index 641c0e46b..04dfc3ae7 100644 --- a/partner_communication_compassion/models/correspondence.py +++ b/partner_communication_compassion/models/correspondence.py @@ -224,14 +224,19 @@ def _can_auto_send(self): require manual validation before. """ self.ensure_one() - partner_langs = self.supporter_languages_ids types = self.communication_type_ids.mapped("name") valid = ( self.sponsorship_id.state == "active" and "Final Letter" not in types and "auto" in self.partner_id.letter_delivery_preference ) - if not (partner_langs & self.beneficiary_language_ids): - valid &= self.translation_language_id in partner_langs + # Only auto-send if the letter's actual content language is one the + # sponsor reads (detected from content, not the field-office stamp). + detected_lang = self._detect_letter_language() + valid = ( + valid + and bool(detected_lang) + and detected_lang in self.supporter_languages_ids + ) return valid diff --git a/sbc_compassion/models/correspondence.py b/sbc_compassion/models/correspondence.py index 140f2e281..c353995b9 100644 --- a/sbc_compassion/models/correspondence.py +++ b/sbc_compassion/models/correspondence.py @@ -500,6 +500,18 @@ def _check_translation_language(self): ): letter.original_language_id = detected_lang + def _detect_letter_language(self): + """Language the letter is actually written in. Detected from its text.""" + self.ensure_one() + text = self.translated_text or self.english_text or self.original_text or "" + clean = ( + text.strip(" \t\n\r.") + .replace(BOX_SEPARATOR, "") + .replace(PAGE_SEPARATOR, "") + .strip() + ) + return self.env["langdetect"].detect_language(clean) + @api.depends("uuid") def _compute_read_url(self): for letter in self: diff --git a/sbc_translation/models/correspondence.py b/sbc_translation/models/correspondence.py index d32803483..1ee4d5ecd 100644 --- a/sbc_translation/models/correspondence.py +++ b/sbc_translation/models/correspondence.py @@ -265,10 +265,13 @@ def process_letter(self): if not force_publish: letter._check_translation_language() - # Can sponser read the letter? + # Can sponser read the letter? Decide from the letter's ACTUAL + # content language, not the field-office TranslationLanguage stamp. + # If it can't be determined, fail safe to translation. + detected_lang = letter._detect_letter_language() langs_match = ( - letter.beneficiary_language_ids & letter.supporter_languages_ids - ) or letter.translation_language_id in letter.supporter_languages_ids + bool(detected_lang) and detected_lang in letter.supporter_languages_ids + ) # Is the letter still in the translation process? translation_hold = ( From 7eebd47ba39e9ce89d881f84ae7d8a979b22e345 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Wed, 5 Aug 2026 08:29:20 +0200 Subject: [PATCH 2/3] Merge pull request #2122 from CompassionCH/T3343-retry-gmc-serialization-errors [T3343] FIX: propagate PG concurrency errors so queue jobs retry --- .../models/gmc_message.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/message_center_compassion/models/gmc_message.py b/message_center_compassion/models/gmc_message.py index 9e991b38e..19715e349 100644 --- a/message_center_compassion/models/gmc_message.py +++ b/message_center_compassion/models/gmc_message.py @@ -13,8 +13,11 @@ import traceback from datetime import datetime +from psycopg2 import OperationalError + from odoo import _, api, fields, models from odoo.exceptions import UserError +from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY from ..tools.onramp_connector import OnrampConnector @@ -196,7 +199,14 @@ def _process_messages(self): if action.direction == "in": try: message_update.update(self._perform_incoming_action()) - except Exception: + except Exception as e: + if ( + isinstance(e, OperationalError) + and e.pgcode in PG_CONCURRENCY_ERRORS_TO_RETRY + ): + # Retryable concurrency error: propagate so the queue job + # is retried instead of writing in the failed transaction. + raise # Abort pending operations logger.error("Failure when processing message", exc_info=True) self.env.cr.rollback() @@ -410,6 +420,13 @@ def _process_single_answer(self, data_object, answer_data): f(answer_data) self.state = "success" except Exception as e: + if ( + isinstance(e, OperationalError) + and e.pgcode in PG_CONCURRENCY_ERRORS_TO_RETRY + ): + # Retryable concurrency error: the transaction is aborted and + # cannot be written to; propagate so the queue job is retried. + raise logger.error(traceback.format_exc()) try: if action.failure_method: From ed8d7c0cae0ef4cbbae783805d4acb65b5fdb288 Mon Sep 17 00:00:00 2001 From: Emanuel Cino Date: Wed, 5 Aug 2026 08:29:57 +0200 Subject: [PATCH 3/3] [MIG] Forward-port changes from 14.0