details:   https://code.tryton.org/tryton/commit/773f7f1ba3d5
branch:    default
user:      Cédric Krier <[email protected]>
date:      Tue Dec 02 16:01:39 2025 +0100
description:
        Add chat to operational documents
diffstat:

 modules/account_budget/account.py              |   5 ++-
 modules/account_dunning/dunning.py             |  13 +++++++-
 modules/account_invoice/invoice.py             |  14 +++++++--
 modules/account_payment/payment.py             |  12 ++++++--
 modules/account_statement/statement.py         |   6 ++--
 modules/account_stock_landed_cost/account.py   |   4 +-
 modules/account_stock_shipment_cost/account.py |   5 ++-
 modules/commission/commission.py               |  11 ++++++-
 modules/production/production.py               |   5 ++-
 modules/production_work/work.py                |   4 +-
 modules/project/work.py                        |  19 ++++++++++--
 modules/purchase/purchase.py                   |  11 ++++++-
 modules/purchase_blanket_agreement/purchase.py |  11 ++++++-
 modules/purchase_request/purchase_request.py   |   4 +-
 modules/purchase_request_quotation/purchase.py |  11 ++++++-
 modules/purchase_requisition/purchase.py       |   4 +-
 modules/quality/quality.py                     |   8 ++--
 modules/sale/sale.py                           |  11 ++++++-
 modules/sale_blanket_agreement/sale.py         |  11 ++++++-
 modules/sale_complaint/complaint.py            |  10 +++++-
 modules/sale_opportunity/opportunity.py        |  10 +++++-
 modules/sale_point/sale.py                     |   5 ++-
 modules/sale_rental/sale.py                    |  12 ++++++--
 modules/sale_subscription/subscription.py      |  10 +++++-
 modules/stock/inventory.py                     |   4 +-
 modules/stock/shipment.py                      |  39 ++++++++++++++++++++++----
 modules/stock_forecast/forecast.py             |   4 +-
 27 files changed, 198 insertions(+), 65 deletions(-)

diffs (890 lines):

diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/account_budget/account.py
--- a/modules/account_budget/account.py Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/account_budget/account.py Tue Dec 02 16:01:39 2025 +0100
@@ -10,7 +10,8 @@
 from trytond import backend
 from trytond.i18n import gettext
 from trytond.model import (
-    Index, ModelSQL, ModelView, Unique, fields, sequence_ordered, tree)
+    ChatMixin, Index, ModelSQL, ModelView, Unique, fields, sequence_ordered,
+    tree)
 from trytond.modules.account.exceptions import FiscalYearNotFoundError
 from trytond.modules.currency.fields import Monetary
 from trytond.pool import Pool
@@ -87,7 +88,7 @@
         raise NotImplementedError
 
 
-class BudgetMixin:
+class BudgetMixin(ChatMixin):
     __slots__ = ()
     name = fields.Char("Name", required=True)
     company = fields.Many2One(
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/account_dunning/dunning.py
--- a/modules/account_dunning/dunning.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/account_dunning/dunning.py        Tue Dec 02 16:01:39 2025 +0100
@@ -6,7 +6,8 @@
 from sql import Literal
 
 from trytond.model import (
-    Index, Model, ModelSQL, ModelView, Unique, fields, sequence_ordered)
+    ChatMixin, Index, Model, ModelSQL, ModelView, Unique, fields,
+    sequence_ordered)
 from trytond.modules.currency.fields import Monetary
 from trytond.pool import Pool
 from trytond.pyson import Eval
@@ -49,7 +50,7 @@
     }
 
 
-class Dunning(ModelSQL, ModelView):
+class Dunning(ModelSQL, ModelView, ChatMixin):
     __name__ = 'account.dunning'
     company = fields.Many2One(
         'company.company', "Company", required=True, states=_STATES,
@@ -303,6 +304,14 @@
             level=level,
             )
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = (
+                self.party.lang.code if self.party and self.party.lang
+                else None)
+        return language
+
     @classmethod
     def process(cls, dunnings):
         pool = Pool()
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/account_invoice/invoice.py
--- a/modules/account_invoice/invoice.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/account_invoice/invoice.py        Tue Dec 02 16:01:39 2025 +0100
@@ -18,8 +18,8 @@
 from trytond import backend, config
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Index, ModelSQL, ModelView, Unique, Workflow, dualmethod,
-    fields, sequence_ordered)
+    ChatMixin, DeactivableMixin, Index, ModelSQL, ModelView, Unique, Workflow,
+    dualmethod, fields, sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.account.exceptions import AccountMissing
 from trytond.modules.account.tax import TaxableMixin
