changeset c05a19615e4c in modules/party:default details: https://hg.tryton.org/modules/party?cmd=changeset;node=c05a19615e4c description: Add ir.message and use custom exceptions
issue3672 diffstat: address.py | 22 ++++------ category.py | 2 +- contact_mechanism.py | 24 +++++------ exceptions.py | 28 ++++++++++++++ message.xml | 40 ++++++++++++++++++++ party.py | 81 +++++++++++++---------------------------- tests/scenario_party_erase.rst | 2 +- tryton.cfg | 1 + 8 files changed, 117 insertions(+), 83 deletions(-) diffs (399 lines): diff -r cba01f8cd768 -r c05a19615e4c address.py --- a/address.py Thu Dec 13 18:48:11 2018 +0100 +++ b/address.py Sat Dec 29 14:20:29 2018 +0100 @@ -7,13 +7,16 @@ from sql.conditionals import Case, Coalesce from sql.operators import Concat +from trytond.i18n import gettext from trytond.model import ( ModelView, ModelSQL, MatchMixin, DeactivableMixin, fields, sequence_ordered) +from trytond.model.exceptions import AccessError from trytond.pyson import Eval, If from trytond.pool import Pool from trytond.transaction import Transaction from trytond.cache import Cache +from .exceptions import InvalidFormat __all__ = ['Address', 'AddressFormat'] @@ -53,9 +56,6 @@ def __setup__(cls): super(Address, cls).__setup__() cls._order.insert(0, ('party', 'ASC')) - cls._error_messages.update({ - 'write_party': 'You can not modify the party of address "%s".', - }) @classmethod def __register__(cls, module_name): @@ -204,8 +204,9 @@ if 'party' in values: for address in addresses: if address.party.id != values['party']: - cls.raise_user_error( - 'write_party', (address.rec_name,)) + raise AccessError( + gettext('party.msg_address_change_party', + address=address.rec_name)) super(Address, cls).write(*args) @fields.depends('subdivision', 'country') @@ -240,10 +241,6 @@ super(AddressFormat, cls).__setup__() cls._order.insert(0, ('country', 'ASC')) cls._order.insert(1, ('language', 'ASC')) - cls._error_messages.update({ - 'invalid_format': ('Invalid format "%(format)s" ' - 'with exception "%(exception)s".'), - }) @classmethod def default_format_(cls): @@ -289,10 +286,9 @@ Template(self.format_).substitute( **address._get_address_substitutions()) except Exception as exception: - self.raise_user_error('invalid_format', { - 'format': self.format_, - 'exception': exception, - }) + raise InvalidFormat(gettext('party.invalid_format', + format=self.format_, + exception=exception)) from exception @classmethod def get_format(cls, address, pattern=None): diff -r cba01f8cd768 -r c05a19615e4c category.py --- a/category.py Thu Dec 13 18:48:11 2018 +0100 +++ b/category.py Sat Dec 29 14:20:29 2018 +0100 @@ -35,7 +35,7 @@ cls._sql_constraints = [ ('name_parent_exclude', Exclude(t, (t.name, Equal), (Coalesce(t.parent, -1), Equal)), - 'The name of a party category must be unique by parent.'), + 'party.msg_category_name_unique'), ] cls._order.insert(0, ('name', 'ASC')) diff -r cba01f8cd768 -r c05a19615e4c contact_mechanism.py --- a/contact_mechanism.py Thu Dec 13 18:48:11 2018 +0100 +++ b/contact_mechanism.py Sat Dec 29 14:20:29 2018 +0100 @@ -7,9 +7,12 @@ except ImportError: phonenumbers = None +from trytond.i18n import gettext from trytond.model import ( ModelView, ModelSQL, DeactivableMixin, fields, sequence_ordered) +from trytond.model.exceptions import AccessError from trytond.pyson import Eval +from .exceptions import InvalidPhoneNumber __all__ = ['ContactMechanism'] @@ -93,12 +96,6 @@ def __setup__(cls): super(ContactMechanism, cls).__setup__() cls._order.insert(0, ('party', 'ASC')) - cls._error_messages.update({ - 'write_party': ('You can not modify the party of contact ' - 'mechanism "%s".'), - 'invalid_phonenumber': ('The phone number "%(phone)s" of ' - 'party "%(party)s" is not valid .'), - }) @staticmethod def default_type(): @@ -239,8 +236,11 @@ if 'party' in values: for mechanism in mechanisms: if mechanism.party.id != values['party']: - cls.raise_user_error( - 'write_party', (mechanism.rec_name,)) + raise AccessError( + gettext('party' + '.msg_contact_mechanism_change_party') % { + 'contact': mechanism.rec_name, + }) super(ContactMechanism, cls).write(*args) cls._format_values(all_mechanisms) @@ -255,11 +255,9 @@ return phonenumber = self._parse_phonenumber(self.value) if not (phonenumber and phonenumbers.is_valid_number(phonenumber)): - self.raise_user_error( - 'invalid_phonenumber', { - 'phone': self.value, - 'party': self.party.rec_name - }) + raise InvalidPhoneNumber( + gettext('party.msg_invalid_phone_number', + phone=self.value, party=self.party.rec_name)) @classmethod def usages(cls, _fields=None): diff -r cba01f8cd768 -r c05a19615e4c 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,28 @@ +# 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, UserWarning +from trytond.model.exceptions import ValidationError + + +class InvalidIdentifierCode(ValidationError): + pass + + +class InvalidPhoneNumber(ValidationError): + pass + + +class VIESUnavailable(UserError): + pass + + +class SimilarityWarning(UserWarning): + pass + + +class EraseError(ValidationError): + pass + + +class InvalidFormat(ValidationError): + pass diff -r cba01f8cd768 -r c05a19615e4c 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,40 @@ +<?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_party_code_unique"> + <field name="text">The code on party must be unique.</field> + </record> + <record model="ir.message" id="msg_contact_mechanism_change_party"> + <field name="text">You cannot change the party of contact mechanism "%(contact)s".</field> + </record> + <record model="ir.message" id="msg_invalid_phone_number"> + <field name="text">The phone number "%(phone)s" for party "%(party)s" is not valid.</field> + </record> + <record model="ir.message" id="msg_invalid_vat_number"> + <field name="text">The VAT number "%(code)s" for party "%(party)s" is not valid.</field> + </record> + <record model="ir.message" id="msg_vies_unavailable"> + <field name="text">The VIES service is unavailable, try again later.</field> + </record> + <record model="ir.message" id="msg_different_name"> + <field name="text">Parties have different names: "%(source_name)s" vs "%(destination_name)s".</field> + </record> + <record model="ir.message" id="msg_different_tax_identifier"> + <field name="text">Parties have different tax identifiers: "%(source_code)s" vs "%(destination_code)s".</field> + </record> + <record model="ir.message" id="msg_erase_active_party"> + <field name="text">Party "%(party)s" cannot be erased because they are still active.</field> + </record> + <record model="ir.message" id="msg_address_change_party"> + <field name="text">You cannot change the party of address "%(address)s".</field> + </record> + <record model="ir.message" id="msg_invalid_format"> + <field name="text">Invalid format "%(format)s" with exception "%(exception)s".</field> + </record> + <record model="ir.message" id="msg_category_name_unique"> + <field name="text">The name of party category must be unique by parent.</field> + </record> + </data> +</tryton> diff -r cba01f8cd768 -r c05a19615e4c party.py --- a/party.py Thu Dec 13 18:48:11 2018 +0100 +++ b/party.py Sat Dec 29 14:20:29 2018 +0100 @@ -5,6 +5,7 @@ from sql import Null, Column, Literal from sql.functions import CharLength, Substring, Position +from trytond.i18n import gettext from trytond.model import (ModelView, ModelSQL, MultiValueMixin, ValueMixin, DeactivableMixin, fields, Unique, sequence_ordered) from trytond.wizard import Wizard, StateTransition, StateView, Button @@ -13,6 +14,8 @@ from trytond.pool import Pool from trytond import backend from trytond.tools.multivalue import migrate_property +from .exceptions import ( + InvalidIdentifierCode, VIESUnavailable, SimilarityWarning, EraseError) __all__ = ['Party', 'PartyLang', 'PartyCategory', 'PartyIdentifier', 'CheckVIESResult', 'CheckVIES', @@ -76,9 +79,8 @@ super(Party, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ - ('code_uniq', Unique(t, t.code), - 'The code of the party must be unique.') - ] + ('code_uniq', Unique(t, t.code), 'party.msg_party_code_unique') + ] cls._order.insert(0, ('name', 'ASC')) cls.active.states.update({ 'readonly': Bool(Eval('replaced_by')), @@ -318,14 +320,6 @@ code = fields.Char('Code', required=True) @classmethod - def __setup__(cls): - super(PartyIdentifier, cls).__setup__() - cls._error_messages.update({ - 'invalid_vat': ('Invalid VAT number "%(code)s" ' - 'on party "%(party)s".'), - }) - - @classmethod def __register__(cls, module_name): pool = Pool() Party = pool.get('party.party') @@ -376,10 +370,9 @@ party = self.party.rec_name else: party = '' - self.raise_user_error('invalid_vat', { - 'code': self.code, - 'party': party, - }) + raise InvalidIdentifierCode( + gettext('party.msg_invalid_vat_number', + code=self.code, party=party)) class CheckVIESResult(ModelView): @@ -406,14 +399,6 @@ Button('OK', 'end', 'tryton-ok', True), ]) - @classmethod - def __setup__(cls): - super(CheckVIES, cls).__setup__() - cls._error_messages.update({ - 'vies_unavailable': ('The VIES service is unavailable, ' - 'try again later.'), - }) - def transition_check(self): Party = Pool().get('party.party') @@ -439,7 +424,8 @@ or e.faultstring.find('MS_UNAVAILABLE') \ or e.faultstring.find('TIMEOUT') \ or e.faultstring.find('SERVER_BUSY'): - self.raise_user_error('vies_unavailable') + raise VIESUnavailable( + gettext('party.msg_vies_unavailable')) from e raise self.result.parties_succeed = parties_succeed self.result.parties_failed = parties_failed @@ -462,26 +448,19 @@ ]) replace = StateTransition() - @classmethod - def __setup__(cls): - super(PartyReplace, cls).__setup__() - cls._error_messages.update({ - 'different_name': ("Parties have different names: " - "%(source_name)s vs %(destination_name)s."), - 'different_tax_identifier': ( - "Parties have different Tax Identifier: " - "%(source_code)s vs %(destination_code)s."), - }) - def check_similarity(self): + pool = Pool() + Warning = pool.get('res.user.warning') source = self.ask.source destination = self.ask.destination if source.name != destination.name: key = 'party.replace name %s %s' % (source.id, destination.id) - self.raise_user_warning(key, 'different_name', { - 'source_name': source.name, - 'destination_name': destination.name, - }) + if Warning.check(key): + raise SimilarityWarning( + key, + gettext('party.msg_different_name', + source_name=source.name, + destination_name=destination.name)) source_code = (source.tax_identifier.code if source.tax_identifier else '') destination_code = (destination.tax_identifier.code @@ -489,10 +468,12 @@ if source_code != destination_code: key = 'party.replace tax_identifier %s %s' % ( source.id, destination.id) - self.raise_user_warning(key, 'different_tax_identifier', { - 'source_code': source_code, - 'destination_code': destination_code, - }) + if Warning.check(key): + raise SimilarityWarning( + key, + gettext('party.msg_different_tax_identifier', + source_code=source_code, + destination_code=destination_code)) def transition_replace(self): pool = Pool() @@ -582,15 +563,6 @@ ]) erase = StateTransition() - @classmethod - def __setup__(cls): - super(PartyErase, cls).__setup__() - cls._error_messages.update({ - 'active_party': ( - 'The party "%(party)s" can not be erased ' - 'because he is still active.'), - }) - def transition_erase(self): pool = Pool() Party = pool.get('party.party') @@ -662,9 +634,8 @@ def check_erase(self, party): if party.active: - self.raise_user_error('active_party', { - 'party': party.rec_name, - }) + raise EraseError(gettext('party.msg_erase_active_party', + party=party.rec_name)) def to_erase(self, party_id): pool = Pool() diff -r cba01f8cd768 -r c05a19615e4c tests/scenario_party_erase.rst --- a/tests/scenario_party_erase.rst Thu Dec 13 18:48:11 2018 +0100 +++ b/tests/scenario_party_erase.rst Sat Dec 29 14:20:29 2018 +0100 @@ -38,7 +38,7 @@ >>> erase.execute('erase') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... - UserError: ... + EraseError: ... Erase inactive party:: diff -r cba01f8cd768 -r c05a19615e4c tryton.cfg --- a/tryton.cfg Thu Dec 13 18:48:11 2018 +0100 +++ b/tryton.cfg Sat Dec 29 14:20:29 2018 +0100 @@ -10,3 +10,4 @@ address.xml contact_mechanism.xml configuration.xml + message.xml