changeset c720259bb5b2 in modules/sale:default
details: https://hg.tryton.org/modules/sale?cmd=changeset;node=c720259bb5b2
description:
        Add ir.message and use custom exceptions

        issue3672
diffstat:

 exceptions.py |   21 +++++++++++
 invoice.py    |   20 +++------
 message.xml   |   46 ++++++++++++++++++++++++
 party.py      |   21 +++-------
 sale.py       |  108 ++++++++++++++++++++++++++-------------------------------
 stock.py      |   26 ++++---------
 tryton.cfg    |    1 +
 7 files changed, 140 insertions(+), 103 deletions(-)

diffs (461 lines):

diff -r a04f626934c1 -r c720259bb5b2 exceptions.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/exceptions.py     Sat Dec 29 14:20:29 2018 +0100
@@ -0,0 +1,21 @@
+# This file is part of Tryton.  The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+from trytond.exceptions import UserError
+from trytond.model.exceptions import ValidationError
+
+
+class SaleValidationError(ValidationError):
+    pass
+
+
+class SaleQuotationError(ValidationError):
+    pass
+
+
+class SaleConfirmError(UserError):
+    pass
+
+
+class PartyLocationError(UserError):
+    pass
diff -r a04f626934c1 -r c720259bb5b2 invoice.py
--- a/invoice.py        Sat Dec 22 00:16:15 2018 +0100
+++ b/invoice.py        Sat Dec 29 14:20:29 2018 +0100
@@ -1,9 +1,10 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
-from itertools import chain
 from functools import wraps
 
+from trytond.i18n import gettext
 from trytond.model import Workflow, fields
+from trytond.model.exceptions import AccessError
 from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
@@ -33,14 +34,6 @@
     sales = fields.Function(fields.One2Many('sale.sale', None, 'Sales'),
         'get_sales', searcher='search_sales')
 
-    @classmethod
-    def __setup__(cls):
-        super(Invoice, cls).__setup__()
-        cls._error_messages.update({
-                'reset_invoice_sale': ('You cannot reset to draft '
-                    'an invoice generated by a sale.'),
-                })
-
     def get_sale_exception_state(self, name):
         sales = self.sales
 
@@ -99,10 +92,11 @@
     @classmethod
     @Workflow.transition('draft')
     def draft(cls, invoices):
-        sales = list(chain(*(i.sales for i in invoices)))
-
-        if sales and any(i.state == 'cancel' for i in invoices):
-            cls.raise_user_error('reset_invoice_sale')
+        for invoice in invoices:
+            if invoice.sales and invoice.state == 'cancel':
+                raise AccessError(
+                    gettext('sale.msg_sale_invoice_reset_draft',
+                        invoice=invoice.rec_name))
 
         return super(Invoice, cls).draft(invoices)
 
diff -r a04f626934c1 -r c720259bb5b2 message.xml
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/message.xml       Sat Dec 29 14:20:29 2018 +0100
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<tryton>
+    <data group="1">
+        <record model="ir.message" id="msg_erase_party_pending_sale">
+            <field name="text">You cannot erase party "%(party)s" while they 
have pending sales with company "%(company)s".</field>
+        </record>
+        <record model="ir.message" id="msg_sale_invoice_reset_draft">
+            <field name="text">You cannot reset invoice "%(invoice)s" to draft 
because it was generated by a sale.</field>
+        </record>
+        <record model="ir.message" id="msg_sale_move_reset_draft">
+            <field name="text">You cannot reset move "%(move)s" to draft 
because it was generated by a sale.</field>
+        </record>
+        <record model="ir.message" id="msg_sale_invalid_method">
+            <field name="text">You cannot use together invoice 
"%(invoice_method)s" and shipment "%(shipment_method)s" on sale 
"%(sale)s".</field>
+        </record>
+        <record model="ir.message" 
id="msg_sale_invoice_address_required_for_quotation">
+            <field name="text">To get a quote for sale "%(sale)s" you must 
enter an invoice address.</field>
+        </record>
+        <record model="ir.message" 
id="msg_sale_shipment_address_required_for_quotation">
+            <field name="text">To get a quote for sale "%(sale)s" you must 
enter a shipment address.</field>
+        </record>
+        <record model="ir.message" 
id="msg_sale_warehouse_required_for_quotation">
+            <field name="text">To get a quote for sale "%(sale)s" you must 
enter a warehouse.</field>
+        </record>
+        <record model="ir.message" id="msg_sale_delete_cancel">
+            <field name="text">To delete sale "%(sale)s" you must cancel 
it.</field>
+        </record>
+        <record model="ir.message" id="msg_sale_customer_location_required">
+            <field name="text">To process sale "%(sale)s" you must set a 
customer location on party "%(party)s".</field>
+        </record>
+        <record model="ir.message" 
id="msg_sale_product_missing_account_expense">
+            <field name="text">To invoice sale "%(sale)s" you must define an 
account revenue for product "%(product)s".</field>
+        </record>
+        <record model="ir.message" id="msg_sale_missing_account_expense">
+            <field name="text">To invoice sale "%(sale)s" you must configure a 
default account revenue.</field>
+        </record>
+        <record model="ir.message" id="msg_sale_line_delete_cancel_draft">
+            <field name="text">To delete line "%(line)s" you must cancel or 
reset to draft sale "%(sale)s".</field>
+        </record>
+        <record model="ir.message" id="msg_sale_modify_header_draft">
+            <field name="text">To modify the header of sale "%(sale)s", it 
must be in "draft" state.</field>
+        </record>
+    </data>
+</tryton>
diff -r a04f626934c1 -r c720259bb5b2 party.py
--- a/party.py  Sat Dec 22 00:16:15 2018 +0100
+++ b/party.py  Sat Dec 29 14:20:29 2018 +0100
@@ -1,7 +1,10 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
+from trytond.i18n import gettext
 from trytond.pool import PoolMeta, Pool
 