@@ -61,7 +61,9 @@
     invoice_report_format = fields.Char("Invoice Report Format", readonly=True)
 
 
-class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
+class Invoice(
+        Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin,
+        ChatMixin):
     __name__ = 'account.invoice'
     _rec_name = 'number'
     _order_name = 'number'
@@ -1531,6 +1533,12 @@
         return ', '.join(set(filter(None,
                     (l.origin_name for l in self.line_lines))))
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def view_attributes(cls):
         return super().view_attributes() + [
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/account_payment/payment.py
--- a/modules/account_payment/payment.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/account_payment/payment.py        Tue Dec 02 16:01:39 2025 +0100
@@ -13,7 +13,7 @@
 from trytond import backend
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields)
+    ChatMixin, DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import (
     employee_field, reset_employee, set_employee)
@@ -62,7 +62,7 @@
         return Transaction().context.get('company')
 
 
-class Group(ModelSQL, ModelView):
+class Group(ModelSQL, ModelView, ChatMixin):
     __name__ = 'account.payment.group'
     _rec_name = 'number'
     number = fields.Char('Number', required=True, readonly=True)
@@ -274,7 +274,7 @@
     }
 
 
-class Payment(Workflow, ModelSQL, ModelView):
+class Payment(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'account.payment'
     _rec_name = 'number'
     number = fields.Char("Number", required=True, readonly=True)
@@ -614,6 +614,12 @@
             ('reference', *clause[1:]),
             ]
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def view_attributes(cls):
         context = Transaction().context
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/account_statement/statement.py
--- a/modules/account_statement/statement.py    Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/account_statement/statement.py    Tue Dec 02 16:01:39 2025 +0100
@@ -13,8 +13,8 @@
 import trytond.config as config
 from trytond.i18n import gettext
 from trytond.model import (
-    Check, DictSchemaMixin, Index, ModelSQL, ModelView, Workflow, fields,
-    sequence_ordered)
+    ChatMixin, Check, DictSchemaMixin, Index, ModelSQL, ModelView, Workflow,
+    fields, sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company import CompanyReport
 from trytond.modules.currency.fields import Monetary
@@ -49,7 +49,7 @@
         return ''
 
 
-class Statement(Workflow, ModelSQL, ModelView):
+class Statement(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'account.statement'
 
     _states = {'readonly': Eval('state') != 'draft'}
diff -r b4deb9f4d972 -r 773f7f1ba3d5 
modules/account_stock_landed_cost/account.py
--- a/modules/account_stock_landed_cost/account.py      Tue Dec 02 13:08:05 
2025 +0100
+++ b/modules/account_stock_landed_cost/account.py      Tue Dec 02 16:01:39 
2025 +0100
@@ -8,7 +8,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Index, MatchMixin, ModelSQL, ModelView, Workflow, fields)
+    ChatMixin, Index, MatchMixin, ModelSQL, ModelView, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import CompanyValueMixin
 from trytond.modules.product import price_digits, round_price
@@ -66,7 +66,7 @@
             return None
 
 
-class LandedCost(Workflow, ModelSQL, ModelView, MatchMixin):
+class LandedCost(Workflow, ModelSQL, ModelView, MatchMixin, ChatMixin):
     __name__ = 'account.landed_cost'
     _rec_name = 'number'
     number = fields.Char("Number", readonly=True)
diff -r b4deb9f4d972 -r 773f7f1ba3d5 
modules/account_stock_shipment_cost/account.py
--- a/modules/account_stock_shipment_cost/account.py    Tue Dec 02 13:08:05 
2025 +0100
+++ b/modules/account_stock_shipment_cost/account.py    Tue Dec 02 16:01:39 
2025 +0100
@@ -6,7 +6,8 @@
 from sql.functions import CharLength
 
 from trytond.i18n import gettext
