From 7f9c38ee89df293b53ebea2f26114e7123e4103d Mon Sep 17 00:00:00 2001 From: Albert Cervera i Areny Date: Thu, 7 Mar 2013 16:53:42 +0100 Subject: [PATCH] Replace _constraints with validate in ModelStorage and improve user messages. issue2616 review371002 --- account.py | 67 ++++++++-------- account.xml | 36 +++++++++ fiscalyear.py | 81 +++++++++++-------- journal.py | 30 ++++--- move.py | 214 ++++++++++++++++++++++++++++++++------------------ period.py | 125 ++++++++++++++++++----------- tax.py | 24 +++--- tax.xml | 18 +++++ 8 files changed, 384 insertions(+), 211 deletions(-) diff --git a/account.py b/account.py index 6adee533ef..db6b6641e7 100644 --- a/account.py +++ b/account.py @@ -47,12 +47,6 @@ class TypeTemplate(ModelSQL, ModelView): @classmethod def __setup__(cls): super(TypeTemplate, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_types'), - ] - cls._error_messages.update({ - 'recursive_types': 'You can not create recursive types!', - }) cls._order.insert(0, ('sequence', 'ASC')) @classmethod @@ -65,6 +59,11 @@ class TypeTemplate(ModelSQL, ModelView): # Migration from 2.4: drop required on sequence table.not_null_action('sequence', action='remove') + @classmethod + def validate(cls, records): + super(TypeTemplate, cls).validate(records) + cls.check_recursion(records, rec_name='name') + @staticmethod def default_balance_sheet(): return False @@ -187,12 +186,6 @@ class Type(ModelSQL, ModelView): @classmethod def __setup__(cls): super(Type, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_types'), - ] - cls._error_messages.update({ - 'recursive_types': 'You can not create recursive types!', - }) cls._order.insert(0, ('sequence', 'ASC')) @classmethod @@ -205,6 +198,11 @@ class Type(ModelSQL, ModelView): # Migration from 2.4: drop required on sequence table.not_null_action('sequence', action='remove') + @classmethod + def validate(cls, types): + super(Type, cls).validate(types) + cls.check_recursion(types, rec_name='name') + @staticmethod def default_balance_sheet(): return False @@ -364,15 +362,14 @@ class AccountTemplate(ModelSQL, ModelView): @classmethod def __setup__(cls): super(AccountTemplate, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_accounts'), - ] - cls._error_messages.update({ - 'recursive_accounts': 'You can not create recursive accounts!', - }) cls._order.insert(0, ('code', 'ASC')) cls._order.insert(1, ('name', 'ASC')) + @classmethod + def validate(cls, templates): + super(AccountTemplate, cls).validate(templates) + cls.check_recursion(templates) + @staticmethod def default_kind(): return 'view' @@ -576,21 +573,22 @@ class Account(ModelSQL, ModelView): @classmethod def __setup__(cls): super(Account, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_accounts'), - ] cls._error_messages.update({ - 'recursive_accounts': 'You can not create recursive accounts!', - 'delete_account_containing_move_lines': ('You can not delete ' - 'accounts containing move lines!'), - }) + 'delete_account_containing_move_lines': ('You can not delete ' + 'account "%s" because it has move lines.'), + }) cls._sql_error_messages.update({ - 'parent_fkey': ('You can not delete accounts ' - 'that have children!'), - }) + 'parent_fkey': ('You can not delete accounts that have ' + 'children.'), + }) cls._order.insert(0, ('code', 'ASC')) cls._order.insert(1, ('name', 'ASC')) + @classmethod + def validate(cls, accounts): + super(Account, cls).validate(accounts) + cls.check_recursion(accounts) + @staticmethod def default_left(): return 0 @@ -870,10 +868,12 @@ class Account(ModelSQL, ModelView): childs = cls.search([ ('parent', 'child_of', [a.id for a in accounts]), ]) - if MoveLine.search([ - ('account', 'in', [a.id for a in childs]), - ]): - cls.raise_user_error('delete_account_containing_move_lines') + lines = MoveLine.search([ + ('account', 'in', [a.id for a in childs]), + ]) + if lines: + cls.raise_user_error('delete_account_containing_move_lines', ( + lines[0].account.rec_name,)) super(Account, cls).delete(accounts) def update_account(self, template2account=None, template2type=None): @@ -2061,7 +2061,8 @@ class OpenAgedBalance(Wizard): super(OpenAgedBalance, cls).__setup__() cls._error_messages.update({ 'warning': 'Warning', - 'term_overlap_desc': 'You cannot define overlapping terms'}) + 'term_overlap_desc': 'You cannot define overlapping terms', + }) def do_print_(self, action): if not (self.start.term1 < self.start.term2 < self.start.term3): diff --git a/account.xml b/account.xml index 28f550082b..f549d98cdb 100644 --- a/account.xml +++ b/account.xml @@ -703,5 +703,41 @@ this repository contains the full copyright notices and license terms. --> id="menu_aged_balance" icon="tryton-print" name="Aged Balance"/> + + account.account.type.template + en_US + error + recursion_error + Recursion error: Account Type Template "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + + + + account.account.type + en_US + error + recursion_error + Recursion error: Account Type "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + + + + account.account.template + en_US + error + recursion_error + Recursion error: Account Template "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + + + + account.account + en_US + error + recursion_error + Recursion error: Account "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + + diff --git a/fiscalyear.py b/fiscalyear.py index 793ece34eb..1fb72e2220 100644 --- a/fiscalyear.py +++ b/fiscalyear.py @@ -51,26 +51,22 @@ class FiscalYear(ModelSQL, ModelView): @classmethod def __setup__(cls): super(FiscalYear, cls).__setup__() - cls._constraints += [ - ('check_dates', 'fiscalyear_overlaps'), - ('check_post_move_sequence', 'different_post_move_sequence'), - ] cls._order.insert(0, ('start_date', 'ASC')) cls._error_messages.update({ - 'change_post_move_sequence': ('You can not change ' - 'the post move sequence'), - 'no_fiscalyear_date': 'No fiscal year defined for this date!', - 'fiscalyear_overlaps': - 'You can not have 2 fiscal years that overlaps!', - 'different_post_move_sequence': - 'You must have different post move sequence per fiscal year!', - 'account_balance_not_zero': - 'The balance of the account "%s" must be zero!', - 'close_error': ('You can not close a fiscal year until ' - 'there is older fiscal year opened!'), - 'reopen_error': ('You can not reopen a fiscal year until ' - 'there is more recent fiscal year closed!'), - }) + 'change_post_move_sequence': ('You can not change the post ' + 'move sequence in fiscal year "%s".'), + 'no_fiscalyear_date': 'No fiscal year defined for "%s".', + 'fiscalyear_overlaps': ('Fiscal year "%(first)s" and ' + '"%(second)s" overlap.'), + 'different_post_move_sequence': ('Fiscal year "%(first)s" and ' + '"%(second)s" have the same post move sequence.'), + 'account_balance_not_zero': ('The balance of the account "%s" ' + 'must be zero.'), + 'close_error': ('You can not close fiscal year "%s" until you ' + 'close all previous fiscal years.'), + 'reopen_error': ('You can not reopen fiscal year "%s" until ' + 'you reopen all later fiscal years.'), + }) cls._buttons.update({ 'create_period': { 'invisible': ((Eval('state') != 'open') @@ -96,6 +92,13 @@ class FiscalYear(ModelSQL, ModelView): def default_company(): return Transaction().context.get('company') + @classmethod + def validate(cls, years): + super(FiscalYear, cls).validate(years) + for year in years: + year.check_dates() + year.check_post_move_sequence() + def check_dates(self): cursor = Transaction().cursor cursor.execute('SELECT id ' @@ -109,17 +112,24 @@ class FiscalYear(ModelSQL, ModelView): self.end_date, self.end_date, self.start_date, self.end_date, self.company.id, self.id)) - if cursor.fetchone(): - return False - return True + second_id = cursor.fetchone() + if second_id: + second = self.__class__(second_id[0]) + self.raise_user_error('fiscalyear_overlaps', { + 'first': self.rec_name, + 'second': second.rec_name, + }) def check_post_move_sequence(self): - if self.search([ - ('post_move_sequence', '=', self.post_move_sequence.id), - ('id', '!=', self.id), - ]): - return False - return True + years = self.search([ + ('post_move_sequence', '=', self.post_move_sequence.id), + ('id', '!=', self.id), + ]) + if years: + self.raise_user_error('different_post_move_sequence', { + 'first': self.rec_name, + 'second': years[0].rec_name, + }) @classmethod def write(cls, fiscalyears, vals): @@ -128,7 +138,8 @@ class FiscalYear(ModelSQL, ModelView): if fiscalyear.post_move_sequence and \ fiscalyear.post_move_sequence.id != \ vals['post_move_sequence']: - cls.raise_user_error('change_post_move_sequence') + cls.raise_user_error('change_post_move_sequence', ( + fiscalyear.rec_name,)) vals = vals.copy() if 'periods' in vals: operator = ['delete', 'unlink_all', 'unlink', 'create', 'write', @@ -189,7 +200,9 @@ class FiscalYear(ModelSQL, ModelView): If exception is set the function will raise an exception if any fiscal year is found. ''' - Date = Pool().get('ir.date') + pool = Pool() + Lang = pool.get('ir.lang') + Date = pool.get('ir.date') if not date: date = Date.today() @@ -200,7 +213,13 @@ class FiscalYear(ModelSQL, ModelView): ], order=[('start_date', 'DESC')], limit=1) if not fiscalyears: if exception: - cls.raise_user_error('no_fiscalyear_date') + language = Transaction().language + languages = Lang.search([('code', '=', language)]) + if not languages: + languages = Lang.search([('code', '=', 'en_US')]) + language, = languages + formatted = Lang.strftime(date, language.code, language.date) + cls.raise_user_error('no_fiscalyear_date', (formatted,)) else: return None return fiscalyears[0].id @@ -242,7 +261,7 @@ class FiscalYear(ModelSQL, ModelView): ('state', '=', 'open'), ('company', '=', fiscalyear.company.id), ]): - cls.raise_user_error('close_error') + cls.raise_user_error('close_error', (fiscalyear.rec_name,)) #First close the fiscalyear to be sure #it will not have new period created between. diff --git a/journal.py b/journal.py index 794a081d8e..b94fe0b38f 100644 --- a/journal.py +++ b/journal.py @@ -31,7 +31,7 @@ class JournalType(ModelSQL, ModelView): def __setup__(cls): super(JournalType, cls).__setup__() cls._sql_constraints += [ - ('code_uniq', 'UNIQUE(code)', 'The code must be unique!'), + ('code_uniq', 'UNIQUE(code)', 'The code must be unique.'), ] cls._order.insert(0, ('code', 'ASC')) @@ -199,17 +199,18 @@ class JournalPeriod(ModelSQL, ModelView): super(JournalPeriod, cls).__setup__() cls._sql_constraints += [ ('journal_period_uniq', 'UNIQUE(journal, period)', - 'You can only open one journal per period!'), + 'You can only open one journal per period.'), ] cls._order.insert(0, ('name', 'ASC')) cls._error_messages.update({ - 'modify_del_journal_period': ('You can not modify/delete ' - 'a journal - period with moves!'), - 'create_journal_period': ('You can not create ' - 'a journal - period on a closed period!'), - 'open_journal_period': ('You can not open ' - 'a journal - period from a closed period!'), - }) + 'modify_del_journal_period': ('You can not modify/delete ' + 'journal - period "%s" because it has moves.'), + 'create_journal_period': ('You can not create a ' + 'journal - period on closed period "%s".'), + 'open_journal_period': ('You can not open ' + 'journal - period "%(journal_period)s" because period ' + '"%(period)s" is closed.'), + }) @staticmethod def default_active(): @@ -231,7 +232,8 @@ class JournalPeriod(ModelSQL, ModelView): ('period', '=', period.period.id), ], limit=1) if moves: - cls.raise_user_error('modify_del_journal_period') + cls.raise_user_error('modify_del_journal_period', ( + period.rec_name,)) @classmethod def create(cls, vlist): @@ -240,7 +242,8 @@ class JournalPeriod(ModelSQL, ModelView): if vals.get('period'): period = Period(vals['period']) if period.state == 'close': - cls.raise_user_error('create_journal_period') + cls.raise_user_error('create_journal_period', ( + period.rec_name,)) return super(JournalPeriod, cls).create(vlist) @classmethod @@ -251,7 +254,10 @@ class JournalPeriod(ModelSQL, ModelView): if vals.get('state') == 'open': for journal_period in periods: if journal_period.period.state == 'close': - cls.raise_user_error('open_journal_period') + cls.raise_user_error('open_journal_period', { + 'journal_period': journal_period.rec_name, + 'period': journal_period.period.rec_name, + }) super(JournalPeriod, cls).write(periods, vals) @classmethod diff --git a/move.py b/move.py index de818987f7..df91350356 100644 --- a/move.py +++ b/move.py @@ -68,31 +68,28 @@ class Move(ModelSQL, ModelView): def __setup__(cls): super(Move, cls).__setup__() cls._check_modify_exclude = ['state'] - cls._constraints += [ - ('check_centralisation', 'period_centralized_journal'), - ('check_company', 'company_in_move'), - ('check_date', 'date_outside_period'), - ] cls._order.insert(0, ('date', 'DESC')) cls._order.insert(1, ('number', 'DESC')) cls._error_messages.update({ - 'del_posted_move': 'You can not delete posted moves!', - 'post_empty_move': 'You can not post an empty move!', - 'post_unbalanced_move': 'You can not post an unbalanced move!', - 'draft_posted_move_journal': ('You can not set a posted move ' - 'to draft in this journal!'), - 'modify_posted_move': 'Move "%s" is already posted!\n' - 'You can not modify a posted move.', - 'period_centralized_journal': ('You can not create more than ' - 'one move per period\n' - 'in a centralized journal!'), - 'company_in_move': ('You can not create lines on accounts\n' - 'of different companies in the same move!'), - 'date_outside_period': ('You can not create move ' - 'with a date outside the period!'), - 'draft_closed_period': ('You can not set to draft a move ' - 'from a closed period'), - }) + 'post_empty_move': ('You can not post move "%s" because it is ' + 'empty.'), + 'post_unbalanced_move': ('You can not post move "%s" because ' + 'it is an unbalanced.'), + 'draft_posted_move_journal': ('You can not set posted move ' + '"%(move)s" to draft in journal "%(journal)s".'), + 'modify_posted_move': ('You can not modify move "%s" because ' + 'it is already posted.'), + 'period_centralized_journal': ('Move "%(move)s" cannot be ' + 'created because there is already a move in journal ' + '"%(journal)s" and you cannot create more than one move ' + 'per period in a centralized journal.'), + 'company_in_move': ('You can not create lines on accounts' + 'of different companies in move "%s".'), + 'date_outside_period': ('You can not create move "%(move)s" ' + 'because it\'s date is outside its period.'), + 'draft_closed_period': ('You can not set to draft move ' + '"%(move)s" because period "%(period)s" is closed.'), + }) cls._buttons.update({ 'post': { 'invisible': Eval('state') == 'posted', @@ -171,6 +168,14 @@ class Move(ModelSQL, ModelView): ]) return [('', '')] + [(m.model, m.name) for m in models] + @classmethod + def validate(cls, moves): + super(Move, cls).validate(moves) + for move in moves: + move.check_centralisation() + move.check_company() + move.check_date() + def check_centralisation(self): if self.journal.centralised: moves = self.search([ @@ -179,8 +184,10 @@ class Move(ModelSQL, ModelView): ('state', '!=', 'posted'), ], limit=2) if len(moves) > 1: - return False - return True + self.raise_user_error('period_centralized_journal', { + 'move': self.rec_name, + 'journal': self.journal.rec_name, + }) def check_company(self): company_id = -1 @@ -188,19 +195,19 @@ class Move(ModelSQL, ModelView): if company_id < 0: company_id = line.account.company.id if line.account.company.id != company_id: - return False - return True + self.raise_user_error('company_in_move', (self.rec_name,)) def check_date(self): - return self.period.start_date <= self.date <= self.period.end_date + if (self.date < self.period.start_date + or self.date > self.period.end_date): + self.raise_user_error('date_outside_period', (self.rec_name,)) @classmethod def check_modify(cls, moves): 'Check posted moves for modifications.' for move in moves: if move.state == 'posted': - cls.raise_user_error('modify_posted_move', - error_args=(move.name,)) + cls.raise_user_error('modify_posted_move', (move.name,)) @classmethod def search_rec_name(cls, name, clause): @@ -219,7 +226,7 @@ class Move(ModelSQL, ModelView): if len(keys): cls.check_modify(moves) super(Move, cls).write(moves, vals) - cls.validate(moves) + cls.validate_move(moves) @classmethod def create(cls, vlist): @@ -247,7 +254,7 @@ class Move(ModelSQL, ModelView): cls.write([move], { 'centralised_line': line.id, }) - cls.validate(moves) + cls.validate_move(moves) return moves @classmethod @@ -280,7 +287,7 @@ class Move(ModelSQL, ModelView): return new_moves @classmethod - def validate(cls, moves): + def validate_move(cls, moves): ''' Validate balanced move and centralise it if in centralised journal ''' @@ -388,14 +395,14 @@ class Move(ModelSQL, ModelView): for move in moves: amount = Decimal('0.0') if not move.lines: - cls.raise_user_error('post_empty_move') + cls.raise_user_error('post_empty_move', (move.rec_name,)) company = None for line in move.lines: amount += line.debit - line.credit if not company: company = line.account.company if not company.currency.is_zero(amount): - cls.raise_user_error('post_unbalanced_move') + cls.raise_user_error('post_unbalanced_move', (move.rec_name,)) for move in moves: values = { 'state': 'posted', @@ -411,10 +418,16 @@ class Move(ModelSQL, ModelView): def draft(cls, moves): for move in moves: if not move.journal.update_posted: - cls.raise_user_error('draft_posted_move_journal') + cls.raise_user_error('draft_posted_move_journal', { + 'move': move.rec_name, + 'journal': move.journal.rec_name, + }) if move.period.state == 'close': - cls.raise_user_error('draft_closed_period') - return cls.write(moves, { + cls.raise_user_error('draft_closed_period', { + 'move': move.rec_name, + 'journal': move.period.rec_name, + }) + cls.write(moves, { 'state': 'draft', }) @@ -429,16 +442,23 @@ class Reconciliation(ModelSQL, ModelView): @classmethod def __setup__(cls): super(Reconciliation, cls).__setup__() - cls._constraints += [ - ('check_lines', 'invalid_reconciliation'), - ] cls._error_messages.update({ - 'modify': 'You can not modify a reconciliation!', - 'invalid_reconciliation': ('You can not create reconciliation ' - 'where lines are not balanced, nor valid, ' - 'nor in the same account, nor in account to reconcile, ' - 'nor from the same party!'), - }) + 'modify': 'You can not modify a reconciliation.', + 'reconciliation_line_not_valid': ('You can not reconcile line ' + '"%s" because it is not in valid state.'), + 'reconciliation_different_accounts': ('You can not reconcile ' + 'line "%(line)s" because it\'s account "%(account1)s" is ' + 'different from "%(account2)s".'), + 'reconciliation_account_no_reconcile': ('You can not reconcile ' + 'line "%(line)s" because it\'s account "%(account)s" is ' + 'configured as not reconcilable.'), + 'reconciliation_different_parties': ('You can not reconcile ' + 'line "%(line)s" because it\'s party "%(party1)s" is ' + 'different from %(party2)s".'), + 'reconciliation_unbalanced': ('You can not create a ' + 'reconciliation where debit "%(debit)s" and credit ' + '"%(credit)s" differ.'), + }) @classmethod def create(cls, vlist): @@ -455,29 +475,59 @@ class Reconciliation(ModelSQL, ModelView): def write(cls, moves, vals): cls.raise_user_error('modify') + @classmethod + def validate(cls, reconciliations): + super(Reconciliation, cls).validate(reconciliations) + cls.check_lines(reconciliations) + @classmethod def check_lines(cls, reconciliations): for reconciliation in reconciliations: amount = Decimal('0.0') + debit = Decimal('0.0') + credit = Decimal('0.0') account = None party = None for line in reconciliation.lines: if line.state != 'valid': - return False - amount += line.debit - line.credit + cls.raise_user_error('reconciliation_line_not_valid', + (line.rec_name,)) + debit += line.debit + credit += line.credit if not account: account = line.account elif account.id != line.account.id: - return False + cls.raise_user_error('reconciliation_different_accounts', { + 'line': line.rec_name, + 'account1': line.account.rec_name, + 'account2': account.rec_name, + }) if not account.reconcile: - return False + cls.raise_user_error('reconciliation_account_no_reconcile', + { + 'line': line.rec_name, + 'account': line.account.rec_name, + }) if not party: party = line.party elif line.party and party.id != line.party.id: - return False - if not account.company.currency.is_zero(amount): - return False - return True + cls.raise_user_error('reconciliation_different_parties', { + 'line': line.rec_name, + 'party1': line.party.rec_name, + 'party2': party.rec_name, + }) + if not account.company.currency.is_zero(debit - credit): + language = Transaction().language + languages = Lang.search([('code', '=', language)]) + if not languages: + languages = Lang.search([('code', '=', 'en_US')]) + language = languages[0] + debit = Lang.currency(lang, debit, account.company.currency) + credit = Lang.currency(lang, credit, account.company.currency) + cls.raise_user_error('reconciliation_unbalanced', { + 'debit': debit, + 'credit': credit, + }) class Line(ModelSQL, ModelView): @@ -553,10 +603,7 @@ class Line(ModelSQL, ModelView): cls._sql_constraints += [ ('credit_debit', 'CHECK(credit * debit = 0.0)', - 'Wrong credit/debit values!'), - ] - cls._constraints += [ - ('check_account', 'move_view_inactive_account'), + 'Wrong credit/debit values.'), ] cls.__rpc__.update({ 'on_write': RPC(instantiate=0), @@ -564,15 +611,20 @@ class Line(ModelSQL, ModelView): }) cls._order[0] = ('id', 'DESC') cls._error_messages.update({ - 'add_modify_closed_journal_period': ('You can not ' - 'add/modify lines in a closed journal period!'), - 'modify_posted_move': 'You can not modify lines of a posted move!', - 'modify_reconciled': 'You can not modify reconciled lines!', - 'no_journal': 'No journal defined!', - 'move_view_inactive_account': ('You can not create move lines\n' - 'on view/inactive accounts!'), - 'already_reconciled': 'Line "%s" (%d) already reconciled!', - }) + 'add_modify_closed_journal_period': ('You can not ' + 'add/modify lines in closed journal period "%s".'), + 'modify_posted_move': ('You can not modify lines of move "%s" ' + 'because it is already posted.'), + 'modify_reconciled': ('You can not modify line "%s" because it ' + 'is reconciled.'), + 'no_journal': ('Move line cannot be created because there is ' + 'no journal defined.'), + 'move_view_account': ('You can not create a move line with ' + 'account "%s" because it is a view account.'), + 'move_inactive_account': ('You can not create a move line with ' + 'account "%s" because it is inactive.'), + 'already_reconciled': 'Line "%s" (%d) already reconciled.', + }) @classmethod def __register__(cls, module_name): @@ -1136,12 +1188,19 @@ class Line(ModelSQL, ModelView): def on_write(cls, lines): return list(set(l.id for line in lines for l in line.move.lines)) + @classmethod + def validate(cls, lines): + super(Line, cls).validate(lines) + for line in lines: + line.check_account() + def check_account(self): if self.account.kind in ('view',): - return False + self.raise_user_error('move_view_account', ( + self.account.rec_name,)) if not self.account.active: - return False - return True + self.raise_user_error('move_inactive_account', ( + self.account.rec_name,)) @classmethod def check_journal_period_modify(cls, period, journal): @@ -1157,7 +1216,8 @@ class Line(ModelSQL, ModelView): if journal_periods: journal_period, = journal_periods if journal_period.state == 'close': - cls.raise_user_error('add_modify_closed_journal_period') + cls.raise_user_error('add_modify_closed_journal_period', ( + journal_period.rec_name,)) else: JournalPeriod.create([{ 'name': journal.name + ' - ' + period.name, @@ -1173,9 +1233,11 @@ class Line(ModelSQL, ModelView): journal_period_done = [] for line in lines: if line.move.state == 'posted': - cls.raise_user_error('modify_posted_move') + cls.raise_user_error('modify_posted_move', ( + line.move.rec_name,)) if line.reconciliation: - cls.raise_user_error('modify_reconciled') + cls.raise_user_error('modify_reconciled', ( + line.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, @@ -1188,7 +1250,7 @@ class Line(ModelSQL, ModelView): cls.check_modify(lines) moves = [x.move for x in lines] super(Line, cls).delete(lines) - Move.validate(moves) + Move.validate_move(moves) @classmethod def write(cls, lines, vals): @@ -1201,7 +1263,7 @@ class Line(ModelSQL, ModelView): Transaction().timestamp = {} - Move.validate(list(set(l.move for l in lines) | set(moves))) + Move.validate_move(list(set(l.move for l in lines) | set(moves))) @classmethod def create(cls, vlist): @@ -1240,7 +1302,7 @@ class Line(ModelSQL, ModelView): for line in lines) for period, journal in period_and_journals: cls.check_journal_period_modify(period, journal) - Move.validate(list(set(line.move for line in lines))) + Move.validate_move(list(set(line.move for line in lines))) return lines @classmethod diff --git a/period.py b/period.py index b4ffed355c..0491e0e6a6 100644 --- a/period.py +++ b/period.py @@ -55,32 +55,31 @@ class Period(ModelSQL, ModelView): @classmethod def __setup__(cls): super(Period, cls).__setup__() - cls._constraints += [ - ('check_dates', 'periods_overlaps'), - ('check_fiscalyear_dates', 'fiscalyear_dates'), - ('check_post_move_sequence', 'check_move_sequence'), - ] cls._order.insert(0, ('start_date', 'ASC')) cls._error_messages.update({ - 'no_period_date': 'No period defined for this date!', - 'modify_del_period_moves': ('You can not modify/delete ' - 'a period with moves!'), - 'create_period_closed_fiscalyear': ('You can not create ' - 'a period on a closed fiscal year!'), - 'open_period_closed_fiscalyear': ('You can not open ' - 'a period from a closed fiscal year!'), - 'change_post_move_sequence': ('You can not change ' - 'the post move sequence ' - 'if there is already posted moves in the period'), - 'close_period_non_posted_move': ('You can not close ' - 'a period with non posted moves!'), - 'periods_overlaps': 'You can not have two overlapping periods!', - 'check_move_sequence': ('You must have different ' - 'post move sequences per fiscal year ' - 'and in the same company!'), - 'fiscalyear_dates': ('The period dates must be in ' - 'the fiscal year dates'), - }) + 'no_period_date': 'No period defined for date "%s".', + 'modify_del_period_moves': ('You can not modify/delete ' + 'period "%s" because it has moves.'), + 'create_period_closed_fiscalyear': ('You can not create ' + 'a period on fiscal year "%s" because it is closed.'), + 'open_period_closed_fiscalyear': ('You can not open period ' + '"%(period)s" because its fiscal year "%(fiscalyear)s" is ' + 'closed.'), + 'change_post_move_sequence': ('You can not change the post ' + 'move sequence of period "%s" because there are already ' + 'posted moves in the period.'), + 'close_period_non_posted_move': ('You can not close period ' + '"%s" because there are non posted moves in this period.'), + 'periods_overlap': ('"%(first)s" and "%(second)s" periods ' + 'overlap.'), + 'check_move_sequence': ('Period "%(first)s" and "%(second)s" ' + 'have the same sequence.'), + 'check_move_sequence_company': ('Company of sequence ' + '"%(sequence)s" does not match the company of period ' + '"%(period)s" to which it is assigned to.'), + 'fiscalyear_dates': ('Dates of period "%s" are outside ' + 'are outside it\'s fiscal year dates.'), + }) @staticmethod def default_state(): @@ -97,6 +96,14 @@ class Period(ModelSQL, ModelView): def search_company(cls, name, clause): return [('fiscalyear.%s' % name,) + tuple(clause[1:])] + @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 check_dates(self): cursor = Transaction().cursor if self.type != 'standard': @@ -113,25 +120,37 @@ class Period(ModelSQL, ModelView): self.end_date, self.end_date, self.start_date, self.end_date, self.fiscalyear.id, self.id)) - return not cursor.fetchone() + period_id = cursor.fetchone() + if period_id: + overlapping_period = self.__class__(period_id[0]) + self.raise_user_error('periods_overlap', { + 'first': self.rec_name, + 'second': overlapping_period.rec_name, + }) def check_fiscalyear_dates(self): - return not (self.start_date < self.fiscalyear.start_date - or self.end_date > self.fiscalyear.end_date) + if (self.start_date < self.fiscalyear.start_date + or self.end_date > self.fiscalyear.end_date): + self.raise_user_error('fiscalyear_dates', (self.rec_name,)) def check_post_move_sequence(self): if not self.post_move_sequence: - return True - if self.search([ - ('post_move_sequence', '=', self.post_move_sequence.id), - ('fiscalyear', '!=', self.fiscalyear.id), - ]): - return False - if (self.post_move_sequence.company - and (self.post_move_sequence.company.id != - self.fiscalyear.company.id)): - return False - return True + return + periods = self.search([ + ('post_move_sequence', '=', self.post_move_sequence.id), + ('fiscalyear', '!=', self.fiscalyear.id), + ]) + if periods: + self.raise_user_error('check_move_sequence', { + 'first': self.rec_name, + 'second': periods[0].rec_name, + }) + if (self.post_move_sequence.company and + self.post_move_sequence.company != self.fiscalyear.company): + self.raise_user_error('check_move_sequence_company', { + 'sequence': self.post_move_sequence.rec_name, + 'period': self.rec_name, + }) @classmethod def find(cls, company_id, date=None, exception=True, test_state=True): @@ -142,7 +161,9 @@ class Period(ModelSQL, ModelView): if no period is found. If test_state is true, it will search on non-closed periods ''' - Date = Pool().get('ir.date') + pool = Pool() + Date = pool.get('ir.date') + Lang = pool.get('ir.lang') if not date: date = Date.today() @@ -157,7 +178,14 @@ class Period(ModelSQL, ModelView): periods = cls.search(clause, order=[('start_date', 'DESC')], limit=1) if not periods: if exception: - cls.raise_user_error('no_period_date') + language = Transaction().language + languages = Lang.search([('code', '=', language)]) + if not languages: + languages = Lang.search([('code', '=', 'en_US')]) + language = languages[0] + formatted = Lang.strftime(date, language.code, + language.date) + cls.raise_user_error('no_period_date', (formatted,)) else: return None return periods[0].id @@ -169,7 +197,8 @@ class Period(ModelSQL, ModelView): ('period', 'in', [p.id for p in periods]), ], limit=1) if moves: - cls.raise_user_error('modify_del_period_moves') + cls.raise_user_error('modify_del_period_moves', ( + moves[0].period.rec_name,)) @classmethod def search(cls, args, offset=0, limit=None, order=None, count=False, @@ -206,7 +235,8 @@ class Period(ModelSQL, ModelView): if vals.get('fiscalyear'): fiscalyear = FiscalYear(vals['fiscalyear']) if fiscalyear.state == 'close': - cls.raise_user_error('create_period_closed_fiscalyear') + cls.raise_user_error('create_period_closed_fiscalyear', + (fiscalyear.rec_name,)) if not vals.get('post_move_sequence'): vals['post_move_sequence'] = ( fiscalyear.post_move_sequence.id) @@ -222,7 +252,10 @@ class Period(ModelSQL, ModelView): if vals.get('state') == 'open': for period in periods: if period.fiscalyear.state == 'close': - cls.raise_user_error('open_period_closed_fiscalyear') + cls.raise_user_error('open_period_closed_fiscalyear', { + 'period': period.rec_name, + 'fiscalyear': period.fiscalyear.rec_name, + }) if vals.get('post_move_sequence'): for period in periods: if period.post_move_sequence and \ @@ -232,7 +265,8 @@ class Period(ModelSQL, ModelView): ('period', '=', period.id), ('state', '=', 'posted'), ]): - cls.raise_user_error('change_post_move_sequence') + cls.raise_user_error('change_post_move_sequence', + (period.rec_name,)) super(Period, cls).write(periods, vals) @classmethod @@ -250,7 +284,8 @@ class Period(ModelSQL, ModelView): ('period', 'in', [p.id for p in periods]), ('state', '!=', 'posted'), ]): - cls.raise_user_error('close_period_non_posted_move') + cls.raise_user_error('close_period_non_posted_move', + (period.rec_name,)) #First close the period to be sure #it will not have new journal.period created between. cls.write(periods, { diff --git a/tax.py b/tax.py index 2d68bd1e14..3746d2cf25 100644 --- a/tax.py +++ b/tax.py @@ -58,16 +58,14 @@ class TaxCodeTemplate(ModelSQL, ModelView): @classmethod def __setup__(cls): super(TaxCodeTemplate, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_tax_code'), - ] - cls._error_messages.update({ - 'recursive_tax_code': ( - 'You can not create recursive tax codes!'), - }) cls._order.insert(0, ('code', 'ASC')) cls._order.insert(0, ('account', 'ASC')) + @classmethod + def validate(cls, templates): + super(CodeTemplate, cls).validate(templates) + cls.check_recursion(templates) + def _get_tax_code_value(self, code=None): ''' Set values for tax code creation. @@ -159,15 +157,13 @@ class TaxCode(ModelSQL, ModelView): @classmethod def __setup__(cls): super(TaxCode, cls).__setup__() - cls._constraints += [ - ('check_recursion', 'recursive_tax_code'), - ] - cls._error_messages.update({ - 'recursive_tax_code': ( - 'You can not create recursive tax codes!'), - }) cls._order.insert(0, ('code', 'ASC')) + @classmethod + def validate(cls, codes): + super(TaxCode, cls).validate(codes) + cls.check_recursion(codes) + @staticmethod def default_active(): return True diff --git a/tax.xml b/tax.xml index d9e09a295c..50d7195f15 100644 --- a/tax.xml +++ b/tax.xml @@ -506,5 +506,23 @@ this repository contains the full copyright notices and license terms. --> + + account.tax.code + en_US + error + recursion_error + Recursion error: Tax Code "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + + + + account.tax.code.template + en_US + error + recursion_error + Recursion error: Tax Code Template "%(rec_name)s" with parent "%(parent_rec_name)s" was configured as ancestor of itself. + account + +