+from trytond.modules.party.exceptions import EraseError
+
 __all__ = ['PartyReplace', 'PartyErase']
 
 
@@ -19,16 +22,6 @@
 class PartyErase(metaclass=PoolMeta):
     __name__ = 'party.erase'
 
-    @classmethod
-    def __setup__(cls):
-        super(PartyErase, cls).__setup__()
-        cls._error_messages.update({
-                'pending_sale': (
-                    'The party "%(party)s" can not be erased '
-                    'because he has pending sales '
-                    'for the company "%(company)s".'),
-                })
-
     def check_erase_company(self, party, company):
         pool = Pool()
         Sale = pool.get('sale.sale')
@@ -42,7 +35,7 @@
                 ('state', 'not in', ['done', 'cancel']),
                 ])
         if sales:
-            self.raise_user_error('pending_sale', {
-                    'party': party.rec_name,
-                    'company': company.rec_name,
-                    })
+            raise EraseError(
+                gettext('sale.msg_erase_party_pending_sale',
+                    party=party.rec_name,
+                    company=company.rec_name))
diff -r a04f626934c1 -r c720259bb5b2 sale.py
--- a/sale.py   Sat Dec 22 00:16:15 2018 +0100
+++ b/sale.py   Sat Dec 29 14:20:29 2018 +0100
@@ -5,8 +5,10 @@
 from itertools import groupby, chain
 from functools import partial
 
+from trytond.i18n import gettext
 from trytond.model import Workflow, Model, ModelView, ModelSQL, fields, \
     sequence_ordered
+from trytond.model.exceptions import AccessError
 from trytond.modules.company import CompanyReport
 from trytond.wizard import Wizard, StateAction, StateView, StateTransition, \
     Button
@@ -15,7 +17,10 @@
 from trytond.pool import Pool
 
 from trytond.modules.account.tax import TaxableMixin
+from trytond.modules.account_product.exceptions import AccountError
 from trytond.modules.product import price_digits
+from .exceptions import (
+    SaleValidationError, SaleQuotationError, PartyLocationError)
 
 __all__ = ['Sale', 'SaleIgnoredInvoice', 'SaleRecreatedInvoice',
     'SaleLine', 'SaleLineTax', 'SaleLineIgnoredMove',
@@ -175,6 +180,7 @@
             'readonly': Eval('state') != 'draft',
             },
         depends=['state'])
+    invoice_method_string = invoice_method.translated('invoice_method')
     invoice_state = fields.Selection([
             ('none', 'None'),
             ('waiting', 'Waiting'),
@@ -197,6 +203,7 @@
             'readonly': Eval('state') != 'draft',
             },
         depends=['state'])