-from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
+from trytond.model import (
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import CompanyValueMixin
 from trytond.modules.product import price_digits, round_price
@@ -58,7 +59,7 @@
             return None
 
 
-class ShipmentCost(Workflow, ModelSQL, ModelView):
+class ShipmentCost(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'account.shipment_cost'
     _rec_name = 'number'
 
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/commission/commission.py
--- a/modules/commission/commission.py  Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/commission/commission.py  Tue Dec 02 16:01:39 2025 +0100
@@ -15,7 +15,7 @@
 from trytond import backend
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, MatchMixin, ModelSQL, ModelView, fields,
+    ChatMixin, DeactivableMixin, MatchMixin, ModelSQL, ModelView, fields,
     sequence_ordered)
 from trytond.modules.currency.fields import Monetary
 from trytond.modules.product import price_digits, round_price
@@ -337,7 +337,7 @@
         return super().match(pattern)
 
 
-class Commission(ModelSQL, ModelView):
+class Commission(ModelSQL, ModelView, ChatMixin):
     __name__ = 'commission'
     _readonly_states = {
         'readonly': Bool(Eval('invoice_line')),
@@ -442,6 +442,13 @@
                 state = invoice.state
         return state
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = (
+                self.agent.party.lang.code if self.agent.party.lang else None)
+        return language
+
     @classmethod
     def copy(cls, commissions, default=None):
         if default is None:
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/production/production.py
--- a/modules/production/production.py  Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/production/production.py  Tue Dec 02 16:01:39 2025 +0100
@@ -11,7 +11,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Index, ModelSQL, ModelView, Workflow, dualmethod, fields)
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, dualmethod, fields)
 from trytond.modules.company.model import employee_field, set_employee
 from trytond.modules.product import price_digits, round_price
 from trytond.modules.stock.shipment import ShipmentAssignMixin
@@ -22,7 +22,8 @@
 from .exceptions import CostWarning
 
 
-class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
+class Production(
+        ShipmentAssignMixin, Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'production'
     _rec_name = 'number'
     _assign_moves_field = 'inputs'
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/production_work/work.py
--- a/modules/production_work/work.py   Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/production_work/work.py   Tue Dec 02 16:01:39 2025 +0100
@@ -12,7 +12,7 @@
 from trytond import backend
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields,
+    ChatMixin, DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields,
     sequence_ordered, tree)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import employee_field, set_employee
@@ -99,7 +99,7 @@
         return picker
 
 
-class Work(sequence_ordered(), ModelSQL, ModelView):
+class Work(sequence_ordered(), ModelSQL, ModelView, ChatMixin):
     __name__ = 'production.work'
     operation = fields.Many2One('production.routing.operation', 'Operation',
         required=True)
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/project/work.py
--- a/modules/project/work.py   Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/project/work.py   Tue Dec 02 16:01:39 2025 +0100
@@ -7,8 +7,8 @@
 from trytond.cache import Cache
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Index, ModelSQL, ModelView, fields, sequence_ordered,
-    sum_tree, tree)
+    ChatMixin, DeactivableMixin, Index, ModelSQL, ModelView, fields,
+    sequence_ordered, sum_tree, tree)
 from trytond.pool import Pool
 from trytond.pyson import Bool, Eval, If, PYSONEncoder, TimeDelta
 from trytond.transaction import Transaction
@@ -104,7 +104,9 @@
         return domains
 
 
