Add ir.message and use custom exceptions
issue3672
This commit is contained in:
1 parent
191b26972d
commit
a67d1952a7
11 files changed
+213
-143
No files matched your search
@@ -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
|
||||
+24
-32
@@ -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
|
||||
|
||||
+15
-19
@@ -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):
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?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_product_change_default_uom">
|
||||
<field name="text">You cannot change the default UOM of a product which is associated to stock moves.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_product_change_type">
|
||||
<field name="text">You cannot change the type of a product which is associated to stock moves.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_location_invalid_type_for_moves">
|
||||
<field name="text">You cannot change the type of location "%(location)s" to "%(type)s"
|
||||
because the type does not support moves and location has existing moves.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_location_child_of_warehouse">
|
||||
<field name="text">To set location "%(location)s" as storage/input/output of warehouse "%(warehouse)s", it must be in the warehouse.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_location_inactive_not_empty">
|
||||
<field name="text">To inactivate location "%(location)s", you must empty it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_period_close_date">
|
||||
<field name="text">You cannot close periods with a date in the future or today.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_period_close_assigned_move">
|
||||
<field name="text">You cannot close periods with assigned moves in it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_shipment_delete_cancel">
|
||||
<field name="text">To delete shipment "%(shipment)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_delete_cancel">
|
||||
<field name="text">To delete inventory "%(inventory)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_missing_empty_quantity">
|
||||
<field name="text">To confirm the inventory "%(inventory)s" you must select an option for empty quantity.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_line_unique">
|
||||
<field name="text">Inventory line "%(line)s" is not unique on inventory "%(inventory)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_line_quantity_positive">
|
||||
<field name="text">Inventory line quantity must be positive.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_line_delete_cancel">
|
||||
<field name="text">To delete inventory line "%(line)s" you must cancel inventory "%(inventory)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inventory_count_create_line">
|
||||
<field name="text">No existing line found for "%(search)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_erase_party_shipment">
|
||||
<field name="text">You cannot erase party "%(party)s" while they have pending shipments with company "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_delete_draft_cancel">
|
||||
<field name="text">To delete stock move "%(move)s" you must cancel it or reset its state to draft.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_modify_period_close">
|
||||
<field name="text">To modify stock move "%(move)s" you must reopen period "%(period)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_modify_assigned">
|
||||
<field name="text">To modify stock move "%(move)s" you must reset its state to draft.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_modify_done">
|
||||
<field name="text">You cannot modify stock move "%(move)s" because it is done.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_modify_cancel">
|
||||
<field name="text">You cannot modify stock move "%(move)s" because it is cancelled.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_no_origin">
|
||||
<field name="text">Stock moves "%(moves)s" have no origin.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_quantity_positive">
|
||||
<field name="text">Move quantity must be positive.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_internal_quantity_positive">
|
||||
<field name="text">Internal move quantity must be positive.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_from_to_location">
|
||||
<field name="text">The source and destination of stock move must be different.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
|
||||
+7
-11
@@ -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
|
||||
|
||||
+17
-29
@@ -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)
|
||||
|
||||
|
||||
@@ -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' % (
|
||||
|
||||
@@ -16,3 +16,4 @@ xml:
|
||||
party.xml
|
||||
configuration.xml
|
||||
period.xml
|
||||
message.xml
|
||||
Reference in new issue
Block a user