+    shipment_method_string = shipment_method.translated('shipment_method')
     shipment_state = fields.Selection([
             ('none', 'None'),
             ('waiting', 'Waiting'),
@@ -222,17 +229,6 @@
             ('sale_date', 'DESC'),
             ('id', 'DESC'),
             ]
-        cls._error_messages.update({
-                'invalid_method': ('Invalid combination of shipment and '
-                    'invoicing methods on sale "%s".'),
-                'addresses_required': (
-                    'Invoice and Shipment addresses must be '
-                    'defined for the quotation of sale "%s".'),
-                'warehouse_required': ('Warehouse must be defined for the '
-                    'quotation of sale "%s".'),
-                'delete_cancel': ('Sale "%s" must be cancelled before '
-                    'deletion.'),
-                })
         cls._transitions |= set((
                 ('draft', 'quotation'),
                 ('quotation', 'confirmed'),
@@ -591,10 +587,18 @@
         '''
         if (self.invoice_method == 'shipment'
                 and self.shipment_method in ('invoice', 'manual')):
-            self.raise_user_error('invalid_method', (self.rec_name,))
+            raise SaleValidationError(
+                gettext('sale.msg_sale_invalid_method',
+                    invoice_method=self.invoice_method_string,
+                    shipment_method=self.shipment_method_string,
+                    sale=self.rec_name))
         if (self.shipment_method == 'invoice'
                 and self.invoice_method in ('shipment', 'manual')):
-            self.raise_user_error('invalid_method', (self.rec_name,))
+            raise SaleValidationError(
+                gettext('sale.msg_sale_invalid_method',
+                    invoice_method=self.invoice_method_string,
+                    shipment_method=self.shipment_method_string,
+                    sale=self.rec_name))
 
     def get_rec_name(self, name):
         items = []
@@ -650,8 +654,15 @@
         return super(Sale, cls).copy(sales, default=default)
 
     def check_for_quotation(self):
-        if not self.invoice_address or not self.shipment_address:
-            self.raise_user_error('addresses_required', (self.rec_name,))
+        if not self.invoice_address:
+            raise SaleQuotationError(
+                gettext('sale.msg_sale_invoice_address_required_for_quotation',
+                    sale=self.rec_name))
+        if not self.shipment_address:
+            raise SaleQuotationError(
+                gettext('sale'
+                    '.msg_sale_shipment_address_required_for_quotation',
+                    sale=self.rec_name))
         for line in self.lines:
             if (line.quantity or 0) >= 0:
                 location = line.from_location
@@ -660,8 +671,9 @@
             if ((not location or not line.warehouse)
                     and line.product
                     and line.product.type in ('goods', 'assets')):
-                self.raise_user_error('warehouse_required',
-                    (self.rec_name,))
+                raise SaleQuotationError(
+                    gettext('sale.msg_sale_warehouse_required_for_quotation',
+                        sale=self.rec_name))
 
     @classmethod
     def set_number(cls, sales):
@@ -816,7 +828,9 @@
         cls.cancel(sales)
         for sale in sales:
             if sale.state != 'cancel':
-                cls.raise_user_error('delete_cancel', (sale.rec_name,))
+                raise AccessError(
+                    gettext('sale.msg_sale_delete_cancel',
+                        sale=sale.rec_name))
         super(Sale, cls).delete(sales)
 
     @classmethod
@@ -1044,21 +1058,6 @@
         'on_change_with_sale_state')
 
     @classmethod
-    def __setup__(cls):
-        super(SaleLine, cls).__setup__()
-        cls._error_messages.update({
-                'customer_location_required': (
-                    'Sale "%(sale)s" is missing the '
-                    'customer location in line "%(line)s".'),
-                'missing_account_revenue': ('Product "%(product)s" of sale '
-                    '%(sale)s misses a revenue account.'),
-                'missing_default_account_revenue': ('Sale "%(sale)s" '
-                    'misses a default "account revenue".'),
-                'delete_cancel_draft': ('The line "%(line)s" must be on '
-                    'canceled or draft sale to be deleted.'),
-                })
-
-    @classmethod
     def __register__(cls, module_name):
         super(SaleLine, cls).__register__(module_name)
         table = cls.__table_handler__(module_name)
@@ -1316,17 +1315,17 @@
         if self.product:
             invoice_line.account = self.product.account_revenue_used
             if not invoice_line.account:
-                self.raise_user_error('missing_account_revenue', {
-                        'sale': self.sale.rec_name,
-                        'product': self.product.rec_name,
-                        })
+                raise AccountError(
+                    gettext('sale.msg_sale_product_missing_account_expense',
+                        sale=self.sale.rec_name,
+                        product=self.product.rec_name))
         else:
             invoice_line.account = account_config.get_multivalue(
                 'default_category_account_revenue')
             if not invoice_line.account:
-                self.raise_user_error('missing_default_account_revenue', {
-                        'sale': self.sale.rec_name,
-                        })
+                raise AccountError(
+                    gettext('sale.msg_sale_missing_account_expense',
+                        sale=self.sale.rec_name))
         invoice_line.stock_moves = self._get_invoice_line_moves()
         return [invoice_line]
 
@@ -1407,10 +1406,10 @@
             return
 
         if not self.sale.party.customer_location:
-            self.raise_user_error('customer_location_required', {
-                    'sale': self.sale.rec_name,
-                    'line': self.rec_name,
-                    })
+            raise PartyLocationError(
+                gettext('sale.msg_sale_customer_location_required',
+                    sale=self.sale.rec_name,
+                    party=self.sale.party.rec_name))
         move = Move()
         move.quantity = quantity
         move.uom = self.unit
@@ -1496,9 +1495,10 @@
     def delete(cls, lines):
         for line in lines:
             if line.sale_state not in {'cancel', 'draft'}:
-                cls.raise_user_error('delete_cancel_draft', {
-                        'line': line.rec_name,
-                        })
+                raise AccessError(
+                    gettext('sale.msg_sale_line_delete_cancel_draft',
+                        line=line.rec_name,
+                        sale=line.sale.rec_name))
         super(SaleLine, cls).delete(lines)
 
     @classmethod
@@ -1763,23 +1763,15 @@
             ])
     modify = StateTransition()
 
-    @classmethod
-    def __setup__(cls):
-        super(ModifyHeader, cls).__setup__()
-        cls._error_messages.update({
-                'not_in_draft': (
-                    'The sale "%(sale)s" must be in draft to modify header.'),
-                })
-
     def get_sale(self):
         pool = Pool()
         Sale = pool.get('sale.sale')
 
         sale = Sale(Transaction().context['active_id'])
         if sale.state != 'draft':
-            self.raise_user_error('not_in_draft', {
-                    'sale': sale.rec_name,
-                    })
+            raise AccessError(
+                gettext('sale.msg_sale_modify_header_draft',
+                    sale=sale.rec_name))
         return sale
 
     def default_start(self, fields):
diff -r a04f626934c1 -r c720259bb5b2 stock.py
--- a/stock.py  Sat Dec 22 00:16:15 2018 +0100
+++ b/stock.py  Sat Dec 29 14:20:29 2018 +0100
@@ -2,7 +2,9 @@
 # this repository contains the full copyright notices and license terms.
 from functools import wraps
 
+from trytond.i18n import gettext
 from trytond.model import Workflow, ModelView, fields
+from trytond.model.exceptions import AccessError
 from trytond.transaction import Transaction
 from trytond.pool import Pool, PoolMeta
 
@@ -29,14 +31,6 @@
     __name__ = 'stock.shipment.out'
 
     @classmethod
-    def __setup__(cls):
-        super(ShipmentOut, cls).__setup__()
-        cls._error_messages.update({
-                'reset_move': ('You cannot reset to draft a move generated '
-                    'by a sale.'),
-                })
-
-    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
     def draft(cls, shipments):
@@ -45,7 +39,9 @@
             for move in shipment.outgoing_moves:
                 if (move.state == 'cancel'
                         and isinstance(move.origin, SaleLine)):
-                    cls.raise_user_error('reset_move')
+                    raise AccessError(
+                        gettext('sale.msg_sale_move_reset_draft',
+                            move=move.rec_name))
 
         return super(ShipmentOut, cls).draft(shipments)
 
@@ -68,14 +64,6 @@
     __name__ = 'stock.shipment.out.return'
 
     @classmethod
-    def __setup__(cls):
-        super(ShipmentOutReturn, cls).__setup__()
-        cls._error_messages.update({
-                'reset_move': ('You cannot reset to draft a move generated '
-                    'by a sale.'),
-                })
-
-    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
     def draft(cls, shipments):
@@ -84,7 +72,9 @@
             for move in shipment.incoming_moves:
                 if (move.state == 'cancel'
                         and isinstance(move.origin, SaleLine)):
-                    cls.raise_user_error('reset_move')
+                    raise AccessError(
+                        gettext('sale.msg_sale_move_reset_draft',
+                            move=move.rec_name))
 
         return super(ShipmentOutReturn, cls).draft(shipments)
 
diff -r a04f626934c1 -r c720259bb5b2 tryton.cfg
--- a/tryton.cfg        Sat Dec 22 00:16:15 2018 +0100
+++ b/tryton.cfg        Sat Dec 29 14:20:29 2018 +0100
@@ -19,3 +19,4 @@
     party.xml
     stock.xml
     product.xml
+    message.xml

Reply via email to