-class Work(sequence_ordered(), tree(separator='\\'), ModelSQL, ModelView):
+class Work(
+        sequence_ordered(), tree(separator='\\'), ModelSQL, ModelView,
+        ChatMixin):
     __name__ = 'project.work'
     name = fields.Char("Name", required=True)
     type = fields.Selection([
@@ -392,6 +394,17 @@
             int,
             {w.id: w.effort_hours * (w.progress or 0) for w in works})
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            work = self
+            party = self.party
+            while work and not party:
+                work = work.parent
+                party = work.party
+            language = party.lang.code if party and party.lang else None
+        return language
+
     @classmethod
     def copy(cls, project_works, default=None):
         pool = Pool()
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/purchase/purchase.py
--- a/modules/purchase/purchase.py      Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/purchase/purchase.py      Tue Dec 02 16:01:39 2025 +0100
@@ -15,7 +15,8 @@
 from trytond.ir.attachment import AttachmentCopyMixin
 from trytond.ir.note import NoteCopyMixin
 from trytond.model import (
-    Index, ModelSQL, ModelView, Unique, Workflow, fields, sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Unique, Workflow, fields,
+    sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.account.tax import TaxableMixin
 from trytond.modules.account_product.exceptions import AccountError
@@ -69,7 +70,7 @@
 
 class Purchase(
         Workflow, ModelSQL, ModelView, TaxableMixin,
-        AttachmentCopyMixin, NoteCopyMixin):
+        AttachmentCopyMixin, NoteCopyMixin, ChatMixin):
     __name__ = 'purchase.purchase'
     _rec_name = 'number'
 
@@ -731,6 +732,12 @@
                     ])
         return attributes
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def get_resources_to_copy(cls, name):
         return {
diff -r b4deb9f4d972 -r 773f7f1ba3d5 
modules/purchase_blanket_agreement/purchase.py
--- a/modules/purchase_blanket_agreement/purchase.py    Tue Dec 02 13:08:05 
2025 +0100
+++ b/modules/purchase_blanket_agreement/purchase.py    Tue Dec 02 16:01:39 
2025 +0100
@@ -9,7 +9,8 @@
 from sql.functions import CharLength
 
 from trytond.i18n import gettext
-from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
+from trytond.model import (
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.modules.currency.fields import Monetary
 from trytond.modules.product import price_digits, round_price
 from trytond.modules.product.exceptions import UOMValidationError
@@ -106,7 +107,7 @@
             return None
 
 
-class BlanketAgreement(Workflow, ModelSQL, ModelView):
+class BlanketAgreement(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'purchase.blanket_agreement'
     _rec_name = 'number'
 
@@ -310,6 +311,12 @@
             ('reference', operator, value),
             ]
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.supplier.lang.code if self.supplier.lang else None
+        return language
+
     @classmethod
     def copy(cls, agreements, default=None):
         default = default.copy() if default is not None else {}
diff -r b4deb9f4d972 -r 773f7f1ba3d5 
modules/purchase_request/purchase_request.py
--- a/modules/purchase_request/purchase_request.py      Tue Dec 02 13:08:05 
2025 +0100
+++ b/modules/purchase_request/purchase_request.py      Tue Dec 02 16:01:39 
2025 +0100
@@ -5,7 +5,7 @@
 from itertools import groupby
 
 from trytond.i18n import gettext
-from trytond.model import Index, ModelSQL, ModelView, fields
+from trytond.model import ChatMixin, Index, ModelSQL, ModelView, fields
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import (
     employee_field, reset_employee, set_employee)
@@ -21,7 +21,7 @@
     }
 
 
-class PurchaseRequest(ModelSQL, ModelView):
+class PurchaseRequest(ModelSQL, ModelView, ChatMixin):
     __name__ = 'purchase.request'
 
     product = fields.Many2One(
diff -r b4deb9f4d972 -r 773f7f1ba3d5 
modules/purchase_request_quotation/purchase.py
--- a/modules/purchase_request_quotation/purchase.py    Tue Dec 02 13:08:05 
2025 +0100
+++ b/modules/purchase_request_quotation/purchase.py    Tue Dec 02 16:01:39 
2025 +0100
@@ -9,7 +9,8 @@
 from sql.functions import CharLength
 
 from trytond.i18n import gettext
-from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
+from trytond.model import (
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.modules.company import CompanyReport
 from trytond.modules.currency.fields import Monetary
 from trytond.modules.product import price_digits
@@ -86,7 +87,7 @@
             return None
 
 
-class Quotation(Workflow, ModelSQL, ModelView):
+class Quotation(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'purchase.request.quotation'
     _rec_name = 'number'
 
@@ -230,6 +231,12 @@
         if self.supplier:
             self.supplier_address = self.supplier.address_get()
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.supplier.lang.code if self.supplier.lang else None
+        return language
+
     @classmethod
     def copy(cls, groups, default=None):
         if default is None:
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/purchase_requisition/purchase.py
--- a/modules/purchase_requisition/purchase.py  Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/purchase_requisition/purchase.py  Tue Dec 02 16:01:39 2025 +0100
@@ -8,7 +8,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
 from trytond.model.exceptions import AccessError, RequiredValidationError
 from trytond.modules.company.model import (
     employee_field, reset_employee, set_employee)
@@ -68,7 +68,7 @@
             return None
 
 
-class PurchaseRequisition(Workflow, ModelSQL, ModelView):
+class PurchaseRequisition(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'purchase.requisition'
     _rec_name = 'number'
     _states = {
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/quality/quality.py
--- a/modules/quality/quality.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/quality/quality.py        Tue Dec 02 16:01:39 2025 +0100
@@ -9,8 +9,8 @@
 
 from trytond.i18n import gettext, lazy_gettext
 from trytond.model import (
-    DeactivableMixin, DictSchemaMixin, MatchMixin, ModelSingleton, ModelSQL,
-    ModelStorage, ModelView, Unique, Workflow, dualmethod, fields)
+    ChatMixin, DeactivableMixin, DictSchemaMixin, MatchMixin, ModelSingleton,
+    ModelSQL, ModelStorage, ModelView, Unique, Workflow, dualmethod, fields)
 from trytond.model.exceptions import AccessError, ButtonActionException
 from trytond.modules.company.model import (
     CompanyMultiValueMixin, CompanyValueMixin, employee_field, reset_employee,
@@ -341,7 +341,7 @@
             return result
 
 
-class Inspection(Workflow, ModelSQL, ModelView):
+class Inspection(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'quality.inspection'
     _rec_name = 'number'
 
@@ -608,7 +608,7 @@
         return cls(control=control, origin=origin)
 
 
-class Alert(Workflow, ModelSQL, ModelView):
+class Alert(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'quality.alert'
     _rec_name = 'number'
 
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale/sale.py
--- a/modules/sale/sale.py      Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale/sale.py      Tue Dec 02 16:01:39 2025 +0100
@@ -16,7 +16,8 @@
 from trytond.ir.attachment import AttachmentCopyMixin
 from trytond.ir.note import NoteCopyMixin
 from trytond.model import (
-    Index, ModelSQL, ModelView, Unique, Workflow, fields, sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Unique, Workflow, fields,
+    sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.account.tax import TaxableMixin
 from trytond.modules.account_product.exceptions import AccountError
@@ -71,7 +72,7 @@
 
 class Sale(
         Workflow, ModelSQL, ModelView, TaxableMixin,
-        AttachmentCopyMixin, NoteCopyMixin):
+        AttachmentCopyMixin, NoteCopyMixin, ChatMixin):
     __name__ = 'sale.sale'
     _rec_name = 'number'
     company = fields.Many2One(
@@ -878,6 +879,12 @@
                     ])
         return attributes
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def get_resources_to_copy(cls, name):
         return {
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_blanket_agreement/sale.py
--- a/modules/sale_blanket_agreement/sale.py    Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_blanket_agreement/sale.py    Tue Dec 02 16:01:39 2025 +0100
@@ -9,7 +9,8 @@
 from sql.functions import CharLength
 
 from trytond.i18n import gettext
-from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
+from trytond.model import (
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.modules.currency.fields import Monetary
 from trytond.modules.product import price_digits, round_price
 from trytond.modules.product.exceptions import UOMValidationError
@@ -106,7 +107,7 @@
             return None
 
 
-class BlanketAgreement(Workflow, ModelSQL, ModelView):
+class BlanketAgreement(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'sale.blanket_agreement'
     _rec_name = 'number'
 
@@ -311,6 +312,12 @@
             ('reference', operator, value),
             ]
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.customer.lang.code if self.customer.lang else None
+        return language
+
     @classmethod
     def copy(cls, agreements, default=None):
         default = default.copy() if default is not None else {}
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_complaint/complaint.py
--- a/modules/sale_complaint/complaint.py       Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_complaint/complaint.py       Tue Dec 02 16:01:39 2025 +0100
@@ -10,7 +10,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields)
+    ChatMixin, DeactivableMixin, Index, ModelSQL, ModelView, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import (
     employee_field, reset_employee, set_employee)
@@ -34,7 +34,7 @@
                     'account.invoice', 'account.invoice.line'])])
 
 
-class Complaint(Workflow, ModelSQL, ModelView):
+class Complaint(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'sale.complaint'
     _rec_name = 'number'
 
@@ -278,6 +278,12 @@
             ('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')),
             ]
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.customer.lang.code if self.customer.lang else None
+        return language
+
     @classmethod
     def preprocess_values(cls, mode, values):
         pool = Pool()
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_opportunity/opportunity.py
--- a/modules/sale_opportunity/opportunity.py   Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_opportunity/opportunity.py   Tue Dec 02 16:01:39 2025 +0100
@@ -10,7 +10,7 @@
 from trytond.ir.attachment import AttachmentCopyMixin
 from trytond.ir.note import NoteCopyMixin
 from trytond.model import (
-    Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import employee_field, set_employee
 from trytond.modules.currency.fields import Monetary
@@ -22,7 +22,7 @@
 
 class SaleOpportunity(
         Workflow, ModelSQL, ModelView,
-        AttachmentCopyMixin, NoteCopyMixin):
+        AttachmentCopyMixin, NoteCopyMixin, ChatMixin):
     __name__ = "sale.opportunity"
     _history = True
     _rec_name = 'number'
@@ -293,6 +293,12 @@
             ('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')),
             ]
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def get_resources_to_copy(cls, name):
         return {
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_point/sale.py
--- a/modules/sale_point/sale.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_point/sale.py        Tue Dec 02 16:01:39 2025 +0100
@@ -13,7 +13,8 @@
 from trytond import backend
 from trytond.i18n import gettext
 from trytond.model import (
-    DeactivableMixin, Exclude, Index, ModelSQL, ModelView, Workflow, fields)
+    ChatMixin, DeactivableMixin, Exclude, Index, ModelSQL, ModelView, Workflow,
+    fields)
 from trytond.model.exceptions import AccessError
 from trytond.modules.account.tax import TaxableMixin
 from trytond.modules.currency.fields import Monetary
@@ -92,7 +93,7 @@
                                 'msg_point_change_tax_included'))
 
 
