From e019315e9c7fc85b0dec3d39bdce5ffd9a171986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 1 Apr 2017 00:25:49 +0200 Subject: [PATCH] Remove Property fields issue2349 review34831002 --- __init__.py | 4 + account.py | 46 +++--------- configuration.py | 144 ++++++++++++++++-------------------- configuration.xml | 30 -------- journal.py | 136 +++++++++++++++++++++++++++++----- party.py | 111 ++++++++++++++++++++++++--- tax.py | 5 +- view/configuration_form.xml | 1 - 8 files changed, 297 insertions(+), 180 deletions(-) diff --git a/__init__.py b/__init__.py index d9dc3481ed..86b3b6b481 100644 --- a/__init__.py +++ b/__init__.py @@ -41,9 +41,12 @@ def register(): AgedBalanceContext, AgedBalance, Configuration, + ConfigurationDefaultAccount, Period, JournalType, Journal, + JournalSequence, + JournalAccount, JournalCashContext, JournalPeriod, Move, @@ -75,6 +78,7 @@ def register(): CreateMoveTemplate, CreateMoveKeywords, Party, + PartyAccount, module='account', type_='model') Pool.register( OpenType, diff --git a/account.py b/account.py index 45ba0083c3..1b05ba9339 100644 --- a/account.py +++ b/account.py @@ -2004,44 +2004,18 @@ class CreateChart(Wizard): def transition_create_properties(self): pool = Pool() - Property = pool.get('ir.property') - ModelField = pool.get('ir.model.field') + Configuration = pool.get('account.configuration') with Transaction().set_context(company=self.properties.company.id): - account_receivable_field, = ModelField.search([ - ('model.model', '=', 'party.party'), - ('name', '=', 'account_receivable'), - ], limit=1) - properties = Property.search([ - ('field', '=', account_receivable_field.id), - ('res', '=', None), - ('company', '=', self.properties.company.id), - ]) - Property.delete(properties) - if self.properties.account_receivable: - Property.create([{ - 'field': account_receivable_field.id, - 'value': str( - self.properties.account_receivable), - 'company': self.properties.company.id, - }]) - - account_payable_field, = ModelField.search([ - ('model.model', '=', 'party.party'), - ('name', '=', 'account_payable'), - ], limit=1) - properties = Property.search([ - ('field', '=', account_payable_field.id), - ('res', '=', None), - ('company', '=', self.properties.company.id), - ]) - Property.delete(properties) - if self.properties.account_payable: - Property.create([{ - 'field': account_payable_field.id, - 'value': str(self.properties.account_payable), - 'company': self.properties.company.id, - }]) + account_receivable = self.properties.account_receivable + account_payable = self.properties.account_payable + config = Configuration(1) + config.set_multivalue( + 'default_account_receivable', + account_receivable.id if account_receivable else None) + config.set_multivalue( + 'default_account_payable', + account_payable.id if account_payable else None) return 'end' diff --git a/configuration.py b/configuration.py index 9c8357c860..c4341324f0 100644 --- a/configuration.py +++ b/configuration.py @@ -1,112 +1,94 @@ # 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 (ModelView, ModelSQL, - ModelSingleton, MatchMixin, fields, sequence_ordered) +from trytond import backend from trytond.pool import Pool +from trytond.model import ModelView, ModelSQL, ModelSingleton, fields from trytond.transaction import Transaction from trytond.pyson import Eval +from trytond.modules.company.model import ( + CompanyMultiValueMixin, CompanyValueMixin) -__all__ = ['Configuration', 'ConfigurationTaxRounding'] - +__all__ = ['Configuration', 'ConfigurationDefaultAccount', + 'ConfigurationTaxRounding'] tax_roundings = [ ('document', 'Per Document'), ('line', 'Per Line'), ] -class Configuration(ModelSingleton, ModelSQL, ModelView): +class Configuration( + ModelSingleton, ModelSQL, ModelView, CompanyMultiValueMixin): 'Account Configuration' __name__ = 'account.configuration' - default_account_receivable = fields.Function(fields.Many2One( - 'account.account', 'Default Account Receivable', - domain=[ + default_account_receivable = fields.MultiValue(fields.Many2One( + 'account.account', "Default Account Receivable", + domain=[ ('kind', '=', 'receivable'), ('company', '=', Eval('context', {}).get('company', -1)), - ]), - 'get_account', setter='set_account') - default_account_payable = fields.Function(fields.Many2One( - 'account.account', 'Default Account Payable', - domain=[ + ])) + default_account_payable = fields.MultiValue(fields.Many2One( + 'account.account', "Default Account Payable", + domain=[ ('kind', '=', 'payable'), ('company', '=', Eval('context', {}).get('company', -1)), - ]), - 'get_account', setter='set_account') - tax_rounding = fields.Function(fields.Selection(tax_roundings, - 'Tax Rounding'), 'on_change_with_tax_rounding') - tax_roundings = fields.One2Many('account.configuration.tax_rounding', - 'configuration', 'Tax Roundings') + ])) + tax_rounding = fields.MultiValue(fields.Selection( + tax_roundings, "Tax Rounding")) @classmethod - def default_tax_rounding(cls): - return 'document' + def multivalue_model(cls, field): + pool = Pool() + if field in {'default_account_receivable', 'default_account_payable'}: + return pool.get('account.configuration.default_account') + return super(Configuration, cls).multivalue_model(field) @classmethod - def _get_account_field(cls, name): - pool = Pool() - ModelField = pool.get('ir.model.field') - if name in ['default_account_receivable', 'default_account_payable']: - field, = ModelField.search([ - ('model.model', '=', 'party.party'), - ('name', '=', name[8:]), - ], limit=1) - return field - - def get_account(self, name): - pool = Pool() - Property = pool.get('ir.property') - company_id = Transaction().context.get('company') - account_field = self._get_account_field(name) - properties = Property.search([ - ('field', '=', account_field.id), - ('res', '=', None), - ('company', '=', company_id), - ], limit=1) - if properties: - prop, = properties - return prop.value.id - - @classmethod - def set_account(cls, configurations, name, value): - pool = Pool() - Property = pool.get('ir.property') - company_id = Transaction().context.get('company') - account_field = cls._get_account_field(name) - properties = Property.search([ - ('field', '=', account_field.id), - ('res', '=', None), - ('company', '=', company_id), - ]) - Property.delete(properties) - if value: - Property.create([{ - 'field': account_field.id, - 'value': 'account.account,%s' % value, - 'company': company_id, - }]) - - @fields.depends('tax_roundings') - def on_change_with_tax_rounding(self, name=None, pattern=None): - context = Transaction().context - if pattern is None: - pattern = {} - pattern = pattern.copy() - pattern['company'] = context.get('company') - - for line in self.tax_roundings: - if line.match(pattern): - return line.method - return self.default_tax_rounding() + def default_tax_rounding(cls, **pattern): + return cls.multivalue_model('tax_rounding').default_tax_rounding() -class ConfigurationTaxRounding( - sequence_ordered(), ModelSQL, ModelView, MatchMixin): +class ConfigurationDefaultAccount(ModelSQL, CompanyValueMixin): + "Account Configuration Default Account" + __name__ = 'account.configuration.default_account' + default_account_receivable = fields.Many2One( + 'account.account', "Default Account Receivable", + domain=[ + ('kind', '=', 'receivable'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + default_account_payable = fields.Many2One( + 'account.account', "Default Account Payable", + domain=[ + ('kind', '=', 'payable'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + + +class ConfigurationTaxRounding(ModelSQL, CompanyValueMixin): 'Account Configuration Tax Rounding' __name__ = 'account.configuration.tax_rounding' configuration = fields.Many2One('account.configuration', 'Configuration', required=True, ondelete='CASCADE') - company = fields.Many2One('company.company', 'Company') - method = fields.Selection(tax_roundings, 'Method', required=True) + tax_rounding = fields.Selection(tax_roundings, 'Method', required=True) @classmethod - def default_method(cls): + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') + sql_table = cls.__table__() + cursor = Transaction().connection.cursor() + + super(ConfigurationTaxRounding, cls).__register__(module_name) + + table = TableHandler(cls, module_name) + + # Migration from 4.2: rename method into tax_rounding + if table.column_exist('method'): + cursor.execute(*sql_table.update( + [sql_table.tax_rounding], [sql_table.method])) + table.drop_column('method') + + @classmethod + def default_tax_rounding(cls): return 'document' diff --git a/configuration.xml b/configuration.xml index 40f5e79a42..5af725a092 100644 --- a/configuration.xml +++ b/configuration.xml @@ -42,35 +42,5 @@ this repository contains the full copyright notices and license terms. --> - - - account.configuration.tax_rounding - form - configuration_tax_rounding_form - - - account.configuration.tax_rounding - tree - configuration_tax_rounding_list - - - - - - - - - - - - - - - - - diff --git a/journal.py b/journal.py index 8c32d82aae..3f215639a8 100644 --- a/journal.py +++ b/journal.py @@ -12,8 +12,11 @@ from trytond.pyson import Eval, Bool from trytond.transaction import Transaction from trytond.pool import Pool from trytond.tools import reduce_ids, grouped_slice +from trytond.tools.multivalue import migrate_property +from trytond.modules.company.model import ( + CompanyMultiValueMixin, CompanyValueMixin) -__all__ = ['JournalType', 'Journal', +__all__ = ['JournalType', 'Journal', 'JournalSequence', 'JournalAccount', 'JournalCashContext', 'JournalPeriod', 'CloseJournalPeriod', 'ReOpenJournalPeriod'] @@ -44,21 +47,28 @@ class JournalType(ModelSQL, ModelView): cls._order.insert(0, ('code', 'ASC')) -class Journal(ModelSQL, ModelView): +class Journal(ModelSQL, ModelView, CompanyMultiValueMixin): 'Journal' __name__ = 'account.journal' name = fields.Char('Name', size=None, required=True, translate=True) code = fields.Char('Code', size=None) active = fields.Boolean('Active', select=True) type = fields.Selection('get_types', 'Type', required=True) - sequence = fields.Property(fields.Many2One('ir.sequence', 'Sequence', - domain=[('code', '=', 'account.journal')], - context={'code': 'account.journal'}, + sequence = fields.MultiValue(fields.Many2One( + 'ir.sequence', "Sequence", + domain=[ + ('code', '=', 'account.journal'), + ('company', 'in', [ + Eval('context', {}).get('company', -1), None]), + ], states={ 'required': Bool(Eval('context', {}).get('company', -1)), })) - credit_account = fields.Property(fields.Many2One('account.account', - 'Default Credit Account', domain=[ + sequences = fields.One2Many( + 'account.journal.sequence', 'journal', "Sequences") + credit_account = fields.MultiValue(fields.Many2One( + 'account.account', "Default Credit Account", + domain=[ ('kind', '!=', 'view'), ('company', '=', Eval('context', {}).get('company', -1)), ], @@ -66,9 +76,11 @@ class Journal(ModelSQL, ModelView): 'required': ((Eval('type').in_(['cash', 'write-off'])) & (Eval('context', {}).get('company', -1) != -1)), 'invisible': ~Eval('context', {}).get('company', -1), - }, depends=['type'])) - debit_account = fields.Property(fields.Many2One('account.account', - 'Default Debit Account', domain=[ + }, + depends=['type'])) + debit_account = fields.MultiValue(fields.Many2One( + 'account.account', "Default Debit Account", + domain=[ ('kind', '!=', 'view'), ('company', '=', Eval('context', {}).get('company', -1)), ], @@ -76,7 +88,10 @@ class Journal(ModelSQL, ModelView): 'required': ((Eval('type').in_(['cash', 'write-off'])) & (Eval('context', {}).get('company', -1) != -1)), 'invisible': ~Eval('context', {}).get('company', -1), - }, depends=['type'])) + }, + depends=['type'])) + accounts = fields.One2Many( + 'account.journal.account', 'journal', "Accounts") debit = fields.Function(fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'get_debit_credit_balance') @@ -96,22 +111,32 @@ class Journal(ModelSQL, ModelView): @classmethod def __register__(cls, module_name): + pool = Pool() + JournalSequence = pool.get('account.journal.sequence') TableHandler = backend.get('TableHandler') + sql_table = cls.__table__() + journal_sequence = JournalSequence.__table__() + super(Journal, cls).__register__(module_name) + cursor = Transaction().connection.cursor() table = TableHandler(cls, module_name) - # Migration from 1.0 sequence Many2One change into Property + # Migration from 1.0 sequence Many2One change into MultiValue if table.column_exist('sequence'): - Property = Pool().get('ir.property') - sql_table = cls.__table__() - cursor.execute(*sql_table.select(sql_table.id, sql_table.sequence)) - for journal_id, sequence_id in cursor.fetchall(): - Property.set('sequence', cls._name, - journal_id, (sequence_id and - 'ir.sequence,' + str(sequence_id) or False)) + query = journal_sequence.insert( + [journal_sequence.journal, journal_sequence.sequence], + sql_table.select(sql_table.id, sql_table.sequence)) + cursor.execute(*query) table.drop_column('sequence', exception=True) + @classmethod + def multivalue_model(cls, field): + pool = Pool() + if field in {'credit_account', 'debit_account'}: + return pool.get('account.journal.account') + return super(Journal, cls).multivalue_model(field) + @staticmethod def default_active(): return True @@ -202,6 +227,79 @@ class Journal(ModelSQL, ModelView): return result +class JournalSequence(ModelSQL, CompanyValueMixin): + "Journal Sequence" + __name__ = 'account.journal.sequence' + journal = fields.Many2One( + 'account.journal', "Journal", ondelete='CASCADE', select=True) + sequence = fields.Many2One( + 'ir.sequence', "Sequence", + domain=[ + ('code', '=', 'account.journal'), + ('company', 'in', [Eval('company', -1), None]), + ], + depends=['company']) + + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') + exist = TableHandler.table_exist(cls._table) + + super(JournalSequence, cls).__register__(module_name) + + if not exist: + cls._migrate_property([], [], []) + + @classmethod + def _migrate_property(cls, field_names, value_names, fields): + field_names.append('sequence') + value_names.append('sequence') + fields.append('company') + migrate_property( + 'account.journal', field_names, cls, value_names, + parent='journal', fields=fields) + + +class JournalAccount(ModelSQL, CompanyValueMixin): + "Journal Account" + __name__ = 'account.journal.account' + journal = fields.Many2One( + 'account.journal', "Journal", ondelete='CASCADE', select=True) + credit_account = fields.Many2One( + 'account.account', "Default Credit Account", + domain=[ + ('kind', '!=', 'view'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + debit_account = fields.Many2One( + 'account.account', "Default Debit Account", + domain=[ + ('kind', '!=', 'view'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') + exist = TableHandler.table_exist(cls._table) + + super(JournalAccount, cls).__register__(module_name) + + if not exist: + cls._migrate_property([], [], []) + + @classmethod + def _migrate_property(cls, field_names, value_names, fields): + field_names.extend(['credit_account', 'debit_account']) + value_names.extend(['credit_account', 'debit_account']) + fields.append('company') + migrate_property( + 'account.journal', field_names, cls, value_names, + parent='journal', fields=fields) + + class JournalCashContext(ModelView): 'Journal Cash Context' __name__ = 'account.journal.open_cash.context' diff --git a/party.py b/party.py index f47c424661..a5844768af 100644 --- a/party.py +++ b/party.py @@ -6,20 +6,29 @@ from sql import Literal, Null, Cast from sql.aggregate import Sum from sql.conditionals import Coalesce -from trytond.model import fields +from trytond import backend +from trytond.model import ModelSQL, fields from trytond.pyson import Eval, Bool from trytond.transaction import Transaction from trytond.pool import Pool, PoolMeta from trytond.tools import reduce_ids, grouped_slice +from trytond.tools.multivalue import migrate_property +from trytond.modules.company.model import ( + CompanyMultiValueMixin, CompanyValueMixin) -__all__ = ['Party', 'PartyReplace'] +__all__ = ['Party', 'PartyAccount', 'PartyReplace'] +account_names = [ + 'account_payable', 'account_receivable', + 'customer_tax_rule', 'supplier_tax_rule'] -class Party: +class Party(CompanyMultiValueMixin): __metaclass__ = PoolMeta __name__ = 'party.party' - account_payable = fields.Property(fields.Many2One('account.account', - 'Account Payable', domain=[ + accounts = fields.One2Many('party.party.account', 'party', "Accounts") + account_payable = fields.MultiValue(fields.Many2One( + 'account.account', "Account Payable", + domain=[ ('kind', '=', 'payable'), ('company', '=', Eval('context', {}).get('company', -1)), ], @@ -27,8 +36,9 @@ class Party: 'required': Bool(Eval('context', {}).get('company')), 'invisible': ~Eval('context', {}).get('company'), })) - account_receivable = fields.Property(fields.Many2One('account.account', - 'Account Receivable', domain=[ + account_receivable = fields.MultiValue(fields.Many2One( + 'account.account', "Account Receivable", + domain=[ ('kind', '=', 'receivable'), ('company', '=', Eval('context', {}).get('company', -1)), ], @@ -36,8 +46,8 @@ class Party: 'required': Bool(Eval('context', {}).get('company')), 'invisible': ~Eval('context', {}).get('company'), })) - customer_tax_rule = fields.Property(fields.Many2One('account.tax.rule', - 'Customer Tax Rule', + customer_tax_rule = fields.MultiValue(fields.Many2One( + 'account.tax.rule', "Customer Tax Rule", domain=[ ('company', '=', Eval('context', {}).get('company', -1)), ('kind', 'in', ['sale', 'both']), @@ -45,8 +55,8 @@ class Party: states={ 'invisible': ~Eval('context', {}).get('company'), }, help='Apply this rule on taxes when party is customer.')) - supplier_tax_rule = fields.Property(fields.Many2One('account.tax.rule', - 'Supplier Tax Rule', + supplier_tax_rule = fields.MultiValue(fields.Many2One( + 'account.tax.rule', "Supplier Tax Rule", domain=[ ('company', '=', Eval('context', {}).get('company', -1)), ('kind', 'in', ['purchase', 'both']), @@ -73,6 +83,31 @@ class Party: depends=['currency_digits']), 'get_receivable_payable', searcher='search_receivable_payable') + @classmethod + def multivalue_model(cls, field): + pool = Pool() + if field in account_names: + return pool.get('party.party.account') + return super(Party, cls).multivalue_model(field) + + @classmethod + def default_account_payable(cls, **pattern): + pool = Pool() + Configuration = pool.get('account.configuration') + config = Configuration(1) + account = config.get_multivalue( + 'default_account_payable', **pattern) + return account.id if account else None + + @classmethod + def default_account_receivable(cls, **pattern): + pool = Pool() + Configuration = pool.get('account.configuration') + config = Configuration(1) + account = config.get_multivalue( + 'default_account_receivable', **pattern) + return account.id if account else None + @classmethod def get_currency_digits(cls, parties, name): pool = Pool() @@ -197,6 +232,60 @@ class Party: return [('id', 'in', query)] +class PartyAccount(ModelSQL, CompanyValueMixin): + "Party Account" + __name__ = 'party.party.account' + party = fields.Many2One( + 'party.party', "Party", ondelete='CASCADE', select=True) + account_payable = fields.Many2One( + 'account.account', "Account Payable", + domain=[ + ('kind', '=', 'payable'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + account_receivable = fields.Many2One( + 'account.account', "Account Receivable", + domain=[ + ('kind', '=', 'receivable'), + ('company', '=', Eval('company', -1)), + ], + depends=['company']) + customer_tax_rule = fields.Many2One( + 'account.tax.rule', "Customer Tax Rule", + domain=[ + ('company', '=', Eval('company', -1)), + ('kind', 'in', ['sale', 'both']), + ], + depends=['company']) + supplier_tax_rule = fields.Many2One( + 'account.tax.rule', "Supplier Tax Rule", + domain=[ + ('company', '=', Eval('company', -1)), + ('kind', 'in', ['purchase', 'both']), + ], + depends=['company']) + + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') + exist = TableHandler.table_exist(cls._table) + + super(PartyAccount, cls).__register__(module_name) + + if not exist: + cls._migrate_property([], [], []) + + @classmethod + def _migrate_property(cls, field_names, value_names, fields): + field_names.extend(account_names) + value_names.extend(account_names) + fields.append('company') + migrate_property( + 'party.party', field_names, cls, value_names, + parent='party', fields=fields) + + class PartyReplace: __metaclass__ = PoolMeta __name__ = 'party.replace' diff --git a/tax.py b/tax.py index 76e64dea61..f5c3be6939 100644 --- a/tax.py +++ b/tax.py @@ -1080,6 +1080,7 @@ class TaxableMixin(object): Configuration = pool.get('account.configuration') config = Configuration(1) + tax_rounding = config.tax_rounding taxes = {} with Transaction().set_context(self._get_tax_context()): taxable_lines = [_TaxableLine(*params) @@ -1096,9 +1097,9 @@ class TaxableMixin(object): else: taxes[taxline]['base'] += taxline['base'] taxes[taxline]['amount'] += taxline['amount'] - if config.tax_rounding == 'line': + if tax_rounding == 'line': self._round_taxes(taxes) - if config.tax_rounding == 'document': + if tax_rounding == 'document': self._round_taxes(taxes) return taxes diff --git a/view/configuration_form.xml b/view/configuration_form.xml index eb884fb4f5..cbeb940bb1 100644 --- a/view/configuration_form.xml +++ b/view/configuration_form.xml @@ -10,5 +10,4 @@ this repository contains the full copyright notices and license terms. -->