diff --git a/exceptions.py b/exceptions.py new file mode 100644 index 0000000000..164cf4bfb2 --- /dev/null +++ b/exceptions.py @@ -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 AssignError(UserError): + pass + + +class LocationValidationError(ValidationError): + pass + + +class PeriodCloseError(UserError): + pass + + +class InventoryValidationError(ValidationError): + pass + + +class InventoryCountWarning(UserWarning): + pass + + +class MoveOriginWarning(UserWarning): + pass diff --git a/inventory.py b/inventory.py index 6598e68900..3db6e96308 100644 --- a/inventory.py +++ b/inventory.py @@ -2,12 +2,16 @@ # this repository contains the full copyright notices and license terms. from sql import Null +from trytond.i18n import gettext from trytond.model import Workflow, Model, ModelView, ModelSQL, fields, Check +from trytond.model.exceptions import AccessError from trytond.pyson import Eval, Bool, If from trytond.transaction import Transaction from trytond.pool import Pool from trytond.wizard import Wizard, StateView, StateTransition, Button +from .exceptions import InventoryValidationError, InventoryCountWarning + __all__ = ['Inventory', 'InventoryLine', 'Count', 'CountSearch', 'CountQuantity'] @@ -72,12 +76,6 @@ class Inventory(Workflow, ModelSQL, ModelView): def __setup__(cls): super(Inventory, cls).__setup__() cls._order.insert(0, ('date', 'DESC')) - cls._error_messages.update({ - 'delete_cancel': ('Inventory "%s" must be canceled before ' - 'deletion.'), - 'unique_line': ('Line "%s" is not unique ' - 'on Inventory "%s".'), - }) cls._transitions |= set(( ('draft', 'done'), ('draft', 'cancel'), @@ -136,7 +134,9 @@ class Inventory(Workflow, ModelSQL, ModelView): cls.cancel(inventories) for inventory in inventories: if inventory.state != 'cancel': - cls.raise_user_error('delete_cancel', inventory.rec_name) + raise AccessError( + gettext('stock.msg_inventory_delete_cancel', + inventory=inventory.rec_name)) super(Inventory, cls).delete(inventories) @classmethod @@ -150,8 +150,10 @@ class Inventory(Workflow, ModelSQL, ModelView): for line in inventory.lines: key = line.unique_key if key in keys: - cls.raise_user_error('unique_line', - (line.rec_name, inventory.rec_name)) + raise InventoryValidationError( + gettext('stock.msg_inventory_line_unique', + line=line.rec_name, + inventory=inventory.rec_name)) keys.add(key) move = line.get_move() if move: @@ -330,15 +332,9 @@ class InventoryLine(ModelSQL, ModelView): t = cls.__table__() cls._sql_constraints += [ ('check_line_qty_pos', Check(t, t.quantity >= 0), - 'Line quantity must be positive.'), + 'stock.msg_inventory_line_quantity_positive'), ] cls._order.insert(0, ('product', 'ASC')) - cls._error_messages.update({ - 'missing_empty_quantity': ('An option for empty quantity is ' - 'missing for inventory "%(inventory)s".'), - 'delete_cancel_draft': ('The line "%(line)s" must be on ' - 'canceled or draft inventory to be deleted.'), - }) @classmethod def __register__(cls, module_name): @@ -428,9 +424,9 @@ class InventoryLine(ModelSQL, ModelView): qty = self.quantity if qty is None: if self.inventory.empty_quantity is None: - self.raise_user_error('missing_empty_quantity', { - 'inventory': self.inventory.rec_name, - }) + raise InventoryValidationError( + gettext('stock.msg_inventory_missing_empty_quantity', + inventory=self.inventory.rec_name)) if self.inventory.empty_quantity == 'keep': return else: @@ -483,9 +479,10 @@ class InventoryLine(ModelSQL, ModelView): def delete(cls, lines): for line in lines: if line.inventory_state not in {'cancel', 'draft'}: - cls.raise_user_error('delete_cancel_draft', { - 'line': line.rec_name, - }) + raise AccessError( + gettext('stock.msg_inventory_line_delete_cancel', + line=line.rec_name, + inventory=line.inventory.rec_name)) super(InventoryLine, cls).delete(lines) @@ -508,17 +505,11 @@ class Count(Wizard): ]) add = StateTransition() - @classmethod - def __setup__(cls): - super(Count, cls).__setup__() - cls._error_messages.update({ - 'create_line': "No existing line found for %(search)s.", - }) - def default_quantity(self, fields): pool = Pool() Inventory = pool.get('stock.inventory') InventoryLine = pool.get('stock.inventory.line') + Warning = pool.get('res.user.warning') context = Transaction().context inventory = Inventory(context['active_id']) values = {} @@ -526,9 +517,10 @@ class Count(Wizard): if not lines: warning_name = '%s.%s.count_create' % ( inventory, self.search.search) - self.raise_user_warning(warning_name, 'create_line', { - 'search': self.search.search.rec_name, - }) + if Warning.check(warning_name): + raise InventoryCountWarning(warning_name, + gettext('stock.msg_inventory_count_create_line', + search=self.search.search.rec_name)) line, = InventoryLine.create([self.get_line_values(inventory)]) else: line, = lines diff --git a/location.py b/location.py index e16243972f..1afac43220 100644 --- a/location.py +++ b/location.py @@ -4,6 +4,7 @@ import datetime import operator from decimal import Decimal +from trytond.i18n import gettext from trytond.model import ( ModelView, ModelSQL, MatchMixin, ValueMixin, DeactivableMixin, fields, sequence_ordered, tree) @@ -14,6 +15,8 @@ from trytond.pool import Pool, PoolMeta from trytond.tools import grouped_slice from trytond.tools.multivalue import migrate_property +from .exceptions import LocationValidationError + __all__ = ['Location', 'Party', 'PartyLocation', 'ProductsByLocationsContext', 'LocationLeadTime'] @@ -47,6 +50,7 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): ('drop', 'Drop'), ('view', 'View'), ], 'Location type', states=STATES, depends=DEPENDS) + type_string = type.translated('type') parent = fields.Many2One("stock.location", "Parent", select=True, left="left", right="right", states={ @@ -133,16 +137,6 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): def __setup__(cls): super(Location, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) - cls._error_messages.update({ - 'invalid_type_for_moves': ('Location "%s" with existing moves ' - 'cannot be changed to a type that does not support moves.' - ), - 'child_of_warehouse': ('Location "%(location)s" must be a ' - 'child of warehouse "%(warehouse)s".'), - 'inactive_location_with_moves': ( - "The location '%(location)s' must be empty " - "to be deactivated."), - }) parent_domain = [ ['OR', @@ -220,8 +214,10 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): ('state', 'not in', ['staging', 'draft']), ]) if moves: - self.raise_user_error( - 'invalid_type_for_moves', (self.rec_name,)) + raise LocationValidationError( + gettext('stock.msg_location_invalid_type_for_moves', + location=self.rec_name, + type=self.type_string)) @classmethod def check_inactive(cls, locations): @@ -230,9 +226,9 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): empty = cls.get_empty_locations(locations) non_empty = set(locations) - set(empty) if non_empty: - cls.raise_user_error('inactive_location_with_moves', { - 'location': next(iter(non_empty)).rec_name, - }) + raise LocationValidationError( + gettext('stock.msg_location_inactive_not_empty', + location=next(iter(non_empty)).rec_name)) @classmethod def get_empty_locations(cls, locations=None): @@ -466,10 +462,10 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): ('parent', 'child_of', warehouse.id), ])) if location not in childs: - cls.raise_user_error('child_of_warehouse', { - 'location': location.rec_name, - 'warehouse': warehouse.rec_name, - }) + raise LocationValidationError( + gettext('stock.msg_location_child_of_warehouse', + location=location.rec_name, + warehouse=warehouse.rec_name)) @classmethod def copy(cls, locations, default=None): diff --git a/message.xml b/message.xml new file mode 100644 index 0000000000..e60a882467 --- /dev/null +++ b/message.xml @@ -0,0 +1,80 @@ + + + + + + You cannot change the default UOM of a product which is associated to stock moves. + + + You cannot change the type of a product which is associated to stock moves. + + + You cannot change the type of location "%(location)s" to "%(type)s" +because the type does not support moves and location has existing moves. + + + To set location "%(location)s" as storage/input/output of warehouse "%(warehouse)s", it must be in the warehouse. + + + To inactivate location "%(location)s", you must empty it. + + + You cannot close periods with a date in the future or today. + + + You cannot close periods with assigned moves in it. + + + To delete shipment "%(shipment)s" you must cancel it. + + + To delete inventory "%(inventory)s" you must cancel it. + + + To confirm the inventory "%(inventory)s" you must select an option for empty quantity. + + + Inventory line "%(line)s" is not unique on inventory "%(inventory)s". + + + Inventory line quantity must be positive. + + + To delete inventory line "%(line)s" you must cancel inventory "%(inventory)s". + + + No existing line found for "%(search)s". + + + You cannot erase party "%(party)s" while they have pending shipments with company "%(company)s". + + + To delete stock move "%(move)s" you must cancel it or reset its state to draft. + + + To modify stock move "%(move)s" you must reopen period "%(period)s". + + + To modify stock move "%(move)s" you must reset its state to draft. + + + You cannot modify stock move "%(move)s" because it is done. + + + You cannot modify stock move "%(move)s" because it is cancelled. + + + Stock moves "%(moves)s" have no origin. + + + Move quantity must be positive. + + + Internal move quantity must be positive. + + + The source and destination of stock move must be different. + + + diff --git a/move.py b/move.py index c0d40ce801..4d536626b7 100644 --- a/move.py +++ b/move.py @@ -11,7 +11,9 @@ from sql import Literal, Union, Column, Null, For from sql.aggregate import Sum from sql.conditionals import Coalesce, Case +from trytond.i18n import gettext from trytond.model import Workflow, Model, ModelView, ModelSQL, fields, Check +from trytond.model.exceptions import AccessError from trytond.pyson import Eval, If, Bool from trytond.tools import reduce_ids from trytond.transaction import Transaction @@ -19,6 +21,8 @@ from trytond.pool import Pool from trytond.modules.product import price_digits +from .exceptions import MoveOriginWarning + __all__ = ['StockMixin', 'Move'] STATES = { @@ -265,31 +269,15 @@ class Move(Workflow, ModelSQL, ModelView): t = cls.__table__() cls._sql_constraints += [ ('check_move_qty_pos', Check(t, t.quantity >= 0), - 'Move quantity must be positive'), + 'stock.msg_move_quantity_positive'), ('check_move_internal_qty_pos', Check(t, t.internal_quantity >= 0), - 'Internal move quantity must be positive'), + 'stock.msg_move_internal_quantity_positive'), ('check_from_to_locations', Check(t, t.from_location != t.to_location), - 'Source and destination location must be different'), + 'stock.msg_move_from_to_location'), ] cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'set_state_draft': ('You can not set stock move "%s" to draft ' - 'state.'), - 'set_state_assigned': ('You can not set stock move "%s" to ' - 'assigned state.'), - 'set_state_done': 'You can not set stock move "%s" to done state.', - 'delele_state': ('You can not delete stock move "%s" because ' - 'it is not in staging, draft nor cancelled state.'), - 'period_closed': ('You can not modify move "%(move)s" because ' - 'period "%(period)s" is closed.'), - 'modify_assigned': ('You can not modify stock move "%s" because ' - 'it is in "Assigned" state.'), - 'modify_done_cancel': ('You can not modify stock move "%s" ' - 'because it is in "Done" or "Cancel" state.'), - 'no_origin': 'The stock moves "%s" have no origin.', - }) cls._transitions |= set(( ('staging', 'draft'), ('staging', 'cancel'), @@ -492,10 +480,10 @@ class Move(Workflow, ModelSQL, ModelView): date = (move.effective_date if move.effective_date else move.planned_date) if date and date <= period.date: - cls.raise_user_error('period_closed', { - 'move': move.rec_name, - 'period': period.rec_name, - }) + raise AccessError( + gettext('stock.msg_move_modify_period_close', + move=move.rec_name, + period=period.rec_name)) def get_rec_name(self, name): return ("%s%s %s" @@ -701,12 +689,15 @@ class Move(Workflow, ModelSQL, ModelView): if cls._deny_modify_assigned & vals_set: for move in moves: if move.state == 'assigned': - cls.raise_user_error('modify_assigned', move.rec_name) + raise AccessError( + gettext('stock.msg_move_modify_assigned', + move=move.rec_name)) if cls._deny_modify_done_cancel & vals_set: for move in moves: if move.state in ('done', 'cancel'): - cls.raise_user_error('modify_done_cancel', - (move.rec_name,)) + raise AccessError( + gettext('stock.msg_move_modify_%s' % move.state, + move=move.rec_name)) super(Move, cls).write(*args) @@ -731,7 +722,9 @@ class Move(Workflow, ModelSQL, ModelView): def delete(cls, moves): for move in moves: if move.state not in {'staging', 'draft', 'cancel'}: - cls.raise_user_error('delele_state', (move.rec_name,)) + raise AccessError( + gettext('stock.msg_move_delete_draft_cancel', + move=move.rec_name)) super(Move, cls).delete(moves) @staticmethod @@ -741,6 +734,8 @@ class Move(Workflow, ModelSQL, ModelView): @classmethod def check_origin(cls, moves, types=None): + pool = Pool() + Warning = pool.get('res.user.warning') if types is None: types = cls.check_origin_types() if not types: @@ -757,7 +752,10 @@ class Move(Workflow, ModelSQL, ModelView): names += '...' warning_name = '%s.done' % hashlib.md5( str(moves).encode('utf-8')).hexdigest() - cls.raise_user_warning(warning_name, 'no_origin', names) + if Warning.check(warning_name): + raise MoveOriginWarning(warning_name, + gettext('stock.msg_move_no_origin', + moves=names)) def pick_product(self, location_quantities): """ diff --git a/party.py b/party.py index 759d3ece76..581790259e 100644 --- a/party.py +++ b/party.py @@ -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'] @@ -21,16 +24,6 @@ class PartyReplace(metaclass=PoolMeta): class PartyErase(metaclass=PoolMeta): __name__ = 'party.erase' - @classmethod - def __setup__(cls): - super(PartyErase, cls).__setup__() - cls._error_messages.update({ - 'pending_shipment': ( - 'The party "%(party)s" can not be erased ' - 'because he has pending shipments ' - 'for the company "%(company)s".'), - }) - def check_erase_company(self, party, company): pool = Pool() ShipmentIn = pool.get('stock.shipment.in') @@ -50,7 +43,7 @@ class PartyErase(metaclass=PoolMeta): ('state', 'not in', ['done', 'cancel']), ]) if shipments: - self.raise_user_error('pending_shipment', { - 'party': party.rec_name, - 'company': company.rec_name, - }) + raise EraseError( + gettext('stock.msg_erase_party_shipment', + party=party.rec_name, + company=company.rec_name)) diff --git a/period.py b/period.py index fdf65931f6..57b180302d 100644 --- a/period.py +++ b/period.py @@ -4,12 +4,15 @@ from itertools import chain from sql import Literal, For +from trytond.i18n import gettext from trytond.model import Workflow, ModelView, ModelSQL, fields from trytond.pyson import Eval, If from trytond.transaction import Transaction from trytond.pool import Pool from trytond.tools import grouped_slice +from .exceptions import PeriodCloseError + __all__ = ['Period', 'Cache'] @@ -37,13 +40,6 @@ class Period(Workflow, ModelSQL, ModelView): @classmethod def __setup__(cls): super(Period, cls).__setup__() - cls._error_messages.update({ - 'close_period_future_today': ('You can not close a period ' - 'in the future or today.'), - 'close_period_assigned_move': ( - 'You can not close a period when ' - 'there still are assigned moves.'), - }) cls._transitions |= set(( ('draft', 'closed'), ('closed', 'draft'), @@ -125,7 +121,8 @@ class Period(Workflow, ModelSQL, ModelView): recent_date = max(period.date for period in periods) if recent_date >= today: - cls.raise_user_error('close_period_future_today') + raise PeriodCloseError( + gettext('stock.msg_period_close_date')) if Move.search([ ('state', '=', 'assigned'), ['OR', [ @@ -134,7 +131,8 @@ class Period(Workflow, ModelSQL, ModelView): ], ('effective_date', '<=', recent_date), ]]): - cls.raise_user_error('close_period_assigned_move') + raise PeriodCloseError( + gettext('stock.msg_period_close_assigned_move')) for grouping in cls.groupings(): Cache = cls.get_cache(grouping) diff --git a/product.py b/product.py index 9902c4cdeb..e647dc5853 100644 --- a/product.py +++ b/product.py @@ -10,7 +10,9 @@ from sql.aggregate import Max from sql.functions import CurrentTimestamp from sql.conditionals import Coalesce +from trytond.i18n import gettext from trytond.model import ModelSQL, ModelView, fields +from trytond.model.exceptions import AccessError from trytond.wizard import Wizard, StateTransition from trytond.pyson import Eval, Or from trytond.transaction import Transaction @@ -41,7 +43,7 @@ def check_no_move(func): ], limit=1, order=[]) if moves: - return True + return moves return False @functools.wraps(func) @@ -53,10 +55,10 @@ def check_no_move(func): and transaction.context.get('_check_access')): actions = iter(args) for records, values in zip(actions, actions): - for field, error in Template._modify_no_move: + for field, msg in Template._modify_no_move: if field in values: if find_moves(cls, records): - Template.raise_user_error(error) + raise AccessError(gettext(msg)) # No moves for those records break func(cls, *args) @@ -86,19 +88,13 @@ class Template(metaclass=PoolMeta): @classmethod def __setup__(cls): super(Template, cls).__setup__() - cls._error_messages.update({ - 'change_default_uom': ('You cannot change the default uom for ' - 'a product which is associated to stock moves.'), - 'change_type': ('You cannot change the type for a product ' - 'which is associated to stock moves.'), - }) cls.cost_price.states['required'] = Or( cls.cost_price.states.get('required', True), Eval('type').in_(['goods', 'assets'])) cls.cost_price.depends.append('type') cls._modify_no_move = [ - ('default_uom', 'change_default_uom'), - ('type', 'change_type'), + ('default_uom', 'stock.msg_product_change_default_uom'), + ('type', 'stock.msg_product_change_type'), ] @classmethod diff --git a/shipment.py b/shipment.py index cb35cbd9c7..5eb3cb112e 100644 --- a/shipment.py +++ b/shipment.py @@ -7,7 +7,9 @@ from functools import partial from sql import Null +from trytond.i18n import gettext from trytond.model import Workflow, ModelView, ModelSQL, fields +from trytond.model.exceptions import AccessError from trytond.modules.company import CompanyReport from trytond.wizard import Wizard, StateTransition, StateView, Button from trytond.pyson import Eval, If, Id, Bool @@ -183,14 +185,6 @@ class ShipmentIn(Workflow, ModelSQL, ModelView): def __setup__(cls): super(ShipmentIn, cls).__setup__() cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'incoming_move_input_dest': ('Incoming Moves must have ' - 'the warehouse input location as destination location.'), - 'inventory_move_input_source': ('Inventory Moves must ' - 'have the warehouse input location as source location.'), - 'delete_cancel': ('Supplier Shipment "%s" must be cancelled ' - 'before deletion.'), - }) cls._transitions |= set(( ('draft', 'received'), ('received', 'done'), @@ -426,7 +420,9 @@ class ShipmentIn(Workflow, ModelSQL, ModelView): cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': - cls.raise_user_error('delete_cancel', shipment.rec_name) + raise AccessError( + gettext('stock.msg_shipment_delete_cancel', + shipment=shipment.rec_name)) Move.delete([m for s in shipments for m in s.moves]) super(ShipmentIn, cls).delete(shipments) @@ -575,10 +571,6 @@ class ShipmentInReturn(Workflow, ModelSQL, ModelView): def __setup__(cls): super(ShipmentInReturn, cls).__setup__() cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'delete_cancel': ('Supplier Return Shipment "%s" must be ' - 'cancelled before deletion.'), - }) cls._transitions |= set(( ('draft', 'waiting'), ('waiting', 'assigned'), @@ -702,7 +694,9 @@ class ShipmentInReturn(Workflow, ModelSQL, ModelView): cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': - cls.raise_user_error('delete_cancel', shipment.rec_name) + raise AccessError( + gettext('stock.msg_shipment_delete_cancel', + shipment=shipment.rec_name)) Move.delete([m for s in shipments for m in s.moves]) super(ShipmentInReturn, cls).delete(shipments) @@ -921,10 +915,6 @@ class ShipmentOut(Workflow, ModelSQL, ModelView): def __setup__(cls): super(ShipmentOut, cls).__setup__() cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'delete_cancel': ('Customer Shipment "%s" must be cancelled ' - 'before deletion.'), - }) cls._transitions |= set(( ('draft', 'waiting'), ('waiting', 'assigned'), @@ -1338,7 +1328,9 @@ class ShipmentOut(Workflow, ModelSQL, ModelView): cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': - cls.raise_user_error('delete_cancel', shipment.rec_name) + raise AccessError( + gettext('stock.msg_shipment_delete_cancel', + shipment=shipment.rec_name)) Move.delete([m for s in shipments for m in s.moves]) super(ShipmentOut, cls).delete(shipments) @@ -1494,10 +1486,6 @@ class ShipmentOutReturn(Workflow, ModelSQL, ModelView): def __setup__(cls): super(ShipmentOutReturn, cls).__setup__() cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'delete_cancel': ('Customer Return Shipment "%s" must be ' - 'cancelled before deletion.'), - }) cls._transitions |= set(( ('draft', 'received'), ('received', 'done'), @@ -1689,7 +1677,9 @@ class ShipmentOutReturn(Workflow, ModelSQL, ModelView): cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': - cls.raise_user_error('delete_cancel', shipment.rec_name) + raise AccessError( + gettext('stock.msg_shipment_delete_cancel', + shipment=shipment.rec_name)) Move.delete([m for s in shipments for m in s.moves]) super(ShipmentOutReturn, cls).delete(shipments) @@ -1994,10 +1984,6 @@ class ShipmentInternal(Workflow, ModelSQL, ModelView): def __setup__(cls): super(ShipmentInternal, cls).__setup__() cls._order[0] = ('id', 'DESC') - cls._error_messages.update({ - 'delete_cancel': ('Internal Shipment "%s" must be cancelled ' - 'before deletion.'), - }) cls._transitions |= set(( ('request', 'draft'), ('draft', 'waiting'), @@ -2166,7 +2152,9 @@ class ShipmentInternal(Workflow, ModelSQL, ModelView): cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': - cls.raise_user_error('delete_cancel', shipment.rec_name) + raise AccessError( + gettext('stock.msg_shipment_delete_cancel', + shipment=shipment.rec_name)) Move.delete([m for s in shipments for m in s.moves]) super(ShipmentInternal, cls).delete(shipments) diff --git a/tests/scenario_stock_inventory_count.rst b/tests/scenario_stock_inventory_count.rst index bd2a6d8674..2058cc1ce0 100644 --- a/tests/scenario_stock_inventory_count.rst +++ b/tests/scenario_stock_inventory_count.rst @@ -76,7 +76,7 @@ Count inventory:: >>> count.execute('quantity') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... - UserWarning: ... + InventoryCountWarning: ... >>> Model.get('res.user.warning')(user=config.user, ... name='stock.inventory,%s.product.product,%s.count_create' % ( diff --git a/tryton.cfg b/tryton.cfg index 574ab671f4..a08bbcfc7e 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -16,3 +16,4 @@ xml: party.xml configuration.xml period.xml + message.xml