-class POSSale(Workflow, ModelSQL, ModelView, TaxableMixin):
+class POSSale(Workflow, ModelSQL, ModelView, TaxableMixin, ChatMixin):
     __name__ = 'sale.point.sale'
     _rec_name = 'number'
 
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_rental/sale.py
--- a/modules/sale_rental/sale.py       Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_rental/sale.py       Tue Dec 02 16:01:39 2025 +0100
@@ -9,8 +9,8 @@
 from trytond.ir.attachment import AttachmentCopyMixin
 from trytond.ir.note import NoteCopyMixin
 from trytond.model import (
-    Index, ModelSQL, ModelView, Unique, Workflow, dualmethod, fields,
-    sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Unique, Workflow, dualmethod,
+    fields, sequence_ordered)
 from trytond.model.exceptions import AccessError, ValidationError
 from trytond.model.fields.date import FormatMixin
 from trytond.modules.account.tax import TaxableMixin
@@ -121,7 +121,7 @@
 
 class Rental(
         Workflow, ModelSQL, ModelView, TaxableMixin,
-        AttachmentCopyMixin, NoteCopyMixin):
+        AttachmentCopyMixin, NoteCopyMixin, ChatMixin):
     __name__ = 'sale.rental'
     _rec_name = 'number'
 
@@ -606,6 +606,12 @@
             ]
         return domain
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def copy(cls, rentals, default=None):
         default = default.copy() if default is not None else {}
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/sale_subscription/subscription.py
--- a/modules/sale_subscription/subscription.py Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/sale_subscription/subscription.py Tue Dec 02 16:01:39 2025 +0100
@@ -9,7 +9,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company.model import (
     employee_field, reset_employee, set_employee)
