Use ModelStorage hooks instead of extending create/write/delete

This commit is contained in:
Cédric Krier
2025-04-06 00:14:17 +02:00
parent 45122b77c0
commit 5aeff66acd
142 changed files with 2104 additions and 3189 deletions
+2 -24
View File
@@ -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
View File
@@ -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'))
+11 -21
View File
@@ -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
View File
@@ -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
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))
+9 -7
View File
@@ -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):
+1 -1
View File
@@ -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):
+22 -21
View File
@@ -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):
+11 -9
View File
@@ -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))
+16 -11
View File
@@ -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 {
+6 -15
View File
@@ -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):
+54 -104
View File
@@ -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
+10 -8
View File
@@ -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):
+8 -8
View File
@@ -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):
+56 -52
View File
@@ -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
+17 -12
View File
@@ -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):
+16 -13
View File
@@ -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(
+2 -11
View File
@@ -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))
+2 -1
View File
@@ -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
+21 -35
View File
@@ -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'
+16 -13
View File
@@ -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(
+39 -30
View File
@@ -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):
+31 -29
View File
@@ -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):
+11 -8
View File
@@ -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))
+4 -26
View File
@@ -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()
+11 -15
View File
@@ -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):
+21 -19
View File
@@ -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):
+21 -19
View File
@@ -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):
+4 -10
View File
@@ -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):
+17 -16
View File
@@ -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):
+15 -21
View File
@@ -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):
+5 -3
View File
@@ -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):
+9 -9
View File
@@ -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()
+9 -27
View File
@@ -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()
+14 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+14 -11
View File
@@ -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
View File
@@ -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):
+6 -7
View File
@@ -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()
+2 -13
View File
@@ -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'))
+2 -17
View File
@@ -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()
+10 -17
View File
@@ -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):
+5 -17
View File
@@ -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):
+6 -20
View File
@@ -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
View File
@@ -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()
+2 -13
View File
@@ -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
+9 -14
View File
@@ -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
View File
@@ -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):
+20 -25
View File
@@ -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):
+5 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+9 -7
View File
@@ -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):
+9 -17
View File
@@ -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):
+2 -12
View File
@@ -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
View File
@@ -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):
+12 -9
View File
@@ -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):
+5 -12
View File
@@ -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):
+15 -18
View File
@@ -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
+8 -17
View File
@@ -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
View File
@@ -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):
+1 -17
View File
@@ -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)
+10 -13
View File
@@ -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):
+11 -14
View File
@@ -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))
+14 -28
View File
@@ -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
+43 -65
View File
@@ -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):
+6 -5
View File
@@ -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)
+6 -6
View File
@@ -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):
+25 -29
View File
@@ -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):
+5 -4
View File
@@ -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 -20
View File
@@ -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
+12 -14
View File
@@ -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:
+22 -21
View File
@@ -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
View File
@@ -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):
+6 -5
View File
@@ -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
View File
@@ -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):
+6 -4
View File
@@ -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)
+29 -26
View File
@@ -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:
+22 -22
View File
@@ -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 -20
View File
@@ -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
+12 -10
View File
@@ -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
View File
@@ -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):
+4 -8
View File
@@ -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):
+8 -16
View File
@@ -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):
+2 -3
View File
@@ -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>
+11 -34
View File
@@ -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')
+4 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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):
-3
View File
@@ -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
View File
@@ -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