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. -->
-