@@ -25,7 +25,7 @@
 from .exceptions import InvalidRecurrence, InvoiceError
 
 
-class Subscription(Workflow, ModelSQL, ModelView):
+class Subscription(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'sale.subscription'
     _rec_name = 'number'
 
@@ -335,6 +335,12 @@
             ]
         return domain
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.party.lang.code if self.party.lang else None
+        return language
+
     @classmethod
     def copy(cls, subscriptions, default=None):
         if default is None:
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/stock/inventory.py
--- a/modules/stock/inventory.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/stock/inventory.py        Tue Dec 02 16:01:39 2025 +0100
@@ -7,7 +7,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Check, Index, Model, ModelSQL, ModelView, Workflow, fields)
+    ChatMixin, Check, Index, Model, ModelSQL, ModelView, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.pool import Pool
 from trytond.pyson import Bool, Eval, If
@@ -19,7 +19,7 @@
     InventoryCountWarning, InventoryFutureWarning, InventoryValidationError)
 
 
-class Inventory(Workflow, ModelSQL, ModelView):
+class Inventory(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'stock.inventory'
     _rec_name = 'number'
 
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/stock/shipment.py
--- a/modules/stock/shipment.py Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/stock/shipment.py Tue Dec 02 16:01:39 2025 +0100
@@ -11,7 +11,7 @@
 
 from trytond.i18n import gettext, lazy_gettext
 from trytond.model import (
-    Index, ModelSQL, ModelView, Workflow, dualmethod, fields, sort)
+    ChatMixin, Index, ModelSQL, ModelView, Workflow, dualmethod, fields, sort)
 from trytond.model.exceptions import AccessError
 from trytond.modules.company import CompanyReport
 from trytond.modules.company.model import employee_field, set_employee
