Use ModelStorage hooks instead of extending create/write/delete
This commit is contained in:
@@ -214,8 +214,8 @@ class Type(
|
||||
ModelSQL, ModelView):
|
||||
__name__ = 'account.account.type'
|
||||
|
||||
parent = fields.Many2One('account.account.type', 'Parent',
|
||||
ondelete="RESTRICT",
|
||||
parent = fields.Many2One(
|
||||
'account.account.type', 'Parent', ondelete='CASCADE',
|
||||
states={
|
||||
'readonly': (Bool(Eval('template', -1))
|
||||
& ~Eval('template_override', False)),
|
||||
@@ -370,13 +370,6 @@ class Type(
|
||||
default.setdefault('template', None)
|
||||
return super().copy(types, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, types):
|
||||
types = cls.search([
|
||||
('parent', 'child_of', [t.id for t in types]),
|
||||
])
|
||||
super().delete(types)
|
||||
|
||||
def update_type(self, template2type=None):
|
||||
'''
|
||||
Update recursively types based on template.
|
||||
@@ -1280,21 +1273,6 @@ class Account(
|
||||
cls._rebuild_tree('parent', None, 0)
|
||||
return new_accounts
|
||||
|
||||
@classmethod
|
||||
def delete(cls, accounts):
|
||||
MoveLine = Pool().get('account.move.line')
|
||||
childs = cls.search([
|
||||
('parent', 'child_of', [a.id for a in accounts]),
|
||||
])
|
||||
lines = MoveLine.search([
|
||||
('account', 'in', [a.id for a in childs]),
|
||||
])
|
||||
if lines:
|
||||
raise AccessError(
|
||||
gettext('account.msg_delete_account_with_move_lines',
|
||||
account=lines[0].account.rec_name))
|
||||
super().delete(accounts)
|
||||
|
||||
def update_account(self, template2account=None, template2type=None):
|
||||
'''
|
||||
Update recursively accounts based on template.
|
||||
|
||||
+12
-15
@@ -133,20 +133,17 @@ class Company(metaclass=PoolMeta):
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, companies, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
transaction = Transaction()
|
||||
if transaction.user and transaction.check_access:
|
||||
actions = iter(args)
|
||||
for companies, values in zip(actions, actions):
|
||||
if 'currency' in values:
|
||||
moves = Move.search([
|
||||
('company', 'in', [c.id for c in companies]),
|
||||
],
|
||||
limit=1, order=[])
|
||||
if moves:
|
||||
raise AccessError(gettext(
|
||||
'account.msg_company_change_currency'))
|
||||
|
||||
super().write(*args)
|
||||
super().check_modification(
|
||||
mode, companies, values=values, external=external)
|
||||
if mode == 'write':
|
||||
if 'currency' in values:
|
||||
moves = Move.search([
|
||||
('company', 'in', [c.id for c in companies]),
|
||||
],
|
||||
limit=1, order=[])
|
||||
if moves:
|
||||
raise AccessError(gettext(
|
||||
'account.msg_company_change_currency'))
|
||||
|
||||
@@ -204,38 +204,28 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
Period.check_fiscalyear_dates(periods, field_names={'fiscalyear'})
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
fiscalyears = super().create(vlist)
|
||||
cls._find_cache.clear()
|
||||
return fiscalyears
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(
|
||||
cls, mode, fiscalyears, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
actions = iter(args)
|
||||
for fiscalyears, values in zip(actions, actions):
|
||||
if values.get('move_sequence'):
|
||||
for fiscalyear in fiscalyears:
|
||||
if (fiscalyear.move_sequence
|
||||
and fiscalyear.move_sequence.id
|
||||
!= values['move_sequence']):
|
||||
super().check_modification(
|
||||
mode, fiscalyears, values=values, external=external)
|
||||
if mode == 'write' and 'move_sequence' in values:
|
||||
for fiscalyear in fiscalyears:
|
||||
if sequence := fiscalyear.move_sequence:
|
||||
if sequence.id != values['move_sequence']:
|
||||
if Move.search([
|
||||
('period.fiscalyear', '=', fiscalyear.id),
|
||||
('state', '=', 'posted'),
|
||||
]):
|
||||
], limit=1):
|
||||
raise AccessError(
|
||||
gettext('account.'
|
||||
'msg_change_fiscalyear_move_sequence',
|
||||
fiscalyear=fiscalyear.rec_name))
|
||||
super().write(*args)
|
||||
cls._find_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, fiscalyears):
|
||||
Period = Pool().get('account.period')
|
||||
Period.delete([p for f in fiscalyears for p in f.periods])
|
||||
super().delete(fiscalyears)
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
cls._find_cache.clear()
|
||||
|
||||
@classmethod
|
||||
|
||||
+34
-48
@@ -147,23 +147,22 @@ class Journal(
|
||||
return journal
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, journals, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
actions = iter(args)
|
||||
for journals, values in zip(actions, actions):
|
||||
if 'type' in values:
|
||||
for sub_journals in grouped_slice(journals):
|
||||
moves = Move.search([
|
||||
('journal', 'in', [j.id for j in sub_journals]),
|
||||
('state', '=', 'posted')
|
||||
], order=[], limit=1)
|
||||
if moves:
|
||||
move, = moves
|
||||
raise AccessError(gettext(
|
||||
'account.msg_journal_account_moves',
|
||||
journal=move.journal.rec_name))
|
||||
super().write(*args)
|
||||
super().check_modification(
|
||||
mode, journals, values=values, external=external)
|
||||
if mode == 'write' and 'type' in values:
|
||||
for sub_journals in grouped_slice(journals):
|
||||
moves = Move.search([
|
||||
('journal', 'in', [j.id for j in sub_journals]),
|
||||
('state', '=', 'posted')
|
||||
], order=[], limit=1)
|
||||
if moves:
|
||||
move, = moves
|
||||
raise AccessError(gettext(
|
||||
'account.msg_journal_account_moves',
|
||||
journal=move.journal.rec_name))
|
||||
|
||||
|
||||
class JournalCashContext(ModelView):
|
||||
@@ -275,39 +274,26 @@ class JournalPeriod(Workflow, ModelSQL, ModelView):
|
||||
journal_period=period.rec_name))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
Period = Pool().get('account.period')
|
||||
for vals in vlist:
|
||||
if vals.get('period'):
|
||||
period = Period(vals['period'])
|
||||
if period.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('account'
|
||||
'.msg_create_journal_period_closed_period',
|
||||
period=period.rec_name))
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for journal_periods, values in zip(actions, actions):
|
||||
if (values != {'state': 'closed'}
|
||||
and values != {'state': 'open'}):
|
||||
cls._check(journal_periods)
|
||||
if values.get('state') == 'open':
|
||||
for journal_period in journal_periods:
|
||||
if journal_period.period.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('account'
|
||||
'.msg_open_journal_period_closed_period',
|
||||
journal_period=journal_period.rec_name,
|
||||
period=journal_period.period.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, periods):
|
||||
cls._check(periods)
|
||||
super().delete(periods)
|
||||
def check_modification(cls, mode, records, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, records, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for record in records:
|
||||
if record.period.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'account.msg_create_journal_period_closed_period',
|
||||
period=record.period.rec_name))
|
||||
elif mode in {'write', 'delete'}:
|
||||
if values != {'state': 'closed'} and values != {'state': 'open'}:
|
||||
cls._check(records)
|
||||
if mode == 'write' and values.get('state') == 'open':
|
||||
for record in records:
|
||||
if record.period.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'account.'
|
||||
'msg_open_journal_period_closed_period',
|
||||
journal_period=record.rec_name,
|
||||
period=record.period.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
|
||||
@@ -95,9 +95,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.message" id="msg_only_one_debit_credit_types">
|
||||
<field name="text">Account can have only one of the debit or credit type set.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_account_with_move_lines">
|
||||
<field name="text">You cannot delete account "%(account)s" because it has move lines.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_account_invalid_type_second_currency">
|
||||
<field name="text">To set a second currency for account "%(account)s", it must not have a type "payable", "revenue", "receivable" nor "expense".</field>
|
||||
</record>
|
||||
|
||||
+94
-137
@@ -132,7 +132,7 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView):
|
||||
super().__setup__()
|
||||
t = cls.__table__()
|
||||
cls.create_date.select = True
|
||||
cls._check_modify_exclude = ['lines']
|
||||
cls._check_modify_exclude = {'lines'}
|
||||
cls._order.insert(0, ('date', 'DESC'))
|
||||
cls._order.insert(1, ('number', 'DESC'))
|
||||
cls._buttons.update({
|
||||
@@ -269,40 +269,17 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView):
|
||||
return [(None, '')] + [(m, get_name(m)) for m in models]
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, moves):
|
||||
'Check posted moves for modifications.'
|
||||
for move in moves:
|
||||
if move.state == 'posted':
|
||||
raise AccessError(
|
||||
gettext('account.msg_modify_posted_moved',
|
||||
move=move.rec_name))
|
||||
|
||||
def get_rec_name(self, name):
|
||||
if self.number:
|
||||
return self.number
|
||||
else:
|
||||
return f'({self.id})'
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for moves, values in zip(actions, actions):
|
||||
keys = list(values.keys())
|
||||
for key in cls._check_modify_exclude:
|
||||
if key in keys:
|
||||
keys.remove(key)
|
||||
if len(keys):
|
||||
cls.check_modify(moves)
|
||||
args.extend((moves, values))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, moves):
|
||||
MoveLine = Pool().get('account.move.line')
|
||||
cls.check_modify(moves)
|
||||
MoveLine.delete([l for m in moves for l in m.lines])
|
||||
super().delete(moves)
|
||||
def check_modification(cls, mode, moves, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, moves, values=values, external=external)
|
||||
if (mode == 'delete'
|
||||
or (mode == 'write'
|
||||
and values.keys() - cls._check_modify_exclude)):
|
||||
for move in moves:
|
||||
if move.state == 'posted':
|
||||
raise AccessError(gettext(
|
||||
'account.msg_modify_posted_moved',
|
||||
move=move.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, moves, default=None):
|
||||
@@ -591,20 +568,49 @@ class Reconciliation(ModelSQL, ModelView):
|
||||
return Transaction().context.get('company')
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('account.configuration')
|
||||
configuration = Configuration(1)
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'reconciliation_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
default_company = cls.default_company()
|
||||
for vals in vlist:
|
||||
if 'number' not in vals:
|
||||
vals['number'] = configuration.get_multivalue(
|
||||
'reconciliation_sequence',
|
||||
company=vals.get('company', default_company)).get()
|
||||
@classmethod
|
||||
def check_modification(
|
||||
cls, mode, reconciliations, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
|
||||
return super().create(vlist)
|
||||
super().check_modification(
|
||||
mode, reconciliations, values=values, external=external)
|
||||
|
||||
if mode == 'delete':
|
||||
for reconciliation in reconciliations:
|
||||
if reconciliation.delegate_to:
|
||||
key = Warning.format('delete.delegated', [reconciliation])
|
||||
if Warning.check(key):
|
||||
raise ReconciliationDeleteWarning(key, gettext(
|
||||
'account.msg_reconciliation_delete_delegated',
|
||||
reconciliation=reconciliation.rec_name,
|
||||
line=reconciliation.delegate_to.rec_name,
|
||||
move=reconciliation.delegate_to.move.rec_name))
|
||||
for line in reconciliation.lines:
|
||||
if line.move.journal.type == 'write-off':
|
||||
key = Warning.format(
|
||||
'delete.write-off', [reconciliation])
|
||||
if Warning.check(key):
|
||||
raise ReconciliationDeleteWarning(key, gettext(
|
||||
'account.'
|
||||
'msg_reconciliation_delete_write_off',
|
||||
reconciliation=reconciliation.rec_name,
|
||||
line=line.rec_name,
|
||||
move=line.move.rec_name))
|
||||
|
||||
@classmethod
|
||||
def write(cls, moves, values, *args):
|
||||
@@ -615,31 +621,6 @@ class Reconciliation(ModelSQL, ModelView):
|
||||
super().validate(reconciliations)
|
||||
cls.check_lines(reconciliations)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, reconciliations):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
for reconciliation in reconciliations:
|
||||
if reconciliation.delegate_to:
|
||||
key = Warning.format('delete.delegated', [reconciliation])
|
||||
if Warning.check(key):
|
||||
raise ReconciliationDeleteWarning(key,
|
||||
gettext('account.msg_reconciliation_delete_delegated',
|
||||
reconciliation=reconciliation.rec_name,
|
||||
line=reconciliation.delegate_to.rec_name,
|
||||
move=reconciliation.delegate_to.move.rec_name))
|
||||
for line in reconciliation.lines:
|
||||
if line.move.journal.type == 'write-off':
|
||||
key = Warning.format('delete.write-off', [reconciliation])
|
||||
if Warning.check(key):
|
||||
raise ReconciliationDeleteWarning(key,
|
||||
gettext(
|
||||
'account.msg_reconciliation_delete_write_off',
|
||||
reconciliation=reconciliation.rec_name,
|
||||
line=line.rec_name,
|
||||
move=line.move.rec_name))
|
||||
super().delete(reconciliations)
|
||||
|
||||
@classmethod
|
||||
def check_lines(cls, reconciliations):
|
||||
Lang = Pool().get('ir.lang')
|
||||
@@ -1441,36 +1422,50 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
}])
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, lines, modified_fields=None):
|
||||
'''
|
||||
Check if the lines can be modified
|
||||
'''
|
||||
if (modified_fields is not None
|
||||
and modified_fields <= cls._check_modify_exclude):
|
||||
return
|
||||
journal_period_done = []
|
||||
for line in lines:
|
||||
if line.move.state == 'posted':
|
||||
raise AccessError(
|
||||
gettext('account.msg_modify_line_posted_move',
|
||||
line=line.rec_name,
|
||||
move=line.move.rec_name))
|
||||
journal_period = (line.journal.id, line.period.id)
|
||||
if journal_period not in journal_period_done:
|
||||
cls.check_journal_period_modify(line.period,
|
||||
line.journal)
|
||||
journal_period_done.append(journal_period)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
|
||||
if (mode in {'create', 'delete'}
|
||||
or (mode == 'write'
|
||||
and values.keys() - cls._check_modify_exclude)):
|
||||
journal_period_done = set()
|
||||
for line in lines:
|
||||
if line.move.state == 'posted':
|
||||
raise AccessError(gettext(
|
||||
'account.msg_modify_line_posted_move',
|
||||
line=line.rec_name,
|
||||
move=line.move.rec_name))
|
||||
journal_period = (line.journal.id, line.period.id)
|
||||
if journal_period not in journal_period_done:
|
||||
cls.check_journal_period_modify(
|
||||
line.period, line.journal)
|
||||
journal_period_done.add(journal_period)
|
||||
|
||||
if (mode == 'delete'
|
||||
or (mode == 'write'
|
||||
and values.keys() & cls._reconciliation_modify_disallow)):
|
||||
for line in lines:
|
||||
if line.reconciliation:
|
||||
raise AccessError(gettext(
|
||||
'account.msg_modify_line_reconciled',
|
||||
line=line.rec_name))
|
||||
|
||||
if mode in {'create', 'write'}:
|
||||
moves = Move.browse({l.move for l in lines})
|
||||
Move.validate_move(moves)
|
||||
|
||||
@classmethod
|
||||
def check_reconciliation(cls, lines, modified_fields=None):
|
||||
if (modified_fields is not None
|
||||
and not modified_fields & cls._reconciliation_modify_disallow):
|
||||
return
|
||||
for line in lines:
|
||||
if line.reconciliation:
|
||||
raise AccessError(
|
||||
gettext('account.msg_modify_line_reconciled',
|
||||
line=line.rec_name))
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
callback = super().on_delete(lines)
|
||||
moves = Move.browse({l.move for l in lines})
|
||||
callback.append(lambda: Move.validate_move(moves))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
@@ -1489,35 +1484,6 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
return {ModelData.get_id(
|
||||
'account', 'move_line_view_list_payable_receivable')}
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
Move = Pool().get('account.move')
|
||||
cls.check_modify(lines)
|
||||
cls.check_reconciliation(lines)
|
||||
moves = [x.move for x in lines]
|
||||
super().delete(lines)
|
||||
Move.validate_move(moves)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
Move = Pool().get('account.move')
|
||||
|
||||
actions = iter(args)
|
||||
args = []
|
||||
moves = []
|
||||
all_lines = []
|
||||
for lines, values in zip(actions, actions):
|
||||
cls.check_modify(lines, set(values.keys()))
|
||||
cls.check_reconciliation(lines, set(values.keys()))
|
||||
moves.extend((x.move for x in lines))
|
||||
all_lines.extend(lines)
|
||||
args.extend((lines, values))
|
||||
|
||||
super().write(*args)
|
||||
|
||||
Transaction().timestamp = {}
|
||||
Move.validate_move(list(set(l.move for l in all_lines) | set(moves)))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
@@ -1550,16 +1516,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
|
||||
# prevent default value for field with set_move_field
|
||||
for fname in move_fields(move_name=False):
|
||||
vals.setdefault(fname, None)
|
||||
lines = super().create(vlist)
|
||||
period_and_journals = set((line.period, line.journal)
|
||||
for line in lines)
|
||||
for period, journal in period_and_journals:
|
||||
cls.check_journal_period_modify(period, journal)
|
||||
# Re-browse for cache alignment
|
||||
moves = Move.browse(list(set(line.move for line in lines)))
|
||||
Move.check_modify(moves)
|
||||
Move.validate_move(moves)
|
||||
return lines
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
|
||||
+36
-36
@@ -30,7 +30,8 @@ class Period(Workflow, ModelSQL, ModelView):
|
||||
end_date = fields.Date('Ending Date', required=True, states=_STATES,
|
||||
domain=[('end_date', '>=', Eval('start_date', None))])
|
||||
fiscalyear = fields.Many2One(
|
||||
'account.fiscalyear', "Fiscal Year", required=True, states=_STATES)
|
||||
'account.fiscalyear', "Fiscal Year", required=True, ondelete='CASCADE',
|
||||
states=_STATES)
|
||||
state = fields.Selection([
|
||||
('open', 'Open'),
|
||||
('closed', 'Closed'),
|
||||
@@ -320,26 +321,29 @@ class Period(Workflow, ModelSQL, ModelView):
|
||||
order=order, count=count, query=query)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
FiscalYear = Pool().get('account.fiscalyear')
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for vals in vlist:
|
||||
if vals.get('fiscalyear'):
|
||||
fiscalyear = FiscalYear(vals['fiscalyear'])
|
||||
if fiscalyear.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('account.msg_create_period_closed_fiscalyear',
|
||||
fiscalyear=fiscalyear.rec_name))
|
||||
periods = super().create(vlist)
|
||||
cls._find_cache.clear()
|
||||
return periods
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and values.get('fiscalyear') is not None:
|
||||
fiscalyear = FiscalYear(values['fiscalyear'])
|
||||
if values.get('move_sequence') is None:
|
||||
values['move_sequence'] = fiscalyear.move_sequence.id
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
Move = Pool().get('account.move')
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for periods, values in zip(actions, actions):
|
||||
def check_modification(cls, mode, periods, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
super().check_modification(
|
||||
mode, periods, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for period in periods:
|
||||
if period.fiscalyear.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('account.msg_create_period_closed_fiscalyear',
|
||||
fiscalyear=period.fiscalyear.rec_name))
|
||||
elif mode == 'write':
|
||||
if values.get('state') == 'open':
|
||||
for period in periods:
|
||||
if period.fiscalyear.state != 'open':
|
||||
@@ -348,26 +352,22 @@ class Period(Workflow, ModelSQL, ModelView):
|
||||
'account.msg_open_period_closed_fiscalyear',
|
||||
period=period.rec_name,
|
||||
fiscalyear=period.fiscalyear.rec_name))
|
||||
if values.get('move_sequence'):
|
||||
if 'move_sequence' in values:
|
||||
for period in periods:
|
||||
if (period.move_sequence
|
||||
and period.move_sequence.id
|
||||
!= values['move_sequence']):
|
||||
if Move.search([
|
||||
('period', '=', period.id),
|
||||
('state', '=', 'posted'),
|
||||
]):
|
||||
raise AccessError(
|
||||
gettext('account'
|
||||
'.msg_change_period_move_sequence',
|
||||
period=period.rec_name))
|
||||
args.extend((periods, values))
|
||||
super().write(*args)
|
||||
cls._find_cache.clear()
|
||||
if sequence := period.move_sequence:
|
||||
if sequence.id != values['move_sequence']:
|
||||
if Move.search([
|
||||
('period', '=', period.id),
|
||||
('state', '=', 'posted'),
|
||||
], limit=1, order=[]):
|
||||
raise AccessError(gettext(
|
||||
'account.'
|
||||
'msg_change_period_move_sequence',
|
||||
period=period.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, periods):
|
||||
super().delete(periods)
|
||||
def on_modification(cls, mode, periods, field_names=None):
|
||||
super().on_modification(mode, periods, field_names=field_names)
|
||||
cls._find_cache.clear()
|
||||
|
||||
@classmethod
|
||||
|
||||
+7
-28
@@ -132,7 +132,8 @@ class TaxCode(
|
||||
code = fields.Char('Code', states=_states)
|
||||
company = fields.Many2One('company.company', 'Company', required=True)
|
||||
parent = fields.Many2One(
|
||||
'account.tax.code', "Parent", states=_states,
|
||||
'account.tax.code', "Parent", ondelete='CASCADE',
|
||||
states=_states,
|
||||
domain=[
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
@@ -243,13 +244,6 @@ class TaxCode(
|
||||
default.setdefault('template', None)
|
||||
return super().copy(codes, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, codes):
|
||||
codes = cls.search([
|
||||
('parent', 'child_of', [c.id for c in codes]),
|
||||
])
|
||||
super().delete(codes)
|
||||
|
||||
@classmethod
|
||||
def update_tax_code(cls, company_id, template2tax_code=None):
|
||||
'''
|
||||
@@ -1407,34 +1401,19 @@ class TaxLine(ModelSQL, ModelView):
|
||||
def search_rec_name(cls, name, clause):
|
||||
return [('tax',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
lines = super().create(vlist)
|
||||
cls.check_modify(lines)
|
||||
return lines
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
lines = sum(args[0:None:2], [])
|
||||
cls.check_modify(lines)
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
cls.check_modify(lines)
|
||||
super().delete(lines)
|
||||
|
||||
@property
|
||||
def period_checked(self):
|
||||
return self.move_line.period
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, lines):
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
for line in lines:
|
||||
period = line.period_checked
|
||||
if period and period.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('account.msg_modify_tax_line_closed_period',
|
||||
raise AccessError(gettext(
|
||||
'account.msg_modify_tax_line_closed_period',
|
||||
period=period.rec_name))
|
||||
|
||||
|
||||
|
||||
@@ -712,13 +712,15 @@ class Asset(Workflow, ModelSQL, ModelView):
|
||||
return super().copy(assets, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, assets):
|
||||
for asset in assets:
|
||||
if asset.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('account_asset.msg_asset_delete_draft',
|
||||
asset=asset.rec_name))
|
||||
return super().delete(assets)
|
||||
def check_modification(cls, mode, assets, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, assets, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for asset in assets:
|
||||
if asset.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_asset.msg_asset_delete_draft',
|
||||
asset=asset.rec_name))
|
||||
|
||||
|
||||
class AssetLine(ModelSQL, ModelView):
|
||||
|
||||
@@ -57,7 +57,7 @@ class Move(metaclass=PoolMeta):
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._check_modify_exclude.append('consolidation_company')
|
||||
cls._check_modify_exclude.add('consolidation_company')
|
||||
|
||||
|
||||
class MoveLine(metaclass=PoolMeta):
|
||||
|
||||
@@ -229,17 +229,16 @@ class Period(metaclass=PoolMeta):
|
||||
help="Check to create SII records for the invoices in the period.")
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
to_check = []
|
||||
for periods, values in zip(actions, actions):
|
||||
if 'es_sii_send_invoices' in values:
|
||||
for period in periods:
|
||||
if (period.es_sii_send_invoices
|
||||
!= values['es_sii_send_invoices']):
|
||||
to_check.append(period)
|
||||
cls.check_es_sii_posted_invoices(to_check)
|
||||
super().write(*args)
|
||||
def check_modification(cls, mode, periods, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, periods, values=values, external=external)
|
||||
if mode == 'write' and 'es_sii_send_invoices' in values:
|
||||
to_check = []
|
||||
for period in periods:
|
||||
if (period.es_sii_send_invoices
|
||||
!= values['es_sii_send_invoices']):
|
||||
to_check.append(period)
|
||||
cls.check_es_sii_posted_invoices(to_check)
|
||||
|
||||
@classmethod
|
||||
def check_es_sii_posted_invoices(cls, periods):
|
||||
@@ -248,11 +247,11 @@ class Period(metaclass=PoolMeta):
|
||||
for sub_ids in grouped_slice(list(map(int, periods))):
|
||||
invoices = Invoice.search([
|
||||
('move.period', 'in', sub_ids),
|
||||
], limit=1)
|
||||
], limit=1, order=[])
|
||||
if invoices:
|
||||
invoice, = invoices
|
||||
raise ESSIIPostedInvoicesError(
|
||||
gettext('account_es_sii.msg_es_sii_posted_invoices',
|
||||
raise ESSIIPostedInvoicesError(gettext(
|
||||
'account_es_sii.msg_es_sii_posted_invoices',
|
||||
period=invoice.move.period.rec_name))
|
||||
|
||||
|
||||
@@ -352,13 +351,15 @@ class InvoiceSII(ModelSQL, ModelView):
|
||||
return [('invoice.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
for record in records:
|
||||
if record.csv:
|
||||
raise AccessError(
|
||||
gettext('account_es_sii.msg_es_sii_invoice_delete_sent',
|
||||
invoice=record.invoice.rec_name))
|
||||
super().delete(records)
|
||||
def check_modification(cls, mode, invoices, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, invoices, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for invoice in invoices:
|
||||
if invoice.csv:
|
||||
raise AccessError(gettext(
|
||||
'account_es_sii.msg_es_sii_invoice_delete_sent',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
|
||||
@@ -60,7 +60,7 @@ class Move(metaclass=PoolMeta):
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._check_modify_exclude.append('export')
|
||||
cls._check_modify_exclude.add('export')
|
||||
|
||||
@classmethod
|
||||
def copy(cls, moves, default=None):
|
||||
@@ -209,11 +209,13 @@ class MoveExport(Workflow, ModelSQL, ModelView):
|
||||
return super().copy(exports, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, exports):
|
||||
for export in exports:
|
||||
if export.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext(
|
||||
'account_export.msg_account_move_export_delete_draft',
|
||||
export=export.rec_name))
|
||||
super().delete(exports)
|
||||
def check_modification(cls, mode, exports, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, exports, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for export in exports:
|
||||
if export.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_export.'
|
||||
'msg_account_move_export_delete_draft',
|
||||
export=export.rec_name))
|
||||
|
||||
@@ -163,11 +163,12 @@ class CredentialChorus(ModelSQL, CompanyValueMixin):
|
||||
return resp.json()
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, records, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for records, values in zip(actions, actions):
|
||||
super().check_modification(
|
||||
mode, records, values=values, external=external)
|
||||
if mode == 'write' and external:
|
||||
for record in records:
|
||||
for field in [
|
||||
'chorus_piste_client_id', 'chorus_piste_client_secret',
|
||||
@@ -182,7 +183,6 @@ class CredentialChorus(ModelSQL, CompanyValueMixin):
|
||||
warning_name,
|
||||
gettext('account_fr_chorus'
|
||||
'.msg_chorus_credential_modified'))
|
||||
return super().write(*args)
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
@@ -300,6 +300,9 @@ class InvoiceChorus(
|
||||
table, _ = tables[None]
|
||||
return [CharLength(table.number), table.number]
|
||||
|
||||
def get_rec_name(self, name):
|
||||
return self.invoice.rec_name
|
||||
|
||||
@classmethod
|
||||
def validate(cls, records):
|
||||
super().validate(records)
|
||||
@@ -316,13 +319,15 @@ class InvoiceChorus(
|
||||
address=address.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
for record in records:
|
||||
if record.number:
|
||||
raise AccessError(
|
||||
gettext('account_fr_chorus.msg_invoice_delete_sent',
|
||||
invoice=record.invoice.rec_name))
|
||||
super().delete(records)
|
||||
def check_modification(cls, mode, invoices, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, invoices, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for invoice in invoices:
|
||||
if invoice.number:
|
||||
raise AccessError(gettext(
|
||||
'account_fr_chorus.msg_invoice_delete_sent',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
def _send_context(self):
|
||||
return {
|
||||
|
||||
@@ -253,27 +253,18 @@ class Reconciliation(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.reconciliation'
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
Invoice = Pool().get('account.invoice')
|
||||
def on_modification(cls, mode, reconciliations, field_names=None):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
reconciliations = super().create(vlist)
|
||||
|
||||
super().on_modification(mode, reconciliations, field_names=field_names)
|
||||
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Invoice.__queue__.process(
|
||||
list(_invoices_to_process(reconciliations)))
|
||||
return reconciliations
|
||||
|
||||
@classmethod
|
||||
def delete(cls, reconciliations):
|
||||
Invoice = Pool().get('account.invoice')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
invoices_to_process = _invoices_to_process(reconciliations)
|
||||
super().delete(reconciliations)
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Invoice.__queue__.process(list(invoices_to_process))
|
||||
|
||||
|
||||
class RenewFiscalYear(metaclass=PoolMeta):
|
||||
|
||||
@@ -1404,17 +1404,6 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
or (self.state == 'cancelled'
|
||||
and (self.move or self.cancel_move or self.number)))
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, invoices):
|
||||
'''
|
||||
Check if the invoices can be modified
|
||||
'''
|
||||
for invoice in invoices:
|
||||
if not invoice.is_modifiable:
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_modify',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
def get_rec_name(self, name):
|
||||
items = []
|
||||
if self.number:
|
||||
@@ -1456,39 +1445,34 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, invoices):
|
||||
cls.check_modify(invoices)
|
||||
# Cancel before delete
|
||||
cls.cancel(invoices)
|
||||
for invoice in invoices:
|
||||
if invoice.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_delete_cancel',
|
||||
invoice=invoice.rec_name))
|
||||
if invoice.number:
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_delete_numbered',
|
||||
invoice=invoice.rec_name))
|
||||
super().delete(invoices)
|
||||
def check_modification(cls, mode, invoices, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, invoices, values=values, external=external)
|
||||
if (mode == 'delete'
|
||||
or (mode == 'write'
|
||||
and values.keys() - cls._check_modify_exclude)):
|
||||
for invoice in invoices:
|
||||
if not invoice.is_modifiable:
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_modify',
|
||||
invoice=invoice.rec_name))
|
||||
if mode == 'delete':
|
||||
for invoice in invoices:
|
||||
if invoice.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_delete_cancel',
|
||||
invoice=invoice.rec_name))
|
||||
if invoice.number:
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_delete_numbered',
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
invoices = super().create(vlist)
|
||||
cls.update_taxes(invoices)
|
||||
return invoices
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
all_invoices = []
|
||||
for invoices, values in zip(actions, actions):
|
||||
if set(values) - cls._check_modify_exclude:
|
||||
cls.check_modify(invoices)
|
||||
all_invoices += invoices
|
||||
update_tax = [i for i in all_invoices if i.state == 'draft']
|
||||
super().write(*args)
|
||||
if update_tax:
|
||||
cls.update_taxes(update_tax)
|
||||
def on_modification(cls, mode, invoices, field_names=None):
|
||||
super().on_modification(mode, invoices, field_names=field_names)
|
||||
if mode != 'delete':
|
||||
if draft_invoices := [i for i in invoices if i.state == 'draft']:
|
||||
cls.update_taxes(draft_invoices)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, invoices, default=None):
|
||||
@@ -2804,15 +2788,22 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, lines, fields=None):
|
||||
'''
|
||||
Check if the lines can be modified
|
||||
'''
|
||||
if fields is None or fields - cls._check_modify_exclude:
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for line in lines:
|
||||
if line.invoice.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_line_create_draft',
|
||||
invoice=line.invoice.rec_name))
|
||||
elif (mode == 'delete'
|
||||
or (mode == 'write'
|
||||
and values.keys() - cls._check_modify_exclude)):
|
||||
for line in lines:
|
||||
if line.invoice and not line.invoice.is_modifiable:
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_line_modify',
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_line_modify',
|
||||
line=line.rec_name,
|
||||
invoice=line.invoice.rec_name))
|
||||
|
||||
@@ -2822,30 +2813,6 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
|
||||
('/form//field[@name="note"]|/form//field[@name="description"]',
|
||||
'spell', Eval('party_lang'))]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
cls.check_modify(lines)
|
||||
super().delete(lines)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for lines, values in zip(actions, actions):
|
||||
cls.check_modify(lines, set(values))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
invoice_ids = filter(None, {v.get('invoice') for v in vlist})
|
||||
for invoice in Invoice.browse(list(invoice_ids)):
|
||||
if invoice.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_line_create_draft',
|
||||
invoice=invoice.rec_name))
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
if default is None:
|
||||
@@ -3134,15 +3101,22 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
return (account_id, tax_id, (getattr(self, 'base', 0) or 0) >= 0)
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, taxes, fields=None):
|
||||
'''
|
||||
Check if the taxes can be modified
|
||||
'''
|
||||
if fields is None or fields - cls._check_modify_exclude:
|
||||
def check_modification(cls, mode, taxes, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, taxes, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for tax in taxes:
|
||||
if tax.invoice.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_line_create_draft',
|
||||
invoice=tax.invoice.rec_name))
|
||||
elif (mode == 'delete'
|
||||
or (mode == 'write'
|
||||
and values.keys() - cls._check_modify_exclude)):
|
||||
for tax in taxes:
|
||||
if not tax.invoice.is_modifiable:
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_tax_modify',
|
||||
raise AccessError(gettext(
|
||||
'account_invoice.msg_invoice_tax_modify',
|
||||
tax=tax.rec_name,
|
||||
invoice=tax.invoice.rec_name))
|
||||
|
||||
@@ -3154,30 +3128,6 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
|
||||
i += 1
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def delete(cls, taxes):
|
||||
cls.check_modify(taxes)
|
||||
super().delete(taxes)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for taxes, values in zip(actions, actions):
|
||||
cls.check_modify(taxes, set(values))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
invoice_ids = filter(None, {v.get('invoice') for v in vlist})
|
||||
for invoice in Invoice.browse(list(invoice_ids)):
|
||||
if invoice.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('account_invoice.msg_invoice_tax_create_draft',
|
||||
invoice=invoice.rec_name))
|
||||
return super().create(vlist)
|
||||
|
||||
def get_move_lines(self):
|
||||
'''
|
||||
Return a list of move lines instances for invoice tax
|
||||
|
||||
@@ -240,14 +240,16 @@ class InvoiceDeferred(Workflow, ModelSQL, ModelView):
|
||||
and deferral.moves[-1].period.end_date >= deferral.end_date)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, deferrals):
|
||||
for deferral in deferrals:
|
||||
if deferral.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('account_invoice_defer'
|
||||
'.msg_invoice_deferred_delete_draft',
|
||||
deferral=deferral.rec_name))
|
||||
return super().delete(deferrals)
|
||||
def check_modification(cls, mode, deferrals, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, deferrals, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for deferral in deferrals:
|
||||
if deferral.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_invoice_defer'
|
||||
'.msg_invoice_deferred_delete_draft',
|
||||
deferral=deferral.rec_name))
|
||||
|
||||
@classmethod
|
||||
def defer_amount(cls, deferrals):
|
||||
|
||||
@@ -115,18 +115,18 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
return ', '.join(sorted(shipments))
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_modification(cls, mode, lines, field_names=None):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
super().write(*args)
|
||||
lines = sum(args[0:None:2], [])
|
||||
moves = sum((l.stock_moves for l in lines), ())
|
||||
if moves:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Move.__queue__.update_unit_price(moves)
|
||||
super().on_modification(mode, lines, field_names=field_names)
|
||||
if mode == 'write':
|
||||
moves = sum((l.stock_moves for l in lines), ())
|
||||
if moves:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Move.__queue__.update_unit_price(moves)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
|
||||
@@ -141,19 +141,18 @@ class Group(ModelSQL, ModelView):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('account.configuration')
|
||||
|
||||
vlist = [v.copy() for v in vlist]
|
||||
config = Config(1)
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'payment_group_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
Configuration = pool.get('account.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
configuration = Configuration(1)
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
if sequence := configuration.get_multivalue(
|
||||
'payment_group_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, groups, default=None):
|
||||
@@ -610,53 +609,58 @@ class Payment(Workflow, ModelSQL, ModelView):
|
||||
return super().copy(payments, default=default)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
Config = pool.get('account.configuration')
|
||||
|
||||
vlist = [v.copy() for v in vlist]
|
||||
config = Config(1)
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'payment_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
payments = super().create(vlist)
|
||||
lines = {p.line for p in payments if p.line}
|
||||
if lines:
|
||||
Line.set_payment_amount(list(lines))
|
||||
return payments
|
||||
Configuration = pool.get('account.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
configuration = Configuration(1)
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
if sequence := configuration.get_multivalue(
|
||||
'payment_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
actions = iter(args)
|
||||
lines = set()
|
||||
for payments, values in zip(actions, actions):
|
||||
if values.keys() & {'line', 'amount', 'state'}:
|
||||
lines.update(p.line for p in payments if p.line)
|
||||
if line := values.get('line'):
|
||||
lines.add(Line(line))
|
||||
super().write(*args)
|
||||
if lines:
|
||||
Line.set_payment_amount(list(lines))
|
||||
def check_modification(cls, mode, payments, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, payments, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for payment in payments:
|
||||
if payment.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'account_payment.msg_payment_delete_draft',
|
||||
payment=payment.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, payments):
|
||||
def on_modification(cls, mode, payments, field_names=None):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
for payment in payments:
|
||||
if payment.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('account_payment.msg_payment_delete_draft',
|
||||
payment=payment.rec_name))
|
||||
lines = {p.line for p in payments if p.line}
|
||||
super().delete(payments)
|
||||
if lines:
|
||||
Line.set_payment_amount(list(lines))
|
||||
super().on_modification(mode, payments, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
if not field_names or 'line' in field_names:
|
||||
if lines := Line.browse({p.line for p in payments if p.line}):
|
||||
Line.set_payment_amount(lines)
|
||||
|
||||
@classmethod
|
||||
def on_write(cls, payments, values):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
callback = super().on_write(payments, values)
|
||||
if values.keys() & {'line', 'amount', 'state'}:
|
||||
if lines := Line.browse({p.line for p in payments if p.line}):
|
||||
callback.append(lambda: Line.set_payment_amount(lines))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def on_delete(cls, payments):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
callback = super().on_delete(payments)
|
||||
if lines := Line.browse({p.line for p in payments if p.line}):
|
||||
callback.append(lambda: Line.set_payment_amount(lines))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
|
||||
@@ -909,20 +909,23 @@ class PaymentBraintreeAccount(ModelSQL, ModelView):
|
||||
return bool(payments)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, accounts, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for accounts, values in zip(actions, actions):
|
||||
if ({'environment', 'merchant_id', 'public_key', 'private_key'}
|
||||
& values.keys()):
|
||||
warning_name = Warning.format('braintree_key', accounts)
|
||||
if Warning.check(warning_name):
|
||||
raise BraintreeAccountWarning(
|
||||
warning_name,
|
||||
gettext('account_payment_braintree'
|
||||
'.msg_braintree_key_modified'))
|
||||
return super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, accounts, values=values, external=external)
|
||||
|
||||
if (mode == 'write'
|
||||
and external
|
||||
and values.keys()
|
||||
& {'environment', 'merchant_id', 'public_key', 'private_key'}):
|
||||
warning_name = Warning.format('braintree_key', accounts)
|
||||
if Warning.check(warning_name):
|
||||
raise BraintreeAccountWarning(
|
||||
warning_name,
|
||||
gettext('account_payment_braintree'
|
||||
'.msg_braintree_key_modified'))
|
||||
|
||||
|
||||
class PaymentBraintreeCustomer(
|
||||
@@ -992,9 +995,11 @@ class PaymentBraintreeCustomer(
|
||||
|
||||
@classmethod
|
||||
def delete(cls, customers):
|
||||
ids, on_delete = cls._before_delete(customers)
|
||||
cls.write(customers, {
|
||||
'active': False,
|
||||
})
|
||||
cls._after_delete(ids, on_delete)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, customers, default=None):
|
||||
|
||||
@@ -21,26 +21,29 @@ class Party(metaclass=PoolMeta):
|
||||
return parties
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_write(cls, parties, values):
|
||||
pool = Pool()
|
||||
Customer = pool.get('account.payment.braintree.customer')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
|
||||
parties = sum(args[0:None:2], [])
|
||||
callback = super().on_write(parties, values)
|
||||
|
||||
customers = sum((p.braintree_customers for p in parties), ())
|
||||
customer2params = {c: c._customer_parameters() for c in customers}
|
||||
if customers:
|
||||
customer2params = {c: c._customer_parameters() for c in customers}
|
||||
|
||||
super().write(*args)
|
||||
|
||||
to_update = []
|
||||
for customer, params in customer2params.items():
|
||||
if customer._customer_parameters() != params:
|
||||
to_update.append(customer)
|
||||
if to_update:
|
||||
with Transaction().set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Customer.__queue__.braintree_update(to_update)
|
||||
def update():
|
||||
to_update = []
|
||||
for customer, params in customer2params.items():
|
||||
if customer._customer_parameters() != params:
|
||||
to_update.append(customer)
|
||||
if to_update:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Customer.__queue__.braintree_update(to_update)
|
||||
callback.append(update)
|
||||
return callback
|
||||
|
||||
|
||||
class PartyReceptionDirectDebit(
|
||||
|
||||
@@ -40,18 +40,9 @@ class MoveReconciliation(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.reconciliation'
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, reconciliations, field_names=None):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
reconciliations = super().create(vlist)
|
||||
super().on_modification(mode, reconciliations, field_names=field_names)
|
||||
Payment.__queue__.update_reconciled(
|
||||
list(_payments_to_update(reconciliations)))
|
||||
return reconciliations
|
||||
|
||||
@classmethod
|
||||
def delete(cls, reconciliations):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
payments = _payments_to_update(reconciliations)
|
||||
super().delete(reconciliations)
|
||||
Payment.__queue__.update_reconciled(list(payments))
|
||||
|
||||
@@ -76,7 +76,7 @@ def cancel_clearing_move(func):
|
||||
Line = pool.get('account.move.line')
|
||||
Reconciliation = pool.get('account.move.reconciliation')
|
||||
|
||||
func(cls, payments, *args, **kwargs)
|
||||
result = func(cls, payments, *args, **kwargs)
|
||||
|
||||
to_delete = []
|
||||
to_reconcile = defaultdict(lambda: defaultdict(list))
|
||||
@@ -110,6 +110,7 @@ def cancel_clearing_move(func):
|
||||
for lines in to_reconcile[party].values():
|
||||
Line.reconcile(lines)
|
||||
cls.update_reconciled(payments)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
|
||||
@@ -639,36 +639,32 @@ class Mandate(Workflow, ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('account.configuration')
|
||||
|
||||
config = Configuration(1)
|
||||
vlist = [v.copy() for v in vlist]
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if not values.get('identification'):
|
||||
sequence = config.get_multivalue(
|
||||
'sepa_mandate_sequence',
|
||||
company=values.get('company', default_company))
|
||||
if sequence:
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('identification'):
|
||||
configuration = Configuration(1)
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
if sequence := configuration.get_multivalue(
|
||||
'sepa_mandate_sequence', company=company_id):
|
||||
values['identification'] = sequence.get()
|
||||
# Prevent raising false unique constraint
|
||||
if values.get('identification') == '':
|
||||
values['identification'] = None
|
||||
return super().create(vlist)
|
||||
if values.get('identification') == '':
|
||||
values['identification'] = None
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for mandates, values in zip(actions, actions):
|
||||
# Prevent raising false unique constraint
|
||||
if values.get('identification') == '':
|
||||
values = values.copy()
|
||||
values['identification'] = None
|
||||
args.extend((mandates, values))
|
||||
super().write(*args)
|
||||
def check_modification(cls, mode, mandates, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, mandates, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for mandate in mandates:
|
||||
if mandate.state not in {'draft', 'cancelled'}:
|
||||
raise AccessError(gettext(
|
||||
'account_payment_sepa'
|
||||
'.msg_mandate_delete_draft_cancelled',
|
||||
mandate=mandate.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, mandates, default=None):
|
||||
@@ -767,16 +763,6 @@ class Mandate(Workflow, ModelSQL, ModelView):
|
||||
# TODO must be automaticaly cancelled 13 months after last collection
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def delete(cls, mandates):
|
||||
for mandate in mandates:
|
||||
if mandate.state not in ('draft', 'cancelled'):
|
||||
raise AccessError(
|
||||
gettext('account_payment_sepa'
|
||||
'.msg_mandate_delete_draft_cancelled',
|
||||
mandate=mandate.rec_name))
|
||||
super().delete(mandates)
|
||||
|
||||
|
||||
class MandateReport(CompanyReport):
|
||||
__name__ = 'account.payment.sepa.mandate'
|
||||
|
||||
@@ -21,26 +21,29 @@ class Party(metaclass=PoolMeta):
|
||||
return parties
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_write(cls, parties, values):
|
||||
pool = Pool()
|
||||
Customer = pool.get('account.payment.stripe.customer')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
|
||||
parties = sum(args[0:None:2], [])
|
||||
callback = super().on_write(parties, values)
|
||||
|
||||
customers = sum((p.stripe_customers for p in parties), ())
|
||||
customer2params = {c: c._customer_parameters() for c in customers}
|
||||
if customers:
|
||||
customer2params = {c: c._customer_parameters() for c in customers}
|
||||
|
||||
super().write(*args)
|
||||
|
||||
to_update = []
|
||||
for customer, params in customer2params.items():
|
||||
if customer._customer_parameters() != params:
|
||||
to_update.append(customer)
|
||||
if to_update:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Customer.__queue__.stripe_update(to_update)
|
||||
def update():
|
||||
to_update = []
|
||||
for customer, params in customer2params.items():
|
||||
if customer._customer_parameters() != params:
|
||||
to_update.append(customer)
|
||||
if to_update:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Customer.__queue__.stripe_update(to_update)
|
||||
callback.append(update)
|
||||
return callback
|
||||
|
||||
|
||||
class PartyReceptionDirectDebit(
|
||||
|
||||
@@ -324,14 +324,15 @@ class Payment(StripeCustomerMethodMixin, CheckoutMixin, metaclass=PoolMeta):
|
||||
payment=payment.rec_name))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
# Ensure to get a different key for each record
|
||||
# default methods are called only once
|
||||
values.setdefault('stripe_idempotency_key',
|
||||
cls.default_stripe_idempotency_key())
|
||||
return super().create(vlist)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
if 'stripe_idempotency_key' not in values:
|
||||
# Ensure to get a different key for each record
|
||||
# default methods are called only once
|
||||
values['stripe_idempotency_key'] = (
|
||||
cls.default_stripe_idempotency_key())
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, payments, default=None):
|
||||
@@ -839,13 +840,15 @@ class Refund(Workflow, ModelSQL, ModelView):
|
||||
return 'draft'
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
values.setdefault(
|
||||
'stripe_idempotency_key',
|
||||
cls.default_stripe_idempotency_key())
|
||||
return super().create(vlist)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
if 'stripe_idempotency_key' not in values:
|
||||
# Ensure to get a different key for each record
|
||||
# default methods are called only once
|
||||
values['stripe_idempotency_key'] = (
|
||||
cls.default_stripe_idempotency_key())
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, refunds, default=None):
|
||||
@@ -1394,19 +1397,22 @@ class Account(ModelSQL, ModelView):
|
||||
cls.save(accounts)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, accounts, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for accounts, values in zip(actions, actions):
|
||||
if {'secret_key', 'publishable_key'} & values.keys():
|
||||
warning_name = Warning.format('stripe_key', accounts)
|
||||
if Warning.check(warning_name):
|
||||
raise StripeAccountWarning(
|
||||
warning_name,
|
||||
gettext('account_payment_stripe'
|
||||
'.msg_stripe_key_modified'))
|
||||
return super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, accounts, values=values, external=external)
|
||||
|
||||
if (mode == 'write'
|
||||
and external
|
||||
and values.keys() & {'secret_key', 'publishable_key'}):
|
||||
warning_name = Warning.format('stripe_key', accounts)
|
||||
if Warning.check(warning_name):
|
||||
raise StripeAccountWarning(
|
||||
warning_name,
|
||||
gettext('account_payment_stripe'
|
||||
'.msg_stripe_key_modified'))
|
||||
|
||||
|
||||
class Customer(CheckoutMixin, DeactivableMixin, ModelSQL, ModelView):
|
||||
@@ -1505,16 +1511,19 @@ class Customer(CheckoutMixin, DeactivableMixin, ModelSQL, ModelView):
|
||||
return self.stripe_customer_id if self.stripe_customer_id else name
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
super().write(*args, **kwargs)
|
||||
cls._sources_cache.clear()
|
||||
cls._payment_methods_cache.clear()
|
||||
def on_modification(cls, mode, customers, field_names=None):
|
||||
super().on_modification(mode, customers, field_names=field_names)
|
||||
if mode == 'write':
|
||||
cls._sources_cache.clear()
|
||||
cls._payment_methods_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, customers):
|
||||
ids, on_delete = cls._before_delete(customers)
|
||||
cls.write(customers, {
|
||||
'active': False,
|
||||
})
|
||||
cls._after_delete(ids, on_delete)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, customers, default=None):
|
||||
|
||||
@@ -368,15 +368,15 @@ class Statement(Workflow, ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, statements):
|
||||
# Cancel before delete
|
||||
cls.cancel(statements)
|
||||
for statement in statements:
|
||||
if statement.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('account_statement.msg_statement_delete_cancel',
|
||||
statement=statement.rec_name))
|
||||
super().delete(statements)
|
||||
def check_modification(cls, mode, statements, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, statements, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for statement in statements:
|
||||
if statement.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'account_statement.msg_statement_delete_cancel',
|
||||
statement=statement.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -963,16 +963,17 @@ class Line(origin_mixin(_states), sequence_ordered(), ModelSQL, ModelView):
|
||||
return [('statement.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
for line in lines:
|
||||
if line.statement_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext(
|
||||
'account_statement.'
|
||||
'msg_statement_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
sale=line.statement.rec_name))
|
||||
super().delete(lines)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for line in lines:
|
||||
if line.statement_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'account_statement.'
|
||||
'msg_statement_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
sale=line.statement.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
@@ -1214,16 +1215,17 @@ class Origin(origin_mixin(_states), ModelSQL, ModelView):
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, origins):
|
||||
for origin in origins:
|
||||
if origin.statement_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext(
|
||||
'account_statement.'
|
||||
'msg_statement_origin_delete_cancel_draft',
|
||||
origin=origin.rec_name,
|
||||
sale=origin.statement.rec_name))
|
||||
super().delete(origins)
|
||||
def check_modification(cls, mode, origins, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, origins, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for origin in origins:
|
||||
if origin.statement_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'account_statement.'
|
||||
'msg_statement_origin_delete_cancel_draft',
|
||||
origin=origin.rec_name,
|
||||
sale=origin.statement.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, origins, default=None):
|
||||
|
||||
@@ -201,11 +201,14 @@ class Move(metaclass=PoolMeta):
|
||||
super().cancel(moves)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, moves):
|
||||
for move in moves:
|
||||
if move.in_anglo_saxon_quantity or move.out_anglo_saxon_quantity:
|
||||
raise AccessError(
|
||||
gettext('account_stock_anglo_saxon'
|
||||
'.msg_move_delete_anglo_saxon',
|
||||
move=move.rec_name))
|
||||
super().delete(moves)
|
||||
def check_modification(cls, mode, moves, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, moves, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for move in moves:
|
||||
if (move.in_anglo_saxon_quantity
|
||||
or move.out_anglo_saxon_quantity):
|
||||
raise AccessError(gettext(
|
||||
'account_stock_anglo_saxon'
|
||||
'.msg_move_delete_anglo_saxon',
|
||||
move=move.rec_name))
|
||||
|
||||
@@ -60,19 +60,8 @@ class IntrastatTransaction(DeactivableMixin, ModelSQL, ModelView):
|
||||
return transaction
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
transactions = super().create(vlist)
|
||||
cls._code_cache.clear()
|
||||
return transactions
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._code_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, transactions):
|
||||
super().delete(transactions)
|
||||
def on_modification(cls, mode, transactions, field_names=None):
|
||||
super().on_modification(mode, transactions, field_names=field_names)
|
||||
cls._code_cache.clear()
|
||||
|
||||
|
||||
@@ -212,19 +201,8 @@ class IntrastatDeclaration(Workflow, ModelSQL, ModelView):
|
||||
cls.close(declarations)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
declarations = super().create(vlist)
|
||||
cls._get_cache.clear()
|
||||
return declarations
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._get_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, declarations):
|
||||
super().delete(declarations)
|
||||
def on_modification(cls, mode, declarations, field_names=None):
|
||||
super().on_modification(mode, declarations, field_names=field_names)
|
||||
cls._get_cache.clear()
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from trytond.model import Index, ModelView, Workflow, fields
|
||||
from trytond.modules.account.exceptions import FiscalYearNotFoundError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import CounterPartyNotFound, CountryNotFound
|
||||
|
||||
@@ -163,19 +163,13 @@ class Move(metaclass=PoolMeta):
|
||||
return self.intrastat_from_country
|
||||
|
||||
@classmethod
|
||||
@without_check_access
|
||||
def _reopen_intrastat(cls, *args):
|
||||
def _reopen_intrastat(cls, moves):
|
||||
pool = Pool()
|
||||
IntrastatDeclaration = pool.get(
|
||||
'account.stock.eu.intrastat.declaration')
|
||||
declarations = set()
|
||||
actions = iter(args)
|
||||
for moves, values in zip(actions, actions):
|
||||
moves = cls.browse(moves)
|
||||
if any(k.startswith('intrastat_') for k in values.keys()):
|
||||
declarations.update(
|
||||
m.intrastat_declaration for m in moves
|
||||
if m.intrastat_declaration)
|
||||
declarations = {
|
||||
m.intrastat_declaration for m in moves
|
||||
if m.intrastat_declaration}
|
||||
if declarations:
|
||||
IntrastatDeclaration.open(
|
||||
IntrastatDeclaration.browse(declarations))
|
||||
@@ -205,10 +199,12 @@ class Move(metaclass=PoolMeta):
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
cls._reopen_intrastat(*args)
|
||||
super().write(*args)
|
||||
cls._reopen_intrastat(*args)
|
||||
def on_write(cls, moves, values):
|
||||
callback = super().on_write(moves, values)
|
||||
callback.append(lambda: cls._reopen_intrastat(moves))
|
||||
if any(f.startswith('intrastat_') for f in values):
|
||||
cls._reopen_intrastat(moves)
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def copy(cls, moves, default=None):
|
||||
|
||||
@@ -410,29 +410,31 @@ class LandedCost(Workflow, ModelSQL, ModelView, MatchMixin):
|
||||
cls.save(landed_costs)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('account.configuration')
|
||||
|
||||
vlist = [v.copy() for v in vlist]
|
||||
config = Config(1)
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'landed_cost_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
values = super().preprocess_values(mode, values)
|
||||
if values.get('number') is None:
|
||||
config = Config(1)
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
if sequence := config.get_multivalue(
|
||||
'landed_cost_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def delete(cls, landed_costs):
|
||||
for landed_cost in landed_costs:
|
||||
if landed_cost.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('account_stock_landed_cost'
|
||||
'.msg_landed_cost_delete_cancel',
|
||||
landed_cost=landed_cost.rec_name))
|
||||
super().delete(landed_costs)
|
||||
def check_modification(
|
||||
cls, mode, landed_costs, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, landed_costs, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for landed_cost in landed_costs:
|
||||
if landed_cost.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('account_stock_landed_cost'
|
||||
'.msg_landed_cost_delete_cancel',
|
||||
landed_cost=landed_cost.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, landed_costs, default=None):
|
||||
|
||||
@@ -321,29 +321,31 @@ class ShipmentCost(Workflow, ModelSQL, ModelView):
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('account.configuration')
|
||||
|
||||
vlist = [v.copy() for v in vlist]
|
||||
config = Config(1)
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'shipment_cost_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
values = super().preprocess_values(mode, values)
|
||||
if values.get('number') is None:
|
||||
config = Config(1)
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
if sequence := config.get_multivalue(
|
||||
'shipment_cost_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def delete(cls, shipment_costs):
|
||||
for shipment_cost in shipment_costs:
|
||||
if shipment_cost.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('account_stock_shipment_cost'
|
||||
'.msg_shipment_cost_delete_cancel',
|
||||
shipment_cost=shipment_cost.rec_name))
|
||||
super().delete(shipment_costs)
|
||||
def check_modification(
|
||||
cls, mode, shipment_costs, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, shipment_costs, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for shipment_cost in shipment_costs:
|
||||
if shipment_cost.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('account_stock_shipment_cost'
|
||||
'.msg_shipment_cost_delete_cancel',
|
||||
shipment_cost=shipment_cost.rec_name))
|
||||
|
||||
|
||||
class ShipmentCost_Shipment(ModelSQL):
|
||||
|
||||
@@ -283,16 +283,10 @@ class Invoice(metaclass=PoolMeta):
|
||||
return ratio
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
|
||||
invoices = []
|
||||
actions = iter(args)
|
||||
for records, values in zip(actions, actions):
|
||||
if 'payment_lines' in values:
|
||||
invoices.extend(records)
|
||||
invoices = cls.browse(invoices)
|
||||
cls._update_tax_cash_basis(invoices)
|
||||
def on_modification(cls, mode, invoices, field_names=None):
|
||||
super().on_modification(mode, invoices, field_names=field_names)
|
||||
if mode == 'write' and 'payment_lines' in field_names:
|
||||
cls._update_tax_cash_basis(invoices)
|
||||
|
||||
@classmethod
|
||||
def process(cls, invoices):
|
||||
|
||||
@@ -305,24 +305,25 @@ class Account(
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, accounts, values=None, external=False):
|
||||
pool = Pool()
|
||||
Entry = pool.get('analytic.account.entry')
|
||||
actions = iter(args)
|
||||
for records, values in zip(actions, actions):
|
||||
if 'root' in values:
|
||||
for sub_records in grouped_slice(records):
|
||||
entries = Entry.search([
|
||||
('account', 'in', list(map(int, sub_records))),
|
||||
],
|
||||
limit=1, order=[])
|
||||
if entries:
|
||||
entry, = entries
|
||||
raise AccessError(gettext(
|
||||
'analytic_account'
|
||||
'.msg_analytic_account_root_change',
|
||||
account=entry.account.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, accounts, values=values, external=external)
|
||||
|
||||
if mode == 'write' and 'root' in values:
|
||||
for sub_records in grouped_slice(accounts):
|
||||
entries = Entry.search([
|
||||
('account', 'in', list(map(int, sub_records))),
|
||||
],
|
||||
limit=1, order=[])
|
||||
if entries:
|
||||
entry, = entries
|
||||
raise AccessError(gettext(
|
||||
'analytic_account'
|
||||
'.msg_analytic_account_root_change',
|
||||
account=entry.account.rec_name))
|
||||
|
||||
|
||||
class AccountContext(ModelView):
|
||||
|
||||
@@ -105,33 +105,27 @@ class Line(ModelSQL, ModelView):
|
||||
return clause
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, lines, field_names=None):
|
||||
pool = Pool()
|
||||
MoveLine = pool.get('account.move.line')
|
||||
lines = super().create(vlist)
|
||||
move_lines = [l.move_line for l in lines]
|
||||
MoveLine.set_analytic_state(move_lines)
|
||||
MoveLine.save(move_lines)
|
||||
return lines
|
||||
super().on_modification(mode, lines, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
move_lines = MoveLine.browse({l.move_line for l in lines})
|
||||
MoveLine.set_analytic_state(move_lines)
|
||||
MoveLine.save(move_lines)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
MoveLine = pool.get('account.move.line')
|
||||
super().write(*args)
|
||||
lines = sum(args[0:None:2], [])
|
||||
move_lines = [l.move_line for l in lines]
|
||||
MoveLine.set_analytic_state(move_lines)
|
||||
MoveLine.save(move_lines)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
pool = Pool()
|
||||
MoveLine = pool.get('account.move.line')
|
||||
move_lines = [l.move_line for l in lines]
|
||||
super().delete(lines)
|
||||
MoveLine.set_analytic_state(move_lines)
|
||||
MoveLine.save(move_lines)
|
||||
callback = super().on_delete(lines)
|
||||
move_lines = MoveLine.browse({l.move_line for l in lines})
|
||||
if move_lines:
|
||||
def set_state():
|
||||
MoveLine.set_analytic_state(move_lines)
|
||||
MoveLine.save(move_lines)
|
||||
callback.append(set_state)
|
||||
return callback
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
|
||||
@@ -98,9 +98,11 @@ class Line(ModelSQL, ModelView):
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
cls.check_closed_period(records, msg='delete')
|
||||
super().delete(records)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
cls.check_closed_period(lines, msg='delete')
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, records, field_names):
|
||||
|
||||
@@ -156,15 +156,15 @@ class Account(DeactivableMixin, ModelSQL, ModelView):
|
||||
bic=iban.bic))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
accounts = super().create(vlist)
|
||||
for account in accounts:
|
||||
if not account.bank:
|
||||
bank = account.guess_bank()
|
||||
if bank:
|
||||
account.bank = bank
|
||||
cls.save(accounts)
|
||||
return accounts
|
||||
def on_modification(cls, mode, accounts, field_names=None):
|
||||
super().on_modification(mode, accounts, field_names=field_names)
|
||||
if mode == 'create':
|
||||
for account in accounts:
|
||||
if not account.bank:
|
||||
bank = account.guess_bank()
|
||||
if bank:
|
||||
account.bank = bank
|
||||
cls.save(accounts)
|
||||
|
||||
def guess_bank(self):
|
||||
pool = Pool()
|
||||
|
||||
@@ -66,19 +66,12 @@ class Carrier(DeactivableMixin, ModelSQL, ModelView):
|
||||
return None, None
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
def on_modification(cls, mode, carriers, field_names=None):
|
||||
pool = Pool()
|
||||
CarrierSelection = pool.get('carrier.selection')
|
||||
carriers = super().create(*args, **kwargs)
|
||||
CarrierSelection._get_carriers_cache.clear()
|
||||
return carriers
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
pool = Pool()
|
||||
CarrierSelection = pool.get('carrier.selection')
|
||||
super().delete(*args, **kwargs)
|
||||
CarrierSelection._get_carriers_cache.clear()
|
||||
super().on_modification(mode, carriers, field_names=field_names)
|
||||
if mode in {'create', 'delete'}:
|
||||
CarrierSelection._get_carriers_cache.clear()
|
||||
|
||||
|
||||
class Selection(sequence_ordered(), MatchMixin, ModelSQL, ModelView):
|
||||
@@ -97,22 +90,6 @@ class Selection(sequence_ordered(), MatchMixin, ModelSQL, ModelView):
|
||||
carrier = fields.Many2One('carrier', 'Carrier', required=True,
|
||||
ondelete='CASCADE', help="The selected carrier.")
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
selections = super().create(*args, **kwargs)
|
||||
cls._get_carriers_cache.clear()
|
||||
return selections
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
super().write(*args, **kwargs)
|
||||
cls._get_carriers_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
cls._get_carriers_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def get_carriers(cls, pattern):
|
||||
pool = Pool()
|
||||
@@ -136,3 +113,8 @@ class Selection(sequence_ordered(), MatchMixin, ModelSQL, ModelView):
|
||||
|
||||
cls._get_carriers_cache.set(key, list(map(int, carriers)))
|
||||
return carriers
|
||||
|
||||
@classmethod
|
||||
def on_modification(cls, mode, selections, field_names=None):
|
||||
super().on_modification(mode, selections, field_names=field_names)
|
||||
cls._get_carriers_cache.clear()
|
||||
|
||||
@@ -83,13 +83,15 @@ class Carriage(sequence_ordered(), ShipmentCostMixin, ModelSQL, ModelView):
|
||||
return f'{self.carrier.rec_name} @ {self.shipment.rec_name}'
|
||||
|
||||
@classmethod
|
||||
def delete(cls, carriages):
|
||||
for carriage in carriages:
|
||||
if carriage.shipment_cost_readonly:
|
||||
raise AccessError(
|
||||
gettext('carrier_carriages.msg_shipment_carriage_delete',
|
||||
carriage=carriage.rec_name))
|
||||
super().delete(carriages)
|
||||
def check_modification(cls, mode, carriages, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, carriages, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for carriage in carriages:
|
||||
if carriage.shipment_cost_readonly:
|
||||
raise AccessError(gettext(
|
||||
'carrier_carriages.msg_shipment_carriage_delete',
|
||||
carriage=carriage.rec_name))
|
||||
|
||||
|
||||
class _ShipmentMixin:
|
||||
@@ -127,12 +129,14 @@ class _ShipmentMixin:
|
||||
return cost
|
||||
|
||||
@classmethod
|
||||
def delete(cls, shipments):
|
||||
def on_delete(cls, shipments):
|
||||
pool = Pool()
|
||||
Carriage = pool.get('stock.shipment.carriage')
|
||||
callback = super().on_delete(shipments)
|
||||
carriages = sum((s.carriages for s in shipments), ())
|
||||
super().delete(shipments)
|
||||
Carriage.delete(carriages)
|
||||
if carriages:
|
||||
callback.append(lambda: Carriage.delete(carriages))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def copy(cls, shipments, default=None):
|
||||
|
||||
+13
-19
@@ -99,27 +99,21 @@ class Company(ModelSQL, ModelView):
|
||||
return [('party.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if logo := values.get('logo'):
|
||||
values['logo'] = cls._logo_convert(logo)
|
||||
return super().create(vlist)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if logo := values.get('logo'):
|
||||
values['logo'] = cls._logo_convert(logo)
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for companies, values in zip(actions, actions):
|
||||
if logo := values.get('logo'):
|
||||
values = values.copy()
|
||||
values['logo'] = cls._logo_convert(logo)
|
||||
args.append(companies)
|
||||
args.append(values)
|
||||
super().write(*args)
|
||||
cls._logo_clear_cache(sum(args[0:None:2], []))
|
||||
# Restart the cache on the domain_get method
|
||||
Pool().get('ir.rule')._domain_get_cache.clear()
|
||||
def on_modification(cls, mode, companies, field_names=None):
|
||||
pool = Pool()
|
||||
Rule = pool.get('ir.rule')
|
||||
super().on_modification(mode, companies, field_names=field_names)
|
||||
if mode == 'write':
|
||||
if not field_names or 'logo' in field_names:
|
||||
cls._logo_clear_cache(companies)
|
||||
Rule._domain_get_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def _logo_convert(cls, image, **_params):
|
||||
|
||||
+9
-38
@@ -18,25 +18,10 @@ class UserCompany(ModelSQL):
|
||||
ondelete='CASCADE', required=True)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
records = super().create(vlist)
|
||||
User._get_companies_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
super().write(*args)
|
||||
User._get_companies_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
super().delete(records)
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
User._get_companies_cache.clear()
|
||||
|
||||
|
||||
@@ -48,25 +33,10 @@ class UserEmployee(ModelSQL):
|
||||
'company.employee', "Employee", ondelete='CASCADE', required=True)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
records = super().create(vlist)
|
||||
User._get_employees_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
super().write(*args)
|
||||
User._get_employees_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
pool = Pool()
|
||||
User = pool.get('res.user')
|
||||
super().delete(records)
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
User._get_employees_cache.clear()
|
||||
|
||||
|
||||
@@ -277,7 +247,8 @@ class User(metaclass=PoolMeta):
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._get_companies_cache.clear()
|
||||
cls._get_employees_cache.clear()
|
||||
def on_modification(cls, mode, users, field_names=None):
|
||||
super().on_modification(mode, users, field_names=field_names)
|
||||
if mode == 'write':
|
||||
cls._get_companies_cache.clear()
|
||||
cls._get_employees_cache.clear()
|
||||
|
||||
+11
-36
@@ -215,25 +215,12 @@ class Country(DeactivableMixin, ModelSQL, ModelView):
|
||||
return self in organization.countries
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for vals in vlist:
|
||||
for code in {'code', 'code3', 'code_numeric'}:
|
||||
if code in vals and vals[code]:
|
||||
vals[code] = vals[code].upper()
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for countries, values in zip(actions, actions):
|
||||
for code in {'code', 'code3', 'code_numeric'}:
|
||||
if values.get(code):
|
||||
values = values.copy()
|
||||
values[code] = values[code].upper()
|
||||
args.extend((countries, values))
|
||||
super().write(*args)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
for code in {'code', 'code3', 'code_numeric'}:
|
||||
if values.get(code):
|
||||
values[code] = values[code].upper()
|
||||
return values
|
||||
|
||||
|
||||
class Subdivision(DeactivableMixin, ModelSQL, ModelView):
|
||||
@@ -465,23 +452,11 @@ class Subdivision(DeactivableMixin, ModelSQL, ModelView):
|
||||
return Operator(column, value)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for vals in vlist:
|
||||
if 'code' in vals and vals['code']:
|
||||
vals['code'] = vals['code'].upper()
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for subdivisions, values in zip(actions, actions):
|
||||
if values.get('code'):
|
||||
values = values.copy()
|
||||
values['code'] = values['code'].upper()
|
||||
args.extend((subdivisions, values))
|
||||
super().write(*args)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if values.get('code'):
|
||||
values['code'] = values['code'].upper()
|
||||
return values
|
||||
|
||||
|
||||
class PostalCode(ModelSQL, ModelView):
|
||||
|
||||
@@ -100,16 +100,19 @@ class Cron(metaclass=PoolMeta):
|
||||
if r.Date == date.strftime('%d.%m.%Y')}
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, crons, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for crons, values in zip(actions, actions):
|
||||
if ({'rs_username', 'rs_password', 'rs_license_id'}
|
||||
& values.keys()):
|
||||
warning_name = Warning.format('rs_credential', crons)
|
||||
if Warning.check(warning_name):
|
||||
raise RSCredentialWarning(
|
||||
warning_name,
|
||||
gettext('currency_rs.msg_rs_credential_modified'))
|
||||
return super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, crons, values=values, external=external)
|
||||
|
||||
if (mode == 'write'
|
||||
and external
|
||||
and values.keys() & {
|
||||
'rs_username', 'rs_password', 'rs_license_id'}):
|
||||
warning_name = Warning.format('rs_credential', crons)
|
||||
if Warning.check(warning_name):
|
||||
raise RSCredentialWarning(
|
||||
warning_name,
|
||||
gettext('currency_rs.msg_rs_credential_modified'))
|
||||
|
||||
+28
-16
@@ -76,18 +76,24 @@ class Category(metaclass=PoolMeta):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, categories):
|
||||
def on_delete(cls, categories):
|
||||
pool = Pool()
|
||||
Product_TariffCode = pool.get('product-customs.tariff.code')
|
||||
|
||||
callback = super().on_delete(categories)
|
||||
|
||||
product_tariffcodes = set()
|
||||
products = [str(t) for t in categories]
|
||||
|
||||
super().delete(categories)
|
||||
|
||||
for products in grouped_slice(products):
|
||||
product_tariffcodes = Product_TariffCode.search([
|
||||
'product', 'in', list(products),
|
||||
])
|
||||
Product_TariffCode.delete(product_tariffcodes)
|
||||
product_tariffcodes.update(Product_TariffCode.search([
|
||||
'product', 'in', list(products),
|
||||
]))
|
||||
if product_tariffcodes:
|
||||
product_tariffcodes = Product_TariffCode.browse(
|
||||
product_tariffcodes)
|
||||
callback.append(
|
||||
lambda: Product_TariffCode.delete(product_tariffcodes))
|
||||
return callback
|
||||
|
||||
|
||||
class Template(metaclass=PoolMeta):
|
||||
@@ -145,18 +151,24 @@ class Template(metaclass=PoolMeta):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, templates):
|
||||
def on_delete(cls, templates):
|
||||
pool = Pool()
|
||||
Product_TariffCode = pool.get('product-customs.tariff.code')
|
||||
|
||||
callback = super().on_delete(templates)
|
||||
|
||||
product_tariffcodes = set()
|
||||
products = [str(t) for t in templates]
|
||||
|
||||
super().delete(templates)
|
||||
|
||||
for products in grouped_slice(products):
|
||||
product_tariffcodes = Product_TariffCode.search([
|
||||
'product', 'in', list(products),
|
||||
])
|
||||
Product_TariffCode.delete(product_tariffcodes)
|
||||
product_tariffcodes.update(Product_TariffCode.search([
|
||||
'product', 'in', list(products),
|
||||
]))
|
||||
if product_tariffcodes:
|
||||
product_tariffcodes = Product_TariffCode.browse(
|
||||
product_tariffcodes)
|
||||
callback.append(
|
||||
lambda: Product_TariffCode.delete(product_tariffcodes))
|
||||
return callback
|
||||
|
||||
|
||||
class Product_TariffCode(sequence_ordered(), ModelSQL, ModelView):
|
||||
|
||||
@@ -29,12 +29,11 @@ class User(metaclass=PoolMeta):
|
||||
return 'square'
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_modification(cls, mode, users, field_names=None):
|
||||
pool = Pool()
|
||||
View = pool.get('ir.ui.view')
|
||||
super().write(*args)
|
||||
for values in args[1:None:2]:
|
||||
if values.keys() & {'dashboard_layout', 'dashboard_actions'}:
|
||||
View._view_get_cache.clear()
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
break
|
||||
super().on_modification(mode, users, field_names=field_names)
|
||||
if (mode == 'write'
|
||||
and field_names & {'dashboard_layout', 'dashboard_actions'}):
|
||||
View._view_get_cache.clear()
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
|
||||
@@ -478,17 +478,6 @@ class IncomingOCRService(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
|
||||
getattr(self, f'_send_feedback_{self.type}')(document)
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
services = super().create(*args, **kwargs)
|
||||
cls._get_service_cache.clear()
|
||||
return services
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
super().write(*args, **kwargs)
|
||||
cls._get_service_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
def on_modification(cls, mode, services, field_names=None):
|
||||
super().on_modification(mode, services, field_names=field_names)
|
||||
cls._get_service_cache.clear()
|
||||
|
||||
@@ -322,16 +322,17 @@ class IncomingOCRService(metaclass=PoolMeta):
|
||||
return str(line.base)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, services, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for services, values in zip(actions, actions):
|
||||
if 'typless_api_key' in values.keys():
|
||||
warning_name = Warning.format('typless_credential', services)
|
||||
if Warning.check(warning_name):
|
||||
raise TyplessCredentialWarning(
|
||||
warning_name,
|
||||
gettext('document_incoming_ocr_typless'
|
||||
'.msg_typless_credential_modified'))
|
||||
return super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, services, values=values, external=external)
|
||||
|
||||
if mode == 'write' and external and 'typless_api_key' in values:
|
||||
warning_name = Warning.format('typless_credential', services)
|
||||
if Warning.check(warning_name):
|
||||
raise TyplessCredentialWarning(
|
||||
warning_name,
|
||||
gettext('document_incoming_ocr_typless'
|
||||
'.msg_typless_credential_modified'))
|
||||
|
||||
@@ -70,23 +70,8 @@ class Incoterm_Company(ModelSQL):
|
||||
company = fields.Many2One('company.company', "Company", required=True)
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
pool = Pool()
|
||||
Incoterm = pool.get('incoterm.incoterm')
|
||||
records = super().create(*args, **kwargs)
|
||||
Incoterm._get_incoterms_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
pool = Pool()
|
||||
Incoterm = pool.get('incoterm.incoterm')
|
||||
super().write(*args, **kwargs)
|
||||
Incoterm._get_incoterms_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
pool = Pool()
|
||||
Incoterm = pool.get('incoterm.incoterm')
|
||||
super().delete(*args, **kwargs)
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
Incoterm._get_incoterms_cache.clear()
|
||||
|
||||
@@ -12,7 +12,6 @@ from trytond.exceptions import LoginException
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.transaction import without_check_access
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
section = 'ldap_authentication'
|
||||
@@ -100,7 +99,6 @@ class User(metaclass=PoolMeta):
|
||||
for e in result]
|
||||
|
||||
@classmethod
|
||||
@without_check_access
|
||||
def _check_passwd_ldap_user(cls, logins):
|
||||
find = False
|
||||
try:
|
||||
@@ -119,23 +117,18 @@ class User(metaclass=PoolMeta):
|
||||
user=login))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
tocheck = []
|
||||
for values in vlist:
|
||||
if values.get('password') and 'login' in values:
|
||||
tocheck.append(values['login'])
|
||||
if tocheck:
|
||||
cls._check_passwd_ldap_user(tocheck)
|
||||
return super().create(vlist)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and values.get('password') and 'login' in values:
|
||||
cls._check_passwd_ldap_user([values['login']])
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for users, values in zip(actions, actions):
|
||||
if values.get('password'):
|
||||
logins = [x.login for x in users]
|
||||
cls._check_passwd_ldap_user(logins)
|
||||
super().write(*args)
|
||||
def check_modification(cls, mode, users, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, users, values=values, external=external)
|
||||
if mode == 'write' and values.get('password'):
|
||||
cls._check_passwd_ldap_user([u.login for u in users])
|
||||
|
||||
@classmethod
|
||||
def _login_ldap(cls, login, parameters):
|
||||
|
||||
@@ -852,13 +852,13 @@ class Record(ModelSQL, ModelView):
|
||||
return '(%s)' % self.id
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
# Ensure to get a different uuid for each record
|
||||
# default methods are called only once
|
||||
values.setdefault('uuid', cls.default_uuid())
|
||||
return super().create(vlist)
|
||||
return values
|
||||
|
||||
|
||||
class RecordActivity(Workflow, ModelSQL, ModelView):
|
||||
|
||||
@@ -31,23 +31,11 @@ class Parameter(DeactivableMixin, ModelSQL, ModelView):
|
||||
return self.name.title()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if values.get('name'):
|
||||
values['name'] = values['name'].lower()
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for parameters, values in zip(actions, actions):
|
||||
if values.get('name'):
|
||||
values = values.copy()
|
||||
values['name'] = values['name'].lower()
|
||||
args.extend((parameters, values))
|
||||
super().write(*args)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if values.get('name'):
|
||||
values['name'] = values['name'].lower()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def from_name(cls, name, create=True):
|
||||
|
||||
@@ -113,29 +113,15 @@ class Email(DeactivableMixin, ModelSQL, ModelView):
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _format_email(cls, records):
|
||||
for record in records:
|
||||
email = normalize_email(record.email).lower()
|
||||
if email != record.email:
|
||||
record.email = email
|
||||
cls.save(records)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
# Ensure to get a different token for each record
|
||||
# default methods are called only once
|
||||
values.setdefault('email_token', cls.default_email_token())
|
||||
records = super().create(vlist)
|
||||
cls._format_email(records)
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
records = sum(args[0:None:2], [])
|
||||
cls._format_email(records)
|
||||
if values.get('email'):
|
||||
values['email'] = normalize_email(values['email']).lower()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, records, fields_names):
|
||||
|
||||
+16
-39
@@ -225,16 +225,15 @@ class Address(
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for addresses, values in zip(actions, actions):
|
||||
if 'party' in values:
|
||||
for address in addresses:
|
||||
if address.party.id != values['party']:
|
||||
raise AccessError(
|
||||
gettext('party.msg_address_change_party',
|
||||
address=address.rec_name))
|
||||
super().write(*args)
|
||||
def check_modification(cls, mode, addresses, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, addresses, values=values, external=external)
|
||||
if mode == 'write' and 'party' in values:
|
||||
for address in addresses:
|
||||
if address.party.id != values['party']:
|
||||
raise AccessError(gettext(
|
||||
'party.msg_address_change_party',
|
||||
address=address.rec_name))
|
||||
|
||||
@fields.depends('subdivision', 'country')
|
||||
def on_change_country(self):
|
||||
@@ -299,19 +298,8 @@ ${subdivision}
|
||||
${COUNTRY}"""
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
records = super().create(*args, **kwargs)
|
||||
cls._get_format_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
super().write(*args, **kwargs)
|
||||
cls._get_format_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
def on_modification(cls, mode, formats, field_names=None):
|
||||
super().on_modification(mode, formats, field_names=field_names)
|
||||
cls._get_format_cache.clear()
|
||||
|
||||
@classmethod
|
||||
@@ -386,22 +374,6 @@ class SubdivisionType(DeactivableMixin, ModelSQL, ModelView):
|
||||
selection = Subdivision.fields_get(['type'])['type']['selection']
|
||||
return [(k, v) for k, v in selection if k is not None]
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
records = super().create(*args, **kwargs)
|
||||
cls._get_types_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args, **kwargs):
|
||||
super().write(*args, **kwargs)
|
||||
cls._get_types_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
cls._get_types_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def get_types(cls, country):
|
||||
key = country.code if country else None
|
||||
@@ -420,3 +392,8 @@ class SubdivisionType(DeactivableMixin, ModelSQL, ModelView):
|
||||
|
||||
cls._get_types_cache.set(key, types)
|
||||
return types
|
||||
|
||||
@classmethod
|
||||
def on_modification(cls, mode, types, field_names=None):
|
||||
super().on_modification(mode, types, field_names=field_names)
|
||||
cls._get_types_cache.clear()
|
||||
|
||||
@@ -64,19 +64,8 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, MultiValueMixin):
|
||||
return selection
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
records = super().create(vlist)
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
super().delete(records)
|
||||
def on_modification(cls, mode, configurations, field_names=None):
|
||||
super().on_modification(mode, configurations, field_names=field_names)
|
||||
ModelView._fields_view_get_cache.clear()
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -285,20 +285,15 @@ class ContactMechanism(
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
all_mechanisms = []
|
||||
for mechanisms, values in zip(actions, actions):
|
||||
all_mechanisms.extend(mechanisms)
|
||||
if 'party' in values:
|
||||
for mechanism in mechanisms:
|
||||
if mechanism.party.id != values['party']:
|
||||
raise AccessError(
|
||||
gettext('party'
|
||||
'.msg_contact_mechanism_change_party') % {
|
||||
'contact': mechanism.rec_name,
|
||||
})
|
||||
super().write(*args)
|
||||
def check_modification(cls, mode, mechanisms, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, mechanisms, values=values, external=external)
|
||||
if mode == 'write' and 'party' in values:
|
||||
for mechanism in mechanisms:
|
||||
if mechanism.party.id != values['party']:
|
||||
raise AccessError(gettext(
|
||||
'party.msg_contact_mechanism_change_party',
|
||||
contact=mechanism.rec_name))
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, mechanisms, field_names):
|
||||
|
||||
+6
-11
@@ -286,19 +286,14 @@ class Party(
|
||||
return config.get_multivalue('party_sequence', **pattern)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [x.copy() for x in vlist]
|
||||
missing_code = []
|
||||
for values in vlist:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
if not values.get('code'):
|
||||
missing_code.append(values)
|
||||
if sequence := cls._code_sequence():
|
||||
values['code'] = sequence.get()
|
||||
values.setdefault('addresses', None)
|
||||
if missing_code:
|
||||
if sequence := cls._code_sequence():
|
||||
for values, code in zip(
|
||||
missing_code, sequence.get_many(len(missing_code))):
|
||||
values['code'] = code
|
||||
return super().create(vlist)
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, parties, default=None):
|
||||
|
||||
@@ -195,8 +195,9 @@ class RelationAll(Relation, ModelView):
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Relation = pool.get('party.relation')
|
||||
vlist = cls._before_create(vlist)
|
||||
relations = Relation.create(vlist)
|
||||
return cls.browse([r.id * 2 for r in relations])
|
||||
return cls.browse(cls._after_create([r.id * 2 for r in relations]))
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
@@ -204,23 +205,19 @@ class RelationAll(Relation, ModelView):
|
||||
Relation = pool.get('party.relation')
|
||||
RelationType = pool.get('party.relation.type')
|
||||
|
||||
all_records = sum(args[0:None:2], [])
|
||||
ids, field_names, on_write, trigger_eligibles, *args = (
|
||||
cls._before_write(*args))
|
||||
|
||||
# Increase transaction counter
|
||||
Transaction().counter += 1
|
||||
|
||||
# Clean local cache
|
||||
for record in all_records:
|
||||
for record_id in (record.id, record.reverse_id):
|
||||
record._local_cache.pop(record_id, None)
|
||||
|
||||
# Clean cursor cache
|
||||
relations = cls.browse(ids)
|
||||
# Clean local cache of reverse
|
||||
for relation in relations:
|
||||
relation._local_cache.pop(relation.reverse_id, None)
|
||||
# Clean cursor cache of reverse
|
||||
for cache in Transaction().cache.values():
|
||||
if cls.__name__ in cache:
|
||||
cache_cls = cache[cls.__name__]
|
||||
for record in all_records:
|
||||
for record_id in (record.id, record.reverse_id):
|
||||
cache_cls.pop(record_id, None)
|
||||
for relation in relations:
|
||||
cache_cls.pop(relation.reverse_id, None)
|
||||
|
||||
actions = iter(args)
|
||||
args = []
|
||||
@@ -247,25 +244,23 @@ class RelationAll(Relation, ModelView):
|
||||
(cls.convert_instances(reverse_relations), reverse_values))
|
||||
Relation.write(*args)
|
||||
|
||||
cls._after_write(ids, field_names, on_write, trigger_eligibles)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, relations):
|
||||
pool = Pool()
|
||||
Relation = pool.get('party.relation')
|
||||
ids, on_delete = cls._before_delete(relations)
|
||||
|
||||
# Increase transaction counter
|
||||
Transaction().counter += 1
|
||||
|
||||
# Clean cursor cache
|
||||
# Clean cursor cache of reverse
|
||||
for cache in list(Transaction().cache.values()):
|
||||
for cache in (cache,
|
||||
list(cache.get('_language_cache', {}).values())):
|
||||
if cls.__name__ in cache:
|
||||
cache_cls = cache[cls.__name__]
|
||||
for record in relations:
|
||||
for record_id in (record.id, record.reverse_id):
|
||||
cache_cls.pop(record_id, None)
|
||||
if cls.__name__ in cache:
|
||||
cache_cls = cache[cls.__name__]
|
||||
for relation in relations:
|
||||
cache_cls.pop(relation.reverse_id, None)
|
||||
|
||||
Relation.delete(cls.convert_instances(relations))
|
||||
cls._after_delete(ids, on_delete)
|
||||
|
||||
|
||||
class Party(metaclass=PoolMeta):
|
||||
|
||||
@@ -97,15 +97,12 @@ class Category(tree(separator=' / '), ModelSQL, ModelView):
|
||||
return config.category_sequence
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
missing_code = [v for v in vlist if not v.get('code')]
|
||||
if missing_code:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('code'):
|
||||
if sequence := cls._code_sequence():
|
||||
for values, code in zip(
|
||||
missing_code, sequence.get_many(len(missing_code))):
|
||||
values['code'] = code
|
||||
return super().create(vlist)
|
||||
values['code'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, categories, default=None):
|
||||
|
||||
+20
-35
@@ -239,33 +239,23 @@ class Template(
|
||||
return config.template_sequence
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
vlist = [v.copy() for v in vlist]
|
||||
missing_code = []
|
||||
for values in vlist:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
values.setdefault('products', None)
|
||||
if not values.get('code'):
|
||||
missing_code.append(values)
|
||||
if missing_code:
|
||||
if sequence := cls._code_sequence():
|
||||
for values, code in zip(
|
||||
missing_code, sequence.get_many(len(missing_code))):
|
||||
values['code'] = code
|
||||
templates = super().create(vlist)
|
||||
products = sum((t.products for t in templates), ())
|
||||
Product.sync_code(products)
|
||||
return templates
|
||||
if sequence := cls._code_sequence():
|
||||
values['code'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_modification(cls, mode, templates, field_names=None):
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
super().write(*args)
|
||||
templates = sum(args[0:None:2], [])
|
||||
products = sum((t.products for t in templates), ())
|
||||
Product.sync_code(products)
|
||||
super().on_modification(mode, templates, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
products = sum((t.products for t in templates), ())
|
||||
Product.sync_code(products)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, templates, default=None):
|
||||
@@ -673,23 +663,18 @@ class Product(
|
||||
return config.product_sequence
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [x.copy() for x in vlist]
|
||||
missing_code = [v for v in vlist if not v.get('suffix_code')]
|
||||
if missing_code:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('suffix_code'):
|
||||
if sequence := cls._code_sequence():
|
||||
for values, code in zip(
|
||||
missing_code, sequence.get_many(len(missing_code))):
|
||||
values['suffix_code'] = code
|
||||
products = super().create(vlist)
|
||||
cls.sync_code(products)
|
||||
return products
|
||||
values['suffix_code'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
products = sum(args[0:None:2], [])
|
||||
cls.sync_code(products)
|
||||
def on_modification(cls, mode, products, field_names=None):
|
||||
super().on_modification(mode, products, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls.sync_code(products)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, products, default=None):
|
||||
|
||||
+24
-25
@@ -10,7 +10,6 @@ from trytond.model import (
|
||||
Check, DeactivableMixin, DigitsMixin, ModelSQL, ModelView, SymbolMixin,
|
||||
fields)
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import UOMAccessError, UOMValidationError
|
||||
|
||||
@@ -150,30 +149,30 @@ class Uom(SymbolMixin, DigitsMixin, DeactivableMixin, ModelSQL, ModelView):
|
||||
uom=uom.rec_name))
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
if Transaction().user == 0:
|
||||
super().write(*args)
|
||||
return
|
||||
|
||||
all_uoms = sum(args[0:None:2], [])
|
||||
old_uom = {
|
||||
uom.id: (uom.factor, uom.rate, uom.category) for uom in all_uoms}
|
||||
old_digits = {uom.id: uom.digits for uom in all_uoms}
|
||||
|
||||
super().write(*args)
|
||||
|
||||
for uom in all_uoms:
|
||||
for i, field in enumerate(['factor', 'rate', 'category']):
|
||||
if getattr(uom, field) != old_uom[uom.id][i]:
|
||||
raise UOMAccessError(
|
||||
gettext('product.msg_uom_modify_%s' % field,
|
||||
uom=uom.rec_name),
|
||||
gettext('product.msg_uom_modify_options'))
|
||||
if uom.digits < old_digits[uom.id]:
|
||||
raise UOMAccessError(
|
||||
gettext('product.msg_uom_decrease_digits',
|
||||
uom=uom.rec_name),
|
||||
gettext('product.msg_uom_modify_options'))
|
||||
def check_modification(cls, mode, uoms, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, uoms, values=values, external=external)
|
||||
if (mode == 'write'
|
||||
and values.keys() & {'factor', 'rate', 'category', 'digits'}):
|
||||
for uom in uoms:
|
||||
for field_name in values.keys() & {'factor', 'rate'}:
|
||||
if values[field_name] != getattr(uom, field_name):
|
||||
raise UOMAccessError(gettext(
|
||||
'product.msg_uom_modify_%s' % field_name,
|
||||
uom=uom.rec_name),
|
||||
gettext('product.msg_uom_modify_options'))
|
||||
if 'category' in values:
|
||||
if values['category'] != uom.category.id:
|
||||
raise UOMAccessError(gettext(
|
||||
'product.msg_uom_modify_category',
|
||||
uom=uom.rec_name),
|
||||
gettext('product.msg_uom_modify_options'))
|
||||
if 'digits' in values:
|
||||
if values['digits'] < uom.digits:
|
||||
raise UOMAccessError(gettext(
|
||||
'product.msg_uom_decrease_digits',
|
||||
uom=uom.rec_name),
|
||||
gettext('product.msg_uom_modify_options'))
|
||||
|
||||
@property
|
||||
def accurate_field(self):
|
||||
|
||||
@@ -155,13 +155,15 @@ class Move(metaclass=PoolMeta):
|
||||
super().cancel(moves)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, moves):
|
||||
for move in moves:
|
||||
if move.fifo_quantity:
|
||||
raise AccessError(
|
||||
gettext('product_cost_fifo.msg_move_delete_fifo',
|
||||
move=move.rec_name))
|
||||
super().delete(moves)
|
||||
def check_modification(cls, mode, moves, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, moves, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for move in moves:
|
||||
if move.fifo_quantity:
|
||||
raise AccessError(gettext(
|
||||
'product_cost_fifo.msg_move_delete_fifo',
|
||||
move=move.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, moves, default=None):
|
||||
|
||||
@@ -123,25 +123,17 @@ class ImageMixin(_ImageMixin):
|
||||
return set()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if values.get('image'):
|
||||
values['image'] = cls.convert(values['image'])
|
||||
return super().create(vlist)
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if values.get('image'):
|
||||
values['image'] = cls.convert(values['image'])
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
args = []
|
||||
for images, values in zip(actions, actions):
|
||||
if values.get('image'):
|
||||
values = values.copy()
|
||||
values['image'] = cls.convert(values['image'])
|
||||
args.append(images)
|
||||
args.append(values)
|
||||
super().write(*args)
|
||||
cls.clear_cache(sum(args[0:None:2], []))
|
||||
def on_modification(cls, mode, images, field_names=None):
|
||||
super().on_modification(mode, images, field_names=field_names)
|
||||
if mode == 'write':
|
||||
cls.clear_cache(images)
|
||||
|
||||
@classmethod
|
||||
def _round_size(cls, size):
|
||||
|
||||
@@ -180,16 +180,6 @@ class PriceListCache(ModelSQL):
|
||||
cache.product, [p.id for p in sub_products])))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
cls._get_cache.clear()
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
cls._get_cache.clear()
|
||||
return super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
cls._get_cache.clear()
|
||||
return super().delete(records)
|
||||
|
||||
+18
-20
@@ -94,15 +94,12 @@ class BOM(DeactivableMixin, ModelSQL, ModelView):
|
||||
return config.bom_sequence
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
missing_code = [v for v in vlist if not v.get('code')]
|
||||
if missing_code:
|
||||
def preprocess_values(cls, mode, values):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('code'):
|
||||
if sequence := cls._code_sequence():
|
||||
for values, code in zip(
|
||||
missing_code, sequence.get_many(len(missing_code))):
|
||||
values['code'] = code
|
||||
return super().create(vlist)
|
||||
values['code'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, records, default=None):
|
||||
@@ -205,27 +202,28 @@ class BOMOutput(BOMInput):
|
||||
return self.unit.floor(self.quantity * factor)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, outputs):
|
||||
def on_delete(cls, outputs):
|
||||
pool = Pool()
|
||||
ProductBOM = pool.get('product.product-production.bom')
|
||||
|
||||
callback = super().on_delete(outputs)
|
||||
|
||||
bom_products = [b for o in outputs for b in o.product.boms]
|
||||
super().delete(outputs)
|
||||
# Validate that output_products domain on bom is still valid
|
||||
ProductBOM._validate(bom_products, ['bom'])
|
||||
callback.append(lambda: ProductBOM._validate(bom_products, ['bom']))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_write(cls, outputs, values):
|
||||
pool = Pool()
|
||||
ProductBOM = pool.get('product.product-production.bom')
|
||||
actions = iter(args)
|
||||
bom_products = []
|
||||
for outputs, values in zip(actions, actions):
|
||||
if 'product' in values:
|
||||
bom_products.extend(
|
||||
[b for o in outputs for b in o.product.boms])
|
||||
super().write(*args)
|
||||
|
||||
callback = super().on_write(outputs, values)
|
||||
|
||||
bom_products = [b for o in outputs for b in o.product.boms]
|
||||
# Validate that output_products domain on bom is still valid
|
||||
ProductBOM._validate(bom_products, ['bom'])
|
||||
callback.append(lambda: ProductBOM._validate(bom_products, ['bom']))
|
||||
return callback
|
||||
|
||||
|
||||
class BOMTree(ModelView):
|
||||
|
||||
@@ -650,15 +650,18 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
cls.save(productions)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
productions = super().create(vlist)
|
||||
cls._set_move_planned_date(productions)
|
||||
return productions
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._set_move_planned_date(sum(args[::2], []))
|
||||
def on_modification(cls, mode, productions, field_names=None):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
super().on_modification(mode, productions, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls._set_move_planned_date(productions)
|
||||
elif mode == 'delete':
|
||||
moves = []
|
||||
for production in productions:
|
||||
moves.extend(production.inputs)
|
||||
moves.extend(production.outputs)
|
||||
Move.delete(moves)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, productions, default=None):
|
||||
|
||||
@@ -125,19 +125,12 @@ class Move(metaclass=PoolMeta):
|
||||
super().set_effective_date()
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cost_price_update = []
|
||||
actions = iter(args)
|
||||
for moves, values in zip(actions, actions):
|
||||
for move in moves:
|
||||
if (move.state == 'done'
|
||||
and move.production_input
|
||||
and 'cost_price' in values):
|
||||
cost_price_update.append(move)
|
||||
if cost_price_update:
|
||||
def on_modification(cls, mode, moves, field_names=None):
|
||||
super().on_modification(mode, moves, field_names=field_names)
|
||||
if mode == 'write' and 'cost_price' in field_names:
|
||||
cls.write(
|
||||
cost_price_update, {'production_cost_price_updated': True})
|
||||
[m for m in moves if m.state == 'done' and m.production_input],
|
||||
{'production_cost_price_updated': True})
|
||||
|
||||
|
||||
class ProductQuantitiesByWarehouseMove(metaclass=PoolMeta):
|
||||
|
||||
@@ -264,25 +264,21 @@ class Work(sequence_ordered(), ModelSQL, ModelView):
|
||||
return costs
|
||||
|
||||
@classmethod
|
||||
def create(cls, values):
|
||||
works = super().create(values)
|
||||
cls.set_state(works)
|
||||
return works
|
||||
def on_modification(cls, mode, works, field_names=None):
|
||||
super().on_modification(mode, works, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls.set_state(works)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
works = sum(args[0:None:2], [])
|
||||
cls.set_state(works)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, works):
|
||||
for work in works:
|
||||
if work.state not in {'request', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('production_work.msg_delete_request',
|
||||
work=work.rec_name))
|
||||
super().delete(works)
|
||||
def check_modification(cls, mode, works, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, works, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for work in works:
|
||||
if work.state not in {'request', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('production_work.msg_delete_request',
|
||||
work=work.rec_name))
|
||||
|
||||
|
||||
def set_work_state(func):
|
||||
@@ -290,8 +286,9 @@ def set_work_state(func):
|
||||
def wrapper(cls, cycles):
|
||||
pool = Pool()
|
||||
Work = pool.get('production.work')
|
||||
func(cls, cycles)
|
||||
result = func(cls, cycles)
|
||||
Work.set_state(Work.browse({c.work.id for c in cycles}))
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
|
||||
@@ -58,28 +58,19 @@ class Work(metaclass=PoolMeta):
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
works = super().create(vlist)
|
||||
cls._set_timesheet_work(works)
|
||||
return works
|
||||
def on_modification(cls, mode, works, field_names=None):
|
||||
super().on_modification(mode, works, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls._set_timesheet_work(works)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
works = sum(args[0:None:2], [])
|
||||
cls._set_timesheet_work(works)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, works):
|
||||
def on_delete(cls, works):
|
||||
pool = Pool()
|
||||
TimesheetWork = pool.get('timesheet.work')
|
||||
|
||||
callback = super().on_delete(works)
|
||||
timesheet_works = [w for pw in works for w in pw.timesheet_works]
|
||||
|
||||
super().delete(works)
|
||||
|
||||
if timesheet_works:
|
||||
TimesheetWork.delete(timesheet_works)
|
||||
callback.append(lambda: TimesheetWork.delete(timesheet_works))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def _set_timesheet_work(cls, works):
|
||||
|
||||
+12
-26
@@ -11,7 +11,7 @@ from trytond.model import (
|
||||
sum_tree, tree)
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Bool, Eval, If, PYSONEncoder, TimeDelta
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import WorkProgressValidationError
|
||||
|
||||
@@ -68,22 +68,10 @@ class WorkStatus(DeactivableMixin, sequence_ordered(), ModelSQL, ModelView):
|
||||
return status
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
cls._get_default_status_cache.clear()
|
||||
cls._get_window_domains_cache.clear()
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._get_default_status_cache.clear()
|
||||
cls._get_window_domains_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, status):
|
||||
cls._get_default_status_cache.clear()
|
||||
cls._get_window_domains_cache.clear()
|
||||
super().delete(status)
|
||||
|
||||
@classmethod
|
||||
def get_window_domains(cls, action):
|
||||
@@ -428,18 +416,16 @@ class Work(sequence_ordered(), tree(separator='\\'), ModelSQL, ModelView):
|
||||
return new_works
|
||||
|
||||
@classmethod
|
||||
def delete(cls, project_works):
|
||||
TimesheetWork = Pool().get('timesheet.work')
|
||||
|
||||
# Get the timesheet works linked to the project works
|
||||
timesheet_works = [
|
||||
w for pw in project_works for w in pw.timesheet_works]
|
||||
|
||||
super().delete(project_works)
|
||||
|
||||
def on_delete(cls, project_works):
|
||||
pool = Pool()
|
||||
TimesheetWork = pool.get('timesheet.work')
|
||||
callback = super().on_delete(project_works)
|
||||
timesheet_works = {
|
||||
w for pw in project_works for w in pw.timesheet_works}
|
||||
if timesheet_works:
|
||||
with without_check_access():
|
||||
TimesheetWork.delete(timesheet_works)
|
||||
timesheet_works = TimesheetWork.browse(timesheet_works)
|
||||
callback.append(lambda: TimesheetWork.delete(timesheet_works))
|
||||
return callback
|
||||
|
||||
@classmethod
|
||||
def search_global(cls, text):
|
||||
|
||||
@@ -11,7 +11,7 @@ from trytond.modules.account_invoice.exceptions import (
|
||||
InvoiceLineValidationError)
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.tools import grouped_slice, reduce_ids
|
||||
from trytond.transaction import Transaction, without_check_access
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class InvoiceLine(metaclass=PoolMeta):
|
||||
@@ -115,19 +115,3 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
default.setdefault('project_invoice_works')
|
||||
default.setdefault('project_invoice_progresses')
|
||||
return super().copy(lines, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
pool = Pool()
|
||||
WorkInvoicedProgress = pool.get('project.work.invoiced_progress')
|
||||
|
||||
progress = []
|
||||
for sub_ids in grouped_slice([l.id for l in lines]):
|
||||
progress += WorkInvoicedProgress.search([
|
||||
('invoice_line', 'in', sub_ids),
|
||||
])
|
||||
if progress:
|
||||
with without_check_access():
|
||||
WorkInvoicedProgress.delete(progress)
|
||||
|
||||
super().delete(lines)
|
||||
|
||||
@@ -426,25 +426,22 @@ class Work(Effort, Progress, Timesheet, metaclass=PoolMeta):
|
||||
return super().copy(records, default=default)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for works, values in zip(actions, actions):
|
||||
def check_modification(cls, mode, works, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, works, values=values, external=external)
|
||||
if mode == 'write':
|
||||
if ('effort_duration' in values
|
||||
and any(w.invoice_line for w in works)):
|
||||
work = next((w for w in works if w.invoice_line))
|
||||
raise AccessError(gettext(
|
||||
'project_invoice.msg_invoiced_work_modify_effort',
|
||||
work=work.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, works):
|
||||
if any(w.invoice_line for w in works):
|
||||
work = next((w for w in works if w.invoice_line))
|
||||
raise AccessError(gettext(
|
||||
'project_invoice.msg_invoiced_work_delete',
|
||||
work=work.rec_name))
|
||||
super().delete(works)
|
||||
elif mode == 'delete':
|
||||
if any(w.invoice_line for w in works):
|
||||
work = next((w for w in works if w.invoice_line))
|
||||
raise AccessError(gettext(
|
||||
'project_invoice.msg_invoiced_work_delete',
|
||||
work=work.rec_name))
|
||||
|
||||
@classmethod
|
||||
def get_invoice_methods(cls):
|
||||
|
||||
@@ -21,10 +21,11 @@ class Line(metaclass=PoolMeta):
|
||||
return super().copy(records, default=default)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for lines, values in zip(actions, actions):
|
||||
if (('duration' in values or 'work' in values)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'write':
|
||||
if (values.keys() & {'duration', 'work'}
|
||||
and any(l.invoice_line for l in lines)):
|
||||
line = next(l for l in lines if l.invoice_line)
|
||||
if 'duration' in values:
|
||||
@@ -34,13 +35,9 @@ class Line(metaclass=PoolMeta):
|
||||
raise AccessError(gettext(
|
||||
f'project_invoice.{msg}',
|
||||
line=line.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
if any(r.invoice_line for r in lines):
|
||||
line = next((l for l in lines if l.invoice_line))
|
||||
raise AccessError(gettext(
|
||||
'project_invoice.msg_invoiced_timesheet_line_delete',
|
||||
line=line.rec_name))
|
||||
super().delete(lines)
|
||||
elif mode == 'delete':
|
||||
if any(r.invoice_line for r in lines):
|
||||
line = next((l for l in lines if l.invoice_line))
|
||||
raise AccessError(gettext(
|
||||
'project_invoice.msg_invoiced_timesheet_line_delete',
|
||||
line=line.rec_name))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# 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.model import ModelSQL, ModelView, fields
|
||||
from trytond.pool import Pool
|
||||
|
||||
|
||||
class Allocation(ModelSQL, ModelView):
|
||||
@@ -26,34 +25,21 @@ class Allocation(ModelSQL, ModelView):
|
||||
return [('employee.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
Work = Pool().get('project.work')
|
||||
super().write(*args)
|
||||
|
||||
works = Work.search([
|
||||
('allocations', 'in',
|
||||
[a.id for allocations in args[::2] for a in allocations]),
|
||||
])
|
||||
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
for work in works:
|
||||
work.compute_dates()
|
||||
def on_modification(cls, mode, allocations, field_names=None):
|
||||
super().on_modification(mode, allocations, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
for allocation in allocations:
|
||||
allocation.work.reset_leveling()
|
||||
allocation.work.compute_dates()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
allocations = super().create(vlist)
|
||||
for allocation in allocations:
|
||||
allocation.work.reset_leveling()
|
||||
allocation.work.compute_dates()
|
||||
return allocations
|
||||
|
||||
@classmethod
|
||||
def delete(cls, allocations):
|
||||
def on_delete(cls, allocations):
|
||||
callback = super().on_delete(allocations)
|
||||
works = [a.work for a in allocations]
|
||||
super().delete(allocations)
|
||||
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
for work in works:
|
||||
work.compute_dates()
|
||||
def replan():
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
work.compute_dates()
|
||||
callback.append(replan)
|
||||
return callback
|
||||
|
||||
@@ -6,7 +6,7 @@ from functools import reduce
|
||||
from heapq import heappop, heappush
|
||||
|
||||
from trytond.model import ModelSQL, fields, tree
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.wizard import StateTransition, Wizard
|
||||
|
||||
@@ -550,41 +550,33 @@ class Work(tree(parent='successors'), metaclass=PoolMeta):
|
||||
self.parent.compute_dates()
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
|
||||
actions = iter(args)
|
||||
for works, values in zip(actions, actions):
|
||||
if 'effort' in values:
|
||||
for work in works:
|
||||
def on_modification(cls, mode, works, field_names=None):
|
||||
super().on_modification(mode, works, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
for work in works:
|
||||
if not field_names or 'effort' in field_names:
|
||||
work.reset_leveling()
|
||||
fields = ('constraint_start_time', 'constraint_finish_time',
|
||||
'effort')
|
||||
if reduce(lambda x, y: x or y in values, fields, False):
|
||||
for work in works:
|
||||
if not field_names or field_names & {
|
||||
'constraint_start_time', 'constraint_finish_time',
|
||||
'effort'}:
|
||||
work.compute_dates()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
works = super().create(vlist)
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
work.compute_dates()
|
||||
return works
|
||||
|
||||
@classmethod
|
||||
def delete(cls, works):
|
||||
def on_delete(cls, works):
|
||||
callback = super().on_delete(works)
|
||||
to_update = set()
|
||||
for work in works:
|
||||
if work.parent and work.parent not in works:
|
||||
to_update.add(work.parent)
|
||||
to_update.update(c for c in work.parent.children
|
||||
if c not in works)
|
||||
super().delete(works)
|
||||
to_update.update(
|
||||
c for c in work.parent.children if c not in works)
|
||||
|
||||
for work in to_update:
|
||||
work.reset_leveling()
|
||||
work.compute_dates()
|
||||
def replan():
|
||||
for work in to_update:
|
||||
work.reset_leveling()
|
||||
work.compute_dates()
|
||||
callback.append(replan)
|
||||
return callback
|
||||
|
||||
|
||||
class PredecessorSuccessor(ModelSQL):
|
||||
@@ -595,49 +587,35 @@ class PredecessorSuccessor(ModelSQL):
|
||||
'project.work', "Successor", ondelete='CASCADE', required=True)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
Work = Pool().get('project.work')
|
||||
super().write(*args)
|
||||
|
||||
work_ids = [v for values in args[1::2]
|
||||
for k, v in values.values()
|
||||
if k in ('predecessor', 'successor')]
|
||||
works = Work.browse(work_ids)
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
for work in works:
|
||||
work.compute_dates()
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
if mode == 'create':
|
||||
if not field_names or field_names & {
|
||||
'predecessor', 'successor', 'parent'}:
|
||||
for record in records:
|
||||
record.predecessor.reset_leveling()
|
||||
record.successor.reset_leveling()
|
||||
if record.predecessor.parent:
|
||||
record.predecessor.parent.compute_dates()
|
||||
|
||||
@classmethod
|
||||
def delete(cls, pred_succs):
|
||||
def on_delete(cls, records):
|
||||
callback = super().on_delete(records)
|
||||
works = set()
|
||||
parents = set()
|
||||
for pred_succ in pred_succs:
|
||||
works.update((pred_succ.predecessor,
|
||||
pred_succ.successor))
|
||||
for record in records:
|
||||
works.add(record.predecessor)
|
||||
works.add(record.successor)
|
||||
if record.predecessor.parent:
|
||||
parents.add(record.predecessor.parent)
|
||||
|
||||
if pred_succ.predecessor.parent:
|
||||
parents.add(pred_succ.predecessor.parent)
|
||||
|
||||
super().delete(pred_succs)
|
||||
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
|
||||
for parent in parents:
|
||||
parent.compute_dates()
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pred_succs = super().create(vlist)
|
||||
|
||||
for pred_succ in pred_succs:
|
||||
pred_succ.predecessor.reset_leveling()
|
||||
pred_succ.successor.reset_leveling()
|
||||
|
||||
if pred_succ.predecessor.parent:
|
||||
pred_succ.predecessor.parent.compute_dates()
|
||||
return pred_succs
|
||||
def replan():
|
||||
for work in works:
|
||||
work.reset_leveling()
|
||||
for parent in parents:
|
||||
parent.compute_dates()
|
||||
callback.append(replan)
|
||||
return callback
|
||||
|
||||
|
||||
class Leveling(Wizard):
|
||||
|
||||
@@ -21,11 +21,12 @@ def process_purchase(func):
|
||||
with without_check_access():
|
||||
purchases = set(
|
||||
p for i in cls.browse(invoices) for p in i.purchases)
|
||||
func(cls, invoices)
|
||||
result = func(cls, invoices)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -68,8 +69,8 @@ class Invoice(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
@process_purchase
|
||||
def delete(cls, invoices):
|
||||
super().delete(invoices)
|
||||
def on_delete(cls, invoices):
|
||||
return super().on_delete(invoices)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -160,7 +161,7 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
transaction = Transaction()
|
||||
@@ -169,8 +170,8 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
invoices = (l.invoice for l in cls.browse(lines)
|
||||
if l.type == 'line' and l.invoice)
|
||||
purchases = set(p for i in invoices for p in i.purchases)
|
||||
super().delete(lines)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return super().on_delete(lines)
|
||||
|
||||
@@ -65,13 +65,14 @@ class Template(metaclass=PoolMeta):
|
||||
yield product_supplier
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, templates, values=None, external=False):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
actions = iter(args)
|
||||
for templates, values in zip(actions, actions):
|
||||
if not values.get("purchase_uom"):
|
||||
continue
|
||||
|
||||
super().check_modification(
|
||||
mode, templates, values=values, external=external)
|
||||
|
||||
if mode == 'write' and values.get("purchase_uom"):
|
||||
for template in templates:
|
||||
if not template.purchase_uom:
|
||||
continue
|
||||
@@ -84,7 +85,6 @@ class Template(metaclass=PoolMeta):
|
||||
if Warning.check(name):
|
||||
raise PurchaseUOMWarning(
|
||||
name, gettext('purchase.msg_change_purchase_uom'))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, templates, default=None):
|
||||
|
||||
@@ -895,15 +895,15 @@ class Purchase(
|
||||
if l.moves_progress is not None))))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, purchases):
|
||||
# Cancel before delete
|
||||
cls.cancel(purchases)
|
||||
for purchase in purchases:
|
||||
if purchase.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_delete_cancel',
|
||||
purchase=purchase.rec_name))
|
||||
super().delete(purchases)
|
||||
def check_modification(cls, mode, purchases, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, purchases, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for purchase in purchases:
|
||||
if purchase.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'purchase.msg_purchase_delete_cancel',
|
||||
purchase=purchase.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -2105,26 +2105,22 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
purchase_ids = filter(None, {v.get('purchase') for v in vlist})
|
||||
for purchase in Purchase.browse(list(purchase_ids)):
|
||||
if purchase.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_line_create_draft',
|
||||
purchase=purchase.rec_name))
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
for line in lines:
|
||||
if line.purchase_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('purchase.msg_purchase_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
purchase=line.purchase.rec_name))
|
||||
super().delete(lines)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for line in lines:
|
||||
if line.purchase.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'purchase.msg_purchase_line_create_draft',
|
||||
purchase=line.purchase.rec_name))
|
||||
elif mode == 'delete':
|
||||
for line in lines:
|
||||
if line.purchase_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'purchase.msg_purchase_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
purchase=line.purchase.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
|
||||
@@ -22,11 +22,12 @@ def process_purchase(moves_field):
|
||||
with without_check_access():
|
||||
purchases = set(m.purchase for s in cls.browse(shipments)
|
||||
for m in getattr(s, moves_field) if m.purchase)
|
||||
func(cls, shipments)
|
||||
result = func(cls, shipments)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return result
|
||||
return wrapper
|
||||
return _process_purchase
|
||||
|
||||
@@ -124,11 +125,11 @@ def process_purchase_move(without_shipment=False):
|
||||
if without_shipment:
|
||||
p_moves = [m for m in p_moves if not m.shipment]
|
||||
purchases = set(m.purchase for m in p_moves if m.purchase)
|
||||
func(cls, moves)
|
||||
if purchases:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Purchase.__queue__.process(purchases)
|
||||
return func(cls, moves)
|
||||
return wrapper
|
||||
return _process_purchase_move
|
||||
|
||||
@@ -250,8 +251,8 @@ class Move(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
@process_purchase_move()
|
||||
def delete(cls, moves):
|
||||
super().delete(moves)
|
||||
def on_delete(cls, moves):
|
||||
return super().on_delete(moves)
|
||||
|
||||
|
||||
class Location(metaclass=PoolMeta):
|
||||
|
||||
@@ -19,13 +19,14 @@ def process_request(func):
|
||||
def wrapper(cls, purchases):
|
||||
pool = Pool()
|
||||
Request = pool.get('purchase.request')
|
||||
func(cls, purchases)
|
||||
result = func(cls, purchases)
|
||||
with without_check_access():
|
||||
requests = [
|
||||
r for p in cls.browse(purchases)
|
||||
for l in p.lines
|
||||
for r in l.requests]
|
||||
Request.update_state(requests)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -33,19 +34,16 @@ class Purchase(metaclass=PoolMeta):
|
||||
__name__ = 'purchase.purchase'
|
||||
|
||||
@classmethod
|
||||
def delete(cls, purchases):
|
||||
cls.check_delete_purchase_request(purchases)
|
||||
super().delete(purchases)
|
||||
|
||||
@classmethod
|
||||
@without_check_access
|
||||
def check_delete_purchase_request(cls, purchases):
|
||||
for purchase in purchases:
|
||||
for line in purchase.lines:
|
||||
if line.requests:
|
||||
raise AccessError(
|
||||
gettext('purchase_request.msg_purchase_delete_request',
|
||||
purchase=purchase.rec_name))
|
||||
def check_modification(cls, mode, purchases, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, purchases, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for purchase in purchases:
|
||||
for line in purchase.lines:
|
||||
if line.requests:
|
||||
raise AccessError(gettext(
|
||||
'purchase_request.msg_purchase_delete_request',
|
||||
purchase=purchase.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -103,11 +101,12 @@ class Line(metaclass=PoolMeta):
|
||||
return super().copy(lines, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Request = pool.get('purchase.request')
|
||||
with without_check_access():
|
||||
requests = [r for l in cls.browse(lines) for r in l.requests]
|
||||
super().delete(lines)
|
||||
with without_check_access():
|
||||
Request.update_state(requests)
|
||||
callback = super().on_delete(lines)
|
||||
requests = {r for l in lines for r in l.requests}
|
||||
if requests:
|
||||
requests = Request.browse(requests)
|
||||
callback.append(lambda: Request.update_state(requests))
|
||||
return callback
|
||||
|
||||
@@ -268,21 +268,19 @@ class PurchaseRequest(ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
transaction = Transaction()
|
||||
if transaction.user and transaction.check_access:
|
||||
raise AccessError(
|
||||
gettext('purchase_request.msg_request_no_create'))
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, requests):
|
||||
for request in requests:
|
||||
if request.purchase_line:
|
||||
def check_modification(cls, mode, requests, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, requests, values=values, external=external)
|
||||
if mode == 'create':
|
||||
if external:
|
||||
raise AccessError(
|
||||
gettext('purchase_request.msg_request_delete_purchased',
|
||||
request=request.rec_name))
|
||||
super().delete(requests)
|
||||
gettext('purchase_request.msg_request_no_create'))
|
||||
elif mode == 'delete':
|
||||
for request in requests:
|
||||
if request.purchase_line:
|
||||
raise AccessError(gettext(
|
||||
'purchase_request.msg_request_delete_purchased',
|
||||
request=request.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, requests, default=None):
|
||||
|
||||
@@ -27,9 +27,10 @@ def process_request(func):
|
||||
def wrapper(cls, quotations):
|
||||
pool = Pool()
|
||||
Request = pool.get('purchase.request')
|
||||
func(cls, quotations)
|
||||
result = func(cls, quotations)
|
||||
requests = [l.request for q in quotations for l in q.lines]
|
||||
Request.update_state(requests)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -411,12 +412,15 @@ class QuotationLine(ModelSQL, ModelView):
|
||||
return domain
|
||||
|
||||
@classmethod
|
||||
def delete(cls, quotationlines):
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Request = pool.get('purchase.request')
|
||||
requests = [l.request for l in quotationlines]
|
||||
super().delete(quotationlines)
|
||||
Request.update_state(requests)
|
||||
callback = super().on_delete(lines)
|
||||
requests = {l.request for l in lines}
|
||||
if requests:
|
||||
requests = Request.browse(requests)
|
||||
callback.append(lambda: Request.update_state(requests))
|
||||
return callback
|
||||
|
||||
def get_product(self, name):
|
||||
if self.request and self.request.product:
|
||||
|
||||
@@ -283,30 +283,31 @@ class PurchaseRequisition(Workflow, ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('purchase.configuration')
|
||||
|
||||
config = Config(1)
|
||||
vlist = [v.copy() for v in vlist]
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'purchase_requisition_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
Configuration = pool.get('purchase.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'purchase_requisition_sequence',
|
||||
company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def delete(cls, requisitions):
|
||||
# Cancel before delete
|
||||
cls.cancel(requisitions)
|
||||
for requisition in requisitions:
|
||||
if requisition.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('purchase_requisition.msg_delete_cancel',
|
||||
requisition=requisition.rec_name))
|
||||
super().delete(requisitions)
|
||||
def check_modification(
|
||||
cls, mode, requisitions, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, requisitions, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for requisition in requisitions:
|
||||
if requisition.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'purchase_requisition.msg_delete_cancel',
|
||||
requisition=requisition.rec_name))
|
||||
|
||||
def check_for_waiting(self):
|
||||
if not self.warehouse:
|
||||
|
||||
+31
-30
@@ -563,19 +563,30 @@ class Inspection(Workflow, ModelSQL, ModelView):
|
||||
origin=self)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('quality.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'inspection_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
config = Configuration(1)
|
||||
default_company = cls.default_company()
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if not values.get('number'):
|
||||
values['number'] = config.get_multivalue(
|
||||
'inspection_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
@classmethod
|
||||
def check_modification(
|
||||
cls, mode, inspections, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, inspections, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for inspection in inspections:
|
||||
if inspection.state != 'pending':
|
||||
raise AccessError(gettext(
|
||||
'quality.msg_inspection_delete_non_pending',
|
||||
inspection=inspection.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, inspections, default=None):
|
||||
@@ -587,15 +598,6 @@ class Inspection(Workflow, ModelSQL, ModelView):
|
||||
default.setdefault('failed_by')
|
||||
return super().copy(inspections, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, inspections):
|
||||
for inspection in inspections:
|
||||
if inspection.state != 'pending':
|
||||
raise AccessError(
|
||||
gettext('quality.msg_inspection_delete_non_pending',
|
||||
inspection=inspection.rec_name))
|
||||
super().delete(inspections)
|
||||
|
||||
@classmethod
|
||||
def get_from_control(cls, control, origin=None):
|
||||
return cls(control=control, origin=origin)
|
||||
@@ -721,19 +723,18 @@ class Alert(Workflow, ModelSQL, ModelView):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('quality.configuration')
|
||||
|
||||
config = Configuration(1)
|
||||
default_company = cls.default_company()
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if not values.get('number'):
|
||||
values['number'] = config.get_multivalue(
|
||||
'alert_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'alert_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, inspections, default=None):
|
||||
|
||||
@@ -19,11 +19,12 @@ def process_sale(func):
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
sales = set(s for i in cls.browse(invoices) for s in i.sales)
|
||||
func(cls, invoices)
|
||||
result = func(cls, invoices)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -77,8 +78,8 @@ class Invoice(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
def delete(cls, invoices):
|
||||
super().delete(invoices)
|
||||
def on_delete(cls, invoices):
|
||||
return super().on_delete(invoices)
|
||||
|
||||
@classmethod
|
||||
@process_sale
|
||||
@@ -158,7 +159,7 @@ class Line(metaclass=PoolMeta):
|
||||
return models
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
def on_delete(cls, lines):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
transaction = Transaction()
|
||||
@@ -167,8 +168,8 @@ class Line(metaclass=PoolMeta):
|
||||
invoices = (l.invoice for l in cls.browse(lines)
|
||||
if l.type == 'line' and l.invoice)
|
||||
sales = set(s for i in invoices for s in i.sales)
|
||||
super().delete(lines)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return super().on_delete(lines)
|
||||
|
||||
+25
-29
@@ -1096,15 +1096,15 @@ class Sale(
|
||||
if l.moves_progress is not None))))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, sales):
|
||||
# Cancel before delete
|
||||
cls.cancel(sales)
|
||||
for sale in sales:
|
||||
if sale.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_delete_cancel',
|
||||
sale=sale.rec_name))
|
||||
super().delete(sales)
|
||||
def check_modification(cls, mode, sales, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, sales, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for sale in sales:
|
||||
if sale.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'sale.msg_sale_delete_cancel',
|
||||
sale=sale.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -2240,26 +2240,22 @@ class SaleLine(TaxableMixin, sequence_ordered(), ModelSQL, ModelView):
|
||||
'spell', Eval('_parent_sale', {}).get('party_lang'))]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
sale_ids = filter(None, {v.get('sale') for v in vlist})
|
||||
for sale in Sale.browse(list(sale_ids)):
|
||||
if sale.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_line_create_draft',
|
||||
sale=sale.rec_name))
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
for line in lines:
|
||||
if line.sale_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('sale.msg_sale_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
sale=line.sale.rec_name))
|
||||
super().delete(lines)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'create':
|
||||
for line in lines:
|
||||
if line.sale.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'sale.msg_sale_line_create_draft',
|
||||
sale=line.sale.rec_name))
|
||||
elif mode == 'delete':
|
||||
for line in lines:
|
||||
if line.sale_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'sale.msg_sale_line_delete_cancel_draft',
|
||||
line=line.rec_name,
|
||||
sale=line.sale.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
|
||||
@@ -22,11 +22,12 @@ def process_sale(moves_field):
|
||||
with without_check_access():
|
||||
sales = set(m.sale for s in cls.browse(shipments)
|
||||
for m in getattr(s, moves_field) if m.sale)
|
||||
func(cls, shipments)
|
||||
result = func(cls, shipments)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
return _process_sale
|
||||
|
||||
@@ -112,11 +113,12 @@ def process_sale_move(func):
|
||||
context = transaction.context
|
||||
with without_check_access():
|
||||
sales = set(m.sale for m in cls.browse(moves) if m.sale)
|
||||
func(cls, moves)
|
||||
result = func(cls, moves)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -230,5 +232,5 @@ class Move(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
@process_sale_move
|
||||
def delete(cls, moves):
|
||||
super().delete(moves)
|
||||
def on_delete(cls, moves):
|
||||
return super().on_delete(moves)
|
||||
|
||||
@@ -279,19 +279,29 @@ class Complaint(Workflow, ModelSQL, ModelView):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('sale.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'complaint_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
config = Configuration(1)
|
||||
vlist = [v.copy() for v in vlist]
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'complaint_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
@classmethod
|
||||
def check_modification(cls, mode, complaints, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, complaints, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for complaint in complaints:
|
||||
if complaint.state != 'draft':
|
||||
raise AccessError(gettext(
|
||||
'sale_complaint.msg_complaint_delete_draft',
|
||||
complaint=complaint.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, complaints, default=None):
|
||||
@@ -306,15 +316,6 @@ class Complaint(Workflow, ModelSQL, ModelView):
|
||||
default.setdefault('cancelled_by')
|
||||
return super().copy(complaints, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, complaints):
|
||||
for complaint in complaints:
|
||||
if complaint.state != 'draft':
|
||||
raise AccessError(
|
||||
gettext('sale_complaint.msg_complaint_delete_draft',
|
||||
complaint=complaint.rec_name))
|
||||
super().delete(complaints)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@@ -721,13 +722,15 @@ class Action(ModelSQL, ModelView):
|
||||
return credit_note
|
||||
|
||||
@classmethod
|
||||
def delete(cls, actions):
|
||||
for action in actions:
|
||||
if action.result:
|
||||
raise AccessError(
|
||||
gettext('sale_complaint.msg_action_delete_result',
|
||||
action=action.rec_name))
|
||||
super().delete(actions)
|
||||
def check_modification(cls, mode, actions, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, actions, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for action in actions:
|
||||
if action.result:
|
||||
raise AccessError(gettext(
|
||||
'sale_complaint.msg_action_delete_result',
|
||||
action=action.rec_name))
|
||||
|
||||
|
||||
class _Action_Line:
|
||||
|
||||
@@ -300,19 +300,30 @@ class SaleOpportunity(
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('sale.configuration')
|
||||
Configuration = pool.get('sale.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'sale_opportunity_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
config = Config(1)
|
||||
vlist = [x.copy() for x in vlist]
|
||||
default_company = cls.default_company()
|
||||
for vals in vlist:
|
||||
if vals.get('number') is None:
|
||||
vals['number'] = config.get_multivalue(
|
||||
'sale_opportunity_sequence',
|
||||
company=vals.get('company', default_company)).get()
|
||||
return super().create(vlist)
|
||||
@classmethod
|
||||
def check_modification(
|
||||
cls, mode, opportunities, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, opportunities, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for opportunity in opportunities:
|
||||
if opportunity.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'sale_opportunity.msg_opportunity_delete_cancel',
|
||||
opportunity=opportunity.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, opportunities, default=None):
|
||||
@@ -371,17 +382,6 @@ class SaleOpportunity(
|
||||
sale.lines = sale_lines
|
||||
return sale
|
||||
|
||||
@classmethod
|
||||
def delete(cls, opportunities):
|
||||
# Cancel before delete
|
||||
cls.cancel(opportunities)
|
||||
for opportunity in opportunities:
|
||||
if opportunity.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('sale_opportunity.msg_opportunity_delete_cancel',
|
||||
opportunity=opportunity.rec_name))
|
||||
super().delete(opportunities)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('lead')
|
||||
|
||||
@@ -17,9 +17,10 @@ def process_opportunity(func):
|
||||
opportunities = Opportunity.browse(
|
||||
set(s.origin for s in cls.browse(sales)
|
||||
if isinstance(s.origin, Opportunity)))
|
||||
func(cls, sales)
|
||||
result = func(cls, sales)
|
||||
with without_check_access():
|
||||
Opportunity.process(opportunities)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -31,28 +32,24 @@ class Sale(metaclass=PoolMeta):
|
||||
return super()._get_origin() + ['sale.opportunity']
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, sales, values=None, external=False):
|
||||
pool = Pool()
|
||||
Opportunity = pool.get('sale.opportunity')
|
||||
actions = iter(args)
|
||||
for sales, values in zip(actions, actions):
|
||||
if 'origin' in values:
|
||||
origin = values['origin']
|
||||
if origin and not isinstance(origin, str):
|
||||
origin = '%s,%s' % tuple(origin)
|
||||
for sale in sales:
|
||||
if (isinstance(sale.origin, Opportunity)
|
||||
and str(sale.origin) != origin):
|
||||
raise AccessError(gettext(
|
||||
'sale_opportunity'
|
||||
'.msg_modify_origin_opportunity',
|
||||
sale=sale.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
@process_opportunity
|
||||
def delete(cls, sales):
|
||||
super().delete(sales)
|
||||
super().check_modification(
|
||||
mode, sales, values=values, external=external)
|
||||
|
||||
if mode == 'write' and 'origin' in values:
|
||||
origin = values['origin']
|
||||
if origin and not isinstance(origin, str):
|
||||
origin = '%s,%s' % tuple(origin)
|
||||
for sale in sales:
|
||||
if (isinstance(sale.origin, Opportunity)
|
||||
and str(sale.origin) != origin):
|
||||
raise AccessError(gettext(
|
||||
'sale_opportunity'
|
||||
'.msg_modify_origin_opportunity',
|
||||
sale=sale.rec_name))
|
||||
|
||||
@classmethod
|
||||
@process_opportunity
|
||||
|
||||
@@ -70,18 +70,20 @@ class Payment(metaclass=PoolMeta):
|
||||
self.currency = currency
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
payments = super().create(vlist)
|
||||
cls.trigger_authorized([p for p in payments if p.is_authorized])
|
||||
return payments
|
||||
def on_modification(cls, mode, payments, field_names=None):
|
||||
super().on_modification(mode, payments, field_names=field_names)
|
||||
if mode == 'create':
|
||||
cls.trigger_authorized([p for p in payments if p.is_authorized])
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
payments = sum(args[0:None:2], [])
|
||||
unauthorized = {p for p in payments if not p.is_authorized}
|
||||
super().write(*args)
|
||||
authorized = {p for p in payments if p.is_authorized}
|
||||
cls.trigger_authorized(cls.browse(unauthorized & authorized))
|
||||
def on_write(cls, payments, values):
|
||||
callback = super().on_write(payments, values)
|
||||
if unauthorized := {p for p in payments if not p.is_authorized}:
|
||||
def trigger():
|
||||
authorized = {p for p in payments if p.is_authorized}
|
||||
cls.trigger_authorized(cls.browse(unauthorized & authorized))
|
||||
callback.append(trigger)
|
||||
return callback
|
||||
|
||||
@property
|
||||
def is_authorized(self): # TODO: move to account_payment
|
||||
|
||||
+78
-65
@@ -72,24 +72,24 @@ class POS(ModelSQL, ModelView):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def check_modification(cls, mode, points, values=None, external=False):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.point.sale')
|
||||
transaction = Transaction()
|
||||
if transaction.user and transaction.check_access:
|
||||
actions = iter(args)
|
||||
for points, values in zip(actions, actions):
|
||||
if 'tax_included' in values:
|
||||
for sub_points in grouped_slice(points):
|
||||
if Sale.search([
|
||||
('point', 'in',
|
||||
list(map(int, sub_points))),
|
||||
],
|
||||
limit=1, order=[]):
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_point_change_tax_included')
|
||||
)
|
||||
super().write(*args)
|
||||
|
||||
super().check_modification(
|
||||
mode, points, values=values, external=external)
|
||||
|
||||
if mode == 'write':
|
||||
if 'tax_included' in values:
|
||||
for sub_points in grouped_slice(points):
|
||||
if Sale.search([
|
||||
('point', 'in',
|
||||
list(map(int, sub_points))),
|
||||
],
|
||||
limit=1, order=[]):
|
||||
raise AccessError(gettext(
|
||||
'sale_point.'
|
||||
'msg_point_change_tax_included'))
|
||||
|
||||
|
||||
class POSSale(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
@@ -276,13 +276,15 @@ class POSSale(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
return super().copy(sales, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, sales):
|
||||
for sale in sales:
|
||||
if sale.number:
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_sale_delete_numbered',
|
||||
sale=sale.rec_name))
|
||||
super().delete(sales)
|
||||
def check_modification(cls, mode, sales, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, sales, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for sale in sales:
|
||||
if sale.number:
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_sale_delete_numbered',
|
||||
sale=sale.rec_name))
|
||||
|
||||
@classmethod
|
||||
def validate(cls, sales):
|
||||
@@ -909,14 +911,21 @@ class POSCashSession(Workflow, ModelSQL, ModelView):
|
||||
session.currency)))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, sessions):
|
||||
cls.open(sessions)
|
||||
for session in sessions:
|
||||
if session.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_cash_session_delete_open',
|
||||
session=session.rec_name))
|
||||
super().delete(sessions)
|
||||
def on_modification(cls, mode, sessions, field_names=None):
|
||||
super().on_modification(mode, sessions, field_names=field_names)
|
||||
if mode == 'delete':
|
||||
cls.open(sessions)
|
||||
|
||||
@classmethod
|
||||
def check_modification(cls, mode, sessions, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, sessions, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for session in sessions:
|
||||
if session.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_cash_session_delete_open',
|
||||
session=session.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -1054,34 +1063,36 @@ class POSPayment(ModelSQL, ModelView):
|
||||
return self.method.cash
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Method = pool.get('sale.point.payment.method')
|
||||
Sale = pool.get('sale.point.sale')
|
||||
Session = pool.get('sale.point.cash.session')
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
if values.get('method') and values.get('sale'):
|
||||
method = Method(values['method'])
|
||||
if method.cash:
|
||||
sale = Sale(values['sale'])
|
||||
values['session'] = Session.get_current(sale.point)
|
||||
return super().create(vlist)
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def delete(cls, payments):
|
||||
for payment in payments:
|
||||
if payment.sale.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_payment_delete_sale_open',
|
||||
payment=payment.rec_name,
|
||||
sale=payment.sale.rec_name))
|
||||
if payment.session and payment.session.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_payment_delete_session_open',
|
||||
payment=payment.rec_name,
|
||||
session=payment.session.rec_name))
|
||||
super().delete(payments)
|
||||
def check_modification(cls, mode, payments, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, payments, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for payment in payments:
|
||||
if payment.sale.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_payment_delete_sale_open',
|
||||
payment=payment.rec_name,
|
||||
sale=payment.sale.rec_name))
|
||||
if payment.session and payment.session.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_payment_delete_session_open',
|
||||
payment=payment.rec_name,
|
||||
session=payment.session.rec_name))
|
||||
|
||||
def get_account_move_lines(self):
|
||||
"Return account move lines"
|
||||
@@ -1258,18 +1269,20 @@ class POSCashTransfer(Workflow, ModelSQL, ModelView):
|
||||
return self.point.company.currency if self.point else None
|
||||
|
||||
@classmethod
|
||||
def delete(cls, transfers):
|
||||
for transfer in transfers:
|
||||
if transfer.state == 'posted':
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_transfer_delete_posted',
|
||||
transfer=transfer.rec_name))
|
||||
if transfer.session.state != 'open':
|
||||
raise AccessError(
|
||||
gettext('sale_point.msg_transfer_delete_session_open',
|
||||
transfer=transfer.rec_name,
|
||||
session=transfer.session.rec_name))
|
||||
super().delete(transfers)
|
||||
def check_modification(cls, mode, transfers, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, transfers, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for transfer in transfers:
|
||||
if transfer.state == 'posted':
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_transfer_delete_posted',
|
||||
transfer=transfer.rec_name))
|
||||
if transfer.session.state != 'open':
|
||||
raise AccessError(gettext(
|
||||
'sale_point.msg_transfer_delete_session_open',
|
||||
transfer=transfer.rec_name,
|
||||
session=transfer.session.rec_name))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@@ -1320,16 +1333,16 @@ class POSCashTransfer(Workflow, ModelSQL, ModelView):
|
||||
return move
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Point = pool.get('sale.point')
|
||||
Session = pool.get('sale.point.cash.session')
|
||||
vlist = [v.copy() for v in vlist]
|
||||
for values in vlist:
|
||||
if values.get('point') and not values.get('session'):
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
if values.get('point') is not None and not values.get('session'):
|
||||
point = Point(values['point'])
|
||||
values['session'] = Session.get_current(point)
|
||||
return super().create(vlist)
|
||||
return values
|
||||
|
||||
|
||||
class POSCashTransferType(ModelSQL, ModelView):
|
||||
|
||||
@@ -45,16 +45,12 @@ class Promotion(metaclass=PoolMeta):
|
||||
return super().match(pattern)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
def on_modification(cls, mode, promotions, field_names=None):
|
||||
pool = Pool()
|
||||
Number = pool.get('sale.promotion.coupon.number')
|
||||
super().write(*args)
|
||||
actions = iter(args)
|
||||
numbers = []
|
||||
for promotions, values in zip(actions, actions):
|
||||
if {'start_date', 'end_date'} & values.keys():
|
||||
numbers.extend(cls._update_coupon_number_dates(promotions))
|
||||
Number.save(numbers)
|
||||
super().on_modification(mode, promotions, field_names=field_names)
|
||||
if mode == 'write' and field_names & {'start_date', 'end_date'}:
|
||||
Number.save(list(cls._update_coupon_number_dates(promotions)))
|
||||
|
||||
@classmethod
|
||||
def _update_coupon_number_dates(cls, promotions):
|
||||
|
||||
@@ -871,25 +871,17 @@ class LineConsumption(ModelSQL, ModelView):
|
||||
return super().copy(consumptions, default=default)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
for consumptions in args[::2]:
|
||||
def check_modification(
|
||||
cls, mode, consumptions, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, consumptions, values=values, external=external)
|
||||
if mode in {'write', 'delete'}:
|
||||
for consumption in consumptions:
|
||||
if consumption.invoice_line:
|
||||
raise AccessError(
|
||||
gettext('sale_subscription'
|
||||
'.msg_consumption_modify_invoiced',
|
||||
raise AccessError(gettext(
|
||||
'sale_subscription.'
|
||||
'msg_consumption_modify_invoiced',
|
||||
consumption=consumption.rec_name))
|
||||
super().write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, consumptions):
|
||||
for consumption in consumptions:
|
||||
if consumption.invoice_line:
|
||||
raise AccessError(
|
||||
gettext('sale_subscription'
|
||||
'.msg_consumption_modify_invoiced',
|
||||
consumption=consumption.rec_name))
|
||||
super().delete(consumptions)
|
||||
|
||||
@classmethod
|
||||
def get_invoice_lines(cls, consumptions, invoice):
|
||||
|
||||
@@ -38,7 +38,7 @@ class Request(metaclass=PoolMeta):
|
||||
return super().copy(requests, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, requests):
|
||||
def on_delete(cls, requests):
|
||||
pool = Pool()
|
||||
Sale = pool.get('sale.sale')
|
||||
SaleLine = pool.get('sale.line')
|
||||
@@ -54,9 +54,8 @@ class Request(metaclass=PoolMeta):
|
||||
'purchase_request': None,
|
||||
})
|
||||
|
||||
super().delete(requests)
|
||||
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return super().on_delete(requests)
|
||||
|
||||
@@ -9,9 +9,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.message" id="msg_reset_move">
|
||||
<field name="text">You cannot reset move "%(move)s" to draft because it was generated by a sale or a purchase.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_drop_shipment_delete_cancel">
|
||||
<field name="text">To delete drop shipment "%(shipment)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_move_split_drop">
|
||||
<field name="text">To split move from drop shipment, you must use the split wizard.</field>
|
||||
</record>
|
||||
|
||||
@@ -243,25 +243,18 @@ class ShipmentDrop(
|
||||
Move.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Config = pool.get('stock.configuration')
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
config = Config(1)
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
values['number'] = config.get_multivalue(
|
||||
'shipment_drop_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
shipments = super().create(vlist)
|
||||
cls._set_move_planned_date(shipments)
|
||||
return shipments
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._set_move_planned_date(sum(args[::2], []))
|
||||
Configuration = pool.get('stock.configuration')
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'shipment_drop_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def copy(cls, shipments, default=None):
|
||||
@@ -274,22 +267,6 @@ class ShipmentDrop(
|
||||
default.setdefault('customer_moves', None)
|
||||
return super().copy(shipments, default=default)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, shipments):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
cls.cancel(shipments)
|
||||
for shipment in shipments:
|
||||
if shipment.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('sale_supply_drop_shipment'
|
||||
'.msg_drop_shipment_delete_cancel',
|
||||
shipment=shipment.rec_name,
|
||||
))
|
||||
Move.delete([m for s in shipments for m in s.supplier_moves])
|
||||
super().delete(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
|
||||
@@ -26,11 +26,12 @@ def process_sale_supply(func):
|
||||
sales.update([s.id for s in Sale.search([
|
||||
('lines.productions', 'in', ids),
|
||||
])])
|
||||
func(cls, productions)
|
||||
result = func(cls, productions)
|
||||
if sales:
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Sale.__queue__.process(sales)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -43,8 +44,8 @@ class Production(metaclass=PoolMeta):
|
||||
|
||||
@classmethod
|
||||
@process_sale_supply
|
||||
def delete(cls, productions):
|
||||
super().delete(productions)
|
||||
def on_delete(cls, productions):
|
||||
return super().on_delete(productions)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
|
||||
+37
-38
@@ -147,17 +147,6 @@ class Inventory(Workflow, ModelSQL, ModelView):
|
||||
('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def delete(cls, inventories):
|
||||
# Cancel before delete
|
||||
cls.cancel(inventories)
|
||||
for inventory in inventories:
|
||||
if inventory.state != 'cancelled':
|
||||
raise AccessError(
|
||||
gettext('stock.msg_inventory_delete_cancel',
|
||||
inventory=inventory.rec_name))
|
||||
super().delete(inventories)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@@ -215,26 +204,36 @@ class Inventory(Workflow, ModelSQL, ModelView):
|
||||
Line.cancel_move([l for i in inventories for l in i.lines])
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('stock.configuration')
|
||||
config = Configuration(1)
|
||||
vlist = [x.copy() for x in vlist]
|
||||
default_company = cls.default_company()
|
||||
for values in vlist:
|
||||
if values.get('number') is None:
|
||||
values['number'] = config.get_multivalue(
|
||||
'inventory_sequence',
|
||||
company=values.get('company', default_company)).get()
|
||||
inventories = super().create(vlist)
|
||||
cls.complete_lines(inventories, fill=False)
|
||||
return inventories
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create' and not values.get('number'):
|
||||
company_id = values.get('company', cls.default_company())
|
||||
if company_id is not None:
|
||||
configuration = Configuration(1)
|
||||
if sequence := configuration.get_multivalue(
|
||||
'inventory_sequence', company=company_id):
|
||||
values['number'] = sequence.get()
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
inventories = cls.browse(set(sum(args[::2], [])))
|
||||
cls.complete_lines(inventories, fill=False)
|
||||
def on_modification(cls, mode, inventories, field_names=None):
|
||||
super().on_modification(mode, inventories, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls.complete_lines(inventories, fill=False)
|
||||
|
||||
@classmethod
|
||||
def check_modification(
|
||||
cls, mode, inventories, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, inventories, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for inventory in inventories:
|
||||
if inventory.state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'stock.msg_inventory_delete_cancel',
|
||||
inventory=inventory.rec_name))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, inventories, default=None):
|
||||
@@ -249,9 +248,7 @@ class Inventory(Workflow, ModelSQL, ModelView):
|
||||
default.setdefault('lines.moves', None)
|
||||
default.setdefault('number', None)
|
||||
|
||||
new_inventories = super().copy(inventories, default=default)
|
||||
cls.complete_lines(new_inventories, fill=False)
|
||||
return new_inventories
|
||||
return super().copy(inventories, default=default)
|
||||
|
||||
@staticmethod
|
||||
def grouping():
|
||||
@@ -514,14 +511,16 @@ class InventoryLine(ModelSQL, ModelView):
|
||||
self.expected_quantity = quantity
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
for line in lines:
|
||||
if line.inventory_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(
|
||||
gettext('stock.msg_inventory_line_delete_cancel',
|
||||
line=line.rec_name,
|
||||
inventory=line.inventory.rec_name))
|
||||
super().delete(lines)
|
||||
def check_modification(cls, mode, lines, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, lines, values=values, external=external)
|
||||
if mode == 'delete':
|
||||
for line in lines:
|
||||
if line.inventory_state not in {'cancelled', 'draft'}:
|
||||
raise AccessError(gettext(
|
||||
'stock.msg_inventory_line_delete_cancel',
|
||||
line=line.rec_name,
|
||||
inventory=line.inventory.rec_name))
|
||||
|
||||
|
||||
class Count(Wizard):
|
||||
|
||||
+14
-35
@@ -545,43 +545,23 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView):
|
||||
cls.save(to_save)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
locations = super().create(vlist)
|
||||
cls._set_warehouse_parent(locations)
|
||||
def on_modification(cls, mode, locations, field_names=None):
|
||||
super().on_modification(mode, locations, field_names=field_names)
|
||||
if mode in {'create', 'write'}:
|
||||
cls._set_warehouse_parent(locations)
|
||||
cls._default_warehouse_cache.clear()
|
||||
return locations
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
locations = sum(args[::2], [])
|
||||
cls._set_warehouse_parent(locations)
|
||||
cls._default_warehouse_cache.clear()
|
||||
|
||||
ids = [l.id for l in locations]
|
||||
warehouses = cls.search([
|
||||
('type', '=', 'warehouse'),
|
||||
['OR',
|
||||
('storage_location', 'in', ids),
|
||||
('input_location', 'in', ids),
|
||||
('output_location', 'in', ids),
|
||||
]])
|
||||
|
||||
fields = ('storage_location', 'input_location', 'output_location')
|
||||
wh2childs = {}
|
||||
for warehouse in warehouses:
|
||||
in_out_sto = (getattr(warehouse, f).id for f in fields)
|
||||
for location in locations:
|
||||
if location.id not in in_out_sto:
|
||||
continue
|
||||
childs = wh2childs.setdefault(warehouse.id, cls.search([
|
||||
('parent', 'child_of', warehouse.id),
|
||||
]))
|
||||
if location not in childs:
|
||||
raise LocationValidationError(
|
||||
gettext('stock.msg_location_child_of_warehouse',
|
||||
location=location.rec_name,
|
||||
warehouse=warehouse.rec_name))
|
||||
def check_modification(cls, mode, locations, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, locations, values=values, external=external)
|
||||
if mode == 'write' and 'parent' in values:
|
||||
warehouses = cls.search([
|
||||
('type', '=', 'warehouse'),
|
||||
])
|
||||
cls.validate_fields(
|
||||
warehouses,
|
||||
{'storage_location', 'input_location', 'output_location'})
|
||||
|
||||
@classmethod
|
||||
def delete(cls, locations):
|
||||
@@ -594,7 +574,6 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView):
|
||||
location.storage_location,
|
||||
]))
|
||||
super().delete(locations + extra_locations)
|
||||
cls._default_warehouse_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def copy(cls, locations, default=None):
|
||||
|
||||
@@ -26,9 +26,6 @@ You must use the "Modify Cost Price" wizard.</field>
|
||||
<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>
|
||||
|
||||
+36
-56
@@ -3,7 +3,7 @@
|
||||
import copy
|
||||
import datetime
|
||||
import operator
|
||||
from collections import OrderedDict, defaultdict
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
from itertools import groupby
|
||||
|
||||
@@ -991,72 +991,52 @@ class Move(Workflow, ModelSQL, ModelView):
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
def preprocess_values(cls, mode, values):
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
context = Transaction().context
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
# Use ordered dict to optimize cache alignment
|
||||
products = OrderedDict()
|
||||
for vals in vlist:
|
||||
if vals.get('product') is not None:
|
||||
products[vals['product']] = None
|
||||
id2product = {p.id: p for p in Product.browse(products.keys())}
|
||||
for vals in vlist:
|
||||
assert vals.get('state', cls.default_state()
|
||||
) in ['draft', 'staging']
|
||||
if vals.get('product') is not None:
|
||||
product = id2product[int(vals['product'])]
|
||||
if context.get('_product_replacement', True):
|
||||
vals['product'] = product.replacement.id
|
||||
moves = super().create(vlist)
|
||||
cls.check_period_closed(moves)
|
||||
return moves
|
||||
values = super().preprocess_values(mode, values)
|
||||
if mode == 'create':
|
||||
assert values.get('state', cls.default_state()) in {
|
||||
'draft', 'staging'}
|
||||
if context.get('_product_replacement', True):
|
||||
product_id = values.get('product')
|
||||
if product_id is not None:
|
||||
product = Product(product_id)
|
||||
values['product'] = product.replacement.id
|
||||
elif mode == 'write':
|
||||
if {'unit_price', 'currency'} & values.keys():
|
||||
values['unit_price_updated'] = True
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
actions = iter(args)
|
||||
for moves, values in zip(actions, actions):
|
||||
vals_set = set(values)
|
||||
if cls._deny_modify_assigned & vals_set:
|
||||
def check_modification(cls, mode, moves, values=None, external=False):
|
||||
super().check_modification(
|
||||
mode, moves, values=values, external=external)
|
||||
if mode == 'create':
|
||||
cls.check_period_closed(moves)
|
||||
elif mode == 'write':
|
||||
if values.keys() - cls._allow_modify_closed_period:
|
||||
cls.check_period_closed(moves)
|
||||
if values.keys() & cls._deny_modify_assigned:
|
||||
for move in moves:
|
||||
if move.state == 'assigned':
|
||||
raise AccessError(
|
||||
gettext('stock.msg_move_modify_assigned',
|
||||
raise AccessError(gettext(
|
||||
'stock.msg_move_modify_assigned',
|
||||
move=move.rec_name))
|
||||
if cls._deny_modify_done_cancel & vals_set:
|
||||
if values.keys() & cls._deny_modify_done_cancel:
|
||||
for move in moves:
|
||||
if move.state in ('done', 'cancelled'):
|
||||
raise AccessError(
|
||||
gettext('stock.msg_move_modify_%s' % move.state,
|
||||
if move.state in {'done', 'cancelled'}:
|
||||
raise AccessError(gettext(
|
||||
'stock.msg_move_modify_%s' % move.state,
|
||||
move=move.rec_name))
|
||||
|
||||
super().write(*args)
|
||||
|
||||
unit_price_update = []
|
||||
actions = iter(args)
|
||||
for moves, values in zip(actions, actions):
|
||||
if any(f not in cls._allow_modify_closed_period for f in values):
|
||||
cls.check_period_closed(moves)
|
||||
elif mode == 'delete':
|
||||
for move in moves:
|
||||
if (move.state == 'done'
|
||||
and ('unit_price' in values
|
||||
or 'currency' in values)):
|
||||
unit_price_update.append(move)
|
||||
|
||||
if unit_price_update:
|
||||
cls.write(unit_price_update, {'unit_price_updated': True})
|
||||
|
||||
@classmethod
|
||||
def delete(cls, moves):
|
||||
for move in moves:
|
||||
if (move.state not in {'staging', 'draft', 'cancelled'}
|
||||
and move.quantity):
|
||||
raise AccessError(
|
||||
gettext('stock.msg_move_delete_draft_cancel',
|
||||
move=move.rec_name))
|
||||
super().delete(moves)
|
||||
if (move.state not in {'staging', 'draft', 'cancelled'}
|
||||
and move.quantity):
|
||||
raise AccessError(gettext(
|
||||
'stock.msg_move_delete_draft_cancel',
|
||||
move=move.rec_name))
|
||||
|
||||
@staticmethod
|
||||
def check_origin_types():
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user