diff --git a/account.py b/account.py index a116413df7..fd58736c02 100644 --- a/account.py +++ b/account.py @@ -952,10 +952,10 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView): table_h.drop_column('kind') @classmethod - def validate(cls, accounts): - super(Account, cls).validate(accounts) - cls.check_second_currency(accounts) - cls.check_move_domain(accounts) + def validate_fields(cls, accounts, field_names): + super().validate_fields(accounts, field_names) + cls.check_second_currency(accounts, field_names) + cls.check_move_domain(accounts, field_names) @staticmethod def default_left(): @@ -1159,9 +1159,14 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView): setattr(self, field, getattr(self.parent, field)) @classmethod - def check_second_currency(cls, accounts): + def check_second_currency(cls, accounts, field_names=None): pool = Pool() Line = pool.get('account.move.line') + if field_names and not ( + field_names & ( + {'second_currency', 'type'} + | set(cls.deferral.validation_depends))): + return for account in accounts: if not account.second_currency: continue @@ -1190,9 +1195,11 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView): account=account.rec_name)) @classmethod - def check_move_domain(cls, accounts): + def check_move_domain(cls, accounts, field_names=None): pool = Pool() Line = pool.get('account.move.line') + if field_names and not (field_names & {'closed', 'type'}): + return accounts = [a for a in accounts if a.closed or not a.type] for sub_accounts in grouped_slice(accounts): sub_accounts = list(sub_accounts) diff --git a/fiscalyear.py b/fiscalyear.py index fa85894549..715db956a1 100644 --- a/fiscalyear.py +++ b/fiscalyear.py @@ -100,45 +100,54 @@ class FiscalYear(Workflow, ModelSQL, ModelView): }.get(self.state) @classmethod - def validate(cls, years): - super(FiscalYear, cls).validate(years) - for year in years: - year.check_dates() - year.check_post_move_sequence() + def validate_fields(cls, fiscalyears, field_names): + super().validate_fields(fiscalyears, field_names) + cls.check_dates(fiscalyears, field_names) + cls.check_post_move_sequence(fiscalyears, field_names) - def check_dates(self): + @classmethod + def check_dates(cls, fiscalyears, field_names=None): + if field_names and not (field_names & { + 'start_date', 'end_date', 'company'}): + return transaction = Transaction() connection = transaction.connection - self.__class__.lock() + cls.lock() cursor = connection.cursor() - table = self.__table__() - cursor.execute(*table.select(table.id, - where=(((table.start_date <= self.start_date) - & (table.end_date >= self.start_date)) - | ((table.start_date <= self.end_date) - & (table.end_date >= self.end_date)) - | ((table.start_date >= self.start_date) - & (table.end_date <= self.end_date))) - & (table.company == self.company.id) - & (table.id != self.id))) - second_id = cursor.fetchone() - if second_id: - second = self.__class__(second_id[0]) - raise FiscalYearDatesError( - gettext('account.msg_fiscalyear_overlap', - first=self.rec_name, - second=second.rec_name)) + table = cls.__table__() + for year in fiscalyears: + cursor.execute(*table.select(table.id, + where=(((table.start_date <= year.start_date) + & (table.end_date >= year.start_date)) + | ((table.start_date <= year.end_date) + & (table.end_date >= year.end_date)) + | ((table.start_date >= year.start_date) + & (table.end_date <= year.end_date))) + & (table.company == year.company.id) + & (table.id != year.id))) + second_id = cursor.fetchone() + if second_id: + second = cls(second_id[0]) + raise FiscalYearDatesError( + gettext('account.msg_fiscalyear_overlap', + first=year.rec_name, + second=second.rec_name)) - def check_post_move_sequence(self): - years = self.search([ - ('post_move_sequence', '=', self.post_move_sequence.id), - ('id', '!=', self.id), - ]) - if years: - raise FiscalYearSequenceError( - gettext('account.msg_fiscalyear_different_post_move_sequence', - first=self.rec_name, - second=years[0].rec_name)) + @classmethod + def check_post_move_sequence(cls, fiscalyears, field_names=None): + if field_names and 'post_move_sequence' not in field_names: + return + for fiscalyear in fiscalyears: + sequence = fiscalyear.post_move_sequence + years = cls.search([ + ('post_move_sequence', '=', sequence.id), + ('id', '!=', fiscalyear.id), + ], limit=1) + if years: + raise FiscalYearSequenceError(gettext( + 'account.msg_fiscalyear_different_post_move_sequence', + first=fiscalyear.rec_name, + second=years[0].rec_name)) @classmethod def write(cls, *args): diff --git a/move.py b/move.py index 554e33b6a7..cd26e2dda4 100644 --- a/move.py +++ b/move.py @@ -219,17 +219,20 @@ class Move(ModelSQL, ModelView): return [(None, '')] + [(m, get_name(m)) for m in models] @classmethod - def validate(cls, moves): - super(Move, cls).validate(moves) - for move in moves: - move.check_date() + def validate_fields(cls, moves, field_names): + super().validate_fields(moves, field_names) + cls.check_date(moves, field_names) - def check_date(self): - if (self.date < self.period.start_date - or self.date > self.period.end_date): - raise MoveDatesError( - gettext('account.msg_move_date_outside_period', - move=self.rec_name)) + @classmethod + def check_date(cls, moves, field_names=None): + if field_names and not (field_names & {'date', 'period'}): + return + for move in moves: + if (move.date < move.period.start_date + or move.date > move.period.end_date): + raise MoveDatesError( + gettext('account.msg_move_date_outside_period', + move=move.rec_name)) @classmethod def check_modify(cls, moves): @@ -1147,22 +1150,25 @@ class Line(MoveLineMixin, ModelSQL, ModelView): return list(set(l.id for line in lines for l in line.move.lines)) @classmethod - def validate(cls, lines): + def validate_fields(cls, lines, field_names): super(Line, cls).validate(lines) - for line in lines: - line.check_account() + cls.check_account(lines, field_names) - def check_account(self): - if not self.account.type or self.account.closed: - raise AccessError( - gettext('account.msg_line_closed_account', - account=self.account.rec_name)) - if bool(self.party) != bool(self.account.party_required): - error = 'party_set' if self.party else 'party_required' - raise AccessError( - gettext('account.msg_line_%s' % error, - account=self.account.rec_name, - line=self.rec_name)) + @classmethod + def check_account(cls, lines, field_names=None): + if field_names and not (field_names & {'account', 'party'}): + return + for line in lines: + if not line.account.type or line.account.closed: + raise AccessError( + gettext('account.msg_line_closed_account', + account=line.account.rec_name)) + if bool(line.party) != bool(line.account.party_required): + error = 'party_set' if line.party else 'party_required' + raise AccessError( + gettext('account.msg_line_%s' % error, + account=line.account.rec_name, + line=line.rec_name)) @classmethod def check_journal_period_modify(cls, period, journal): diff --git a/period.py b/period.py index 6922ae22b3..f65c6be67c 100644 --- a/period.py +++ b/period.py @@ -103,59 +103,76 @@ class Period(Workflow, ModelSQL, ModelView): }.get(self.state) @classmethod - def validate(cls, periods): - super(Period, cls).validate(periods) - for period in periods: - period.check_dates() - period.check_fiscalyear_dates() - period.check_post_move_sequence() + def validate_fields(cls, periods, field_names): + super().validate_fields(periods, field_names) + cls.check_dates(periods, field_names) + cls.check_fiscalyear_dates(periods, field_names) + cls.check_post_move_sequence(periods, field_names) - def check_dates(self): - if self.type != 'standard': - return True + @classmethod + def check_dates(cls, periods, field_names=None): + if field_names and not ( + field_names & { + 'start_date', 'end_date', 'fiscalyear', 'type'}): + return transaction = Transaction() connection = transaction.connection - self.__class__.lock() - table = self.__table__() + cls.lock() + table = cls.__table__() cursor = connection.cursor() - cursor.execute(*table.select(table.id, - where=(((table.start_date <= self.start_date) - & (table.end_date >= self.start_date)) - | ((table.start_date <= self.end_date) - & (table.end_date >= self.end_date)) - | ((table.start_date >= self.start_date) - & (table.end_date <= self.end_date))) - & (table.fiscalyear == self.fiscalyear.id) - & (table.type == 'standard') - & (table.id != self.id))) - period_id = cursor.fetchone() - if period_id: - overlapping_period = self.__class__(period_id[0]) - raise PeriodDatesError( - gettext('account.msg_period_overlap', - first=self.rec_name, - second=overlapping_period.rec_name)) + for period in periods: + if period.type != 'standard': + continue + cursor.execute(*table.select(table.id, + where=(((table.start_date <= period.start_date) + & (table.end_date >= period.start_date)) + | ((table.start_date <= period.end_date) + & (table.end_date >= period.end_date)) + | ((table.start_date >= period.start_date) + & (table.end_date <= period.end_date))) + & (table.fiscalyear == period.fiscalyear.id) + & (table.type == 'standard') + & (table.id != period.id))) + period_id = cursor.fetchone() + if period_id: + overlapping_period = cls(period_id[0]) + raise PeriodDatesError( + gettext('account.msg_period_overlap', + first=period.rec_name, + second=overlapping_period.rec_name)) - def check_fiscalyear_dates(self): - if (self.start_date < self.fiscalyear.start_date - or self.end_date > self.fiscalyear.end_date): - raise PeriodDatesError( - gettext('account.msg_period_fiscalyear_dates', - period=self.rec_name, - fiscalyear=self.fiscalyear.rec_name)) - - def check_post_move_sequence(self): - if not self.post_move_sequence: + @classmethod + def check_fiscalyear_dates(cls, periods, field_names=None): + if field_names and not ( + field_names & { + 'start_date', 'end_date', 'fiscalyear'}): return - periods = self.search([ - ('post_move_sequence', '=', self.post_move_sequence.id), - ('fiscalyear', '!=', self.fiscalyear.id), - ]) - if periods: - raise PeriodSequenceError( - gettext('account.msg_period_same_sequence', - first=self.rec_name, - second=periods[0].rec_name)) + for period in periods: + fiscalyear = period.fiscalyear + if (period.start_date < fiscalyear.start_date + or period.end_date > fiscalyear.end_date): + raise PeriodDatesError( + gettext('account.msg_period_fiscalyear_dates', + period=period.rec_name, + fiscalyear=fiscalyear.rec_name)) + + @classmethod + def check_post_move_sequence(cls, periods, field_names=None): + if field_names and not ( + field_names & {'post_move_sequence', 'fiscalyear'}): + return + for period in periods: + if not period.post_move_sequence: + continue + periods = cls.search([ + ('post_move_sequence', '=', period.post_move_sequence.id), + ('fiscalyear', '!=', period.fiscalyear.id), + ]) + if periods: + raise PeriodSequenceError( + gettext('account.msg_period_same_sequence', + first=period.rec_name, + second=periods[0].rec_name)) @classmethod def find(cls, company_id, date=None, exception=True, test_state=True): diff --git a/tax.py b/tax.py index 89aa243c4e..c57ed9c77f 100644 --- a/tax.py +++ b/tax.py @@ -578,16 +578,19 @@ class TaxTemplate(sequence_ordered(), ModelSQL, ModelView, DeactivableMixin): cls._order.insert(0, ('account', 'ASC')) @classmethod - def validate(cls, tax_templates): - super(TaxTemplate, cls).validate(tax_templates) - for tax_template in tax_templates: - tax_template.check_update_unit_price() + def validate_fields(cls, tax_templates, field_names): + super().validate_fields(tax_templates, field_names) + cls.check_update_unit_price(tax_templates, field_names) - def check_update_unit_price(self): - if self.update_unit_price and self.parent: - raise AccessError( - gettext('account.msg_tax_update_unit_price_with_parent', - tax=self.rec_name)) + @classmethod + def check_update_unit_price(cls, tax_templates, field_names=None): + if field_names and not (field_names & {'update_unit_price', 'parent'}): + return + for tax in tax_templates: + if tax.update_unit_price and tax.parent: + raise AccessError(gettext( + 'account.msg_tax_update_unit_price_with_parent', + tax=tax.rec_name)) @staticmethod def default_type(): @@ -778,16 +781,19 @@ class Tax(sequence_ordered(), ModelSQL, ModelView, DeactivableMixin): del _states @classmethod - def validate(cls, taxes): - super(Tax, cls).validate(taxes) - for tax in taxes: - tax.check_update_unit_price() + def validate_fields(cls, taxes, field_names): + super().validate_fields(taxes, field_names) + cls.check_update_unit_price(taxes, field_names) - def check_update_unit_price(self): - if self.parent and self.update_unit_price: - raise AccessError( - gettext('account.msg_tax_update_unit_price_with_parent', - tax=self.rec_name)) + @classmethod + def check_update_unit_price(cls, taxes, field_names=None): + if field_names and not (field_names & {'parent', 'update_unit_price'}): + return + for tax in taxes: + if tax.parent and tax.update_unit_price: + raise AccessError(gettext( + 'account.msg_tax_update_unit_price_with_parent', + tax=tax.rec_name)) @staticmethod def default_type():