@@ -344,7 +344,8 @@
 
 
 class ShipmentIn(
-        ShipmentCheckQuantity, ShipmentMixin, Workflow, ModelSQL, ModelView):
+        ShipmentCheckQuantity, ShipmentMixin, Workflow, ModelSQL, ModelView,
+        ChatMixin):
     __name__ = 'stock.shipment.in'
 
     company = fields.Many2One(
@@ -658,6 +659,12 @@
         return ', '.join(set(filter(None,
                     (m.origin_name for m in self.incoming_moves))))
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.supplier.lang.code if self.supplier.lang else None
+        return language
+
     @classmethod
     def preprocess_values(cls, mode, values):
         pool = Pool()
@@ -789,7 +796,8 @@
         return self.inventory_moves
 
 
-class ShipmentInReturn(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
+class ShipmentInReturn(
+        ShipmentAssignMixin, Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = 'stock.shipment.in.return'
     _assign_moves_field = 'moves'
 
@@ -983,6 +991,12 @@
         return ', '.join(set(filter(None,
                     (m.origin_name for m in self.moves))))
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.supplier.lang.code if self.supplier.lang else None
+        return language
+
     @classmethod
     def copy(cls, shipments, default=None):
         if default is None:
@@ -1117,7 +1131,7 @@
 
 class ShipmentOut(
         ShipmentCheckQuantity, ShipmentAssignMixin, Workflow, ModelSQL,
-        ModelView):
+        ModelView, ChatMixin):
     __name__ = 'stock.shipment.out'
     _assign_moves_field = 'moves'
     company = fields.Many2One(
@@ -1453,6 +1467,12 @@
         return ', '.join(set(filter(None,
                     (m.origin_name for m in self.outgoing_moves))))
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.customer.lang.code if self.customer.lang else None
+        return language
+
     @classmethod
     @ModelView.button
     @Workflow.transition('draft')
@@ -1814,7 +1834,8 @@
 
 
 class ShipmentOutReturn(
-        ShipmentCheckQuantity, ShipmentMixin, Workflow, ModelSQL, ModelView):
+        ShipmentCheckQuantity, ShipmentMixin, Workflow, ModelSQL, ModelView,
+        ChatMixin):
     __name__ = 'stock.shipment.out.return'
 
     company = fields.Many2One(
@@ -2108,6 +2129,12 @@
         return ', '.join(set(filter(None,
                     (m.origin_name for m in self.incoming_moves))))
 
+    def chat_language(self, audience='internal'):
+        language = super().chat_language(audience=audience)
+        if audience == 'public':
+            language = self.customer.lang.code if self.customer.lang else None
+        return language
+
     @classmethod
     def preprocess_values(cls, mode, values):
         pool = Pool()
@@ -2238,7 +2265,7 @@
 
 class ShipmentInternal(
         ShipmentCheckQuantity, ShipmentAssignMixin, Workflow, ModelSQL,
-        ModelView):
+        ModelView, ChatMixin):
     __name__ = 'stock.shipment.internal'
     _assign_moves_field = 'moves'
     effective_start_date = fields.Date('Effective Start Date',
diff -r b4deb9f4d972 -r 773f7f1ba3d5 modules/stock_forecast/forecast.py
--- a/modules/stock_forecast/forecast.py        Tue Dec 02 13:08:05 2025 +0100
+++ b/modules/stock_forecast/forecast.py        Tue Dec 02 16:01:39 2025 +0100
@@ -12,7 +12,7 @@
 
 from trytond.i18n import gettext
 from trytond.model import (
-    Exclude, Index, ModelSQL, ModelView, Unique, Workflow, fields)
+    ChatMixin, Exclude, Index, ModelSQL, ModelView, Unique, Workflow, fields)
 from trytond.model.exceptions import AccessError
 from trytond.pool import Pool
 from trytond.pyson import Bool, Eval, If
@@ -23,7 +23,7 @@
 from trytond.wizard import Button, StateTransition, StateView, Wizard
 
 
-class Forecast(Workflow, ModelSQL, ModelView):
+class Forecast(Workflow, ModelSQL, ModelView, ChatMixin):
     __name__ = "stock.forecast"
 
     _states = {


Reply via email to