2030 lines
72 KiB
Python
2030 lines
72 KiB
Python
# 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 decimal import Decimal
|
|
import datetime
|
|
from functools import wraps
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
from sql import Column, Null, Window, Literal
|
|
from sql.aggregate import Sum, Max
|
|
from sql.conditionals import Coalesce, Case
|
|
|
|
from trytond.model import ModelView, ModelSQL, fields, Unique
|
|
from trytond.wizard import Wizard, StateView, StateAction, StateTransition, \
|
|
Button
|
|
from trytond.report import Report
|
|
from trytond.tools import reduce_ids, grouped_slice
|
|
from trytond.pyson import Eval, PYSONEncoder
|
|
from trytond.transaction import Transaction
|
|
from trytond.pool import Pool
|
|
from trytond import backend
|
|
|
|
__all__ = ['TypeTemplate', 'Type', 'OpenType', 'AccountTemplate', 'Account',
|
|
'AccountDeferral', 'OpenChartAccountStart', 'OpenChartAccount',
|
|
'GeneralLedgerAccount', 'GeneralLedgerAccountContext',
|
|
'GeneralLedgerLine', 'GeneralLedgerLineContext',
|
|
'GeneralLedger', 'TrialBalance',
|
|
'BalanceSheetContext', 'IncomeStatementContext',
|
|
'AgedBalanceContext', 'AgedBalance', 'AgedBalanceReport',
|
|
'CreateChartStart', 'CreateChartAccount', 'CreateChartProperties',
|
|
'CreateChart', 'UpdateChartStart', 'UpdateChartSucceed', 'UpdateChart']
|
|
|
|
|
|
def inactive_records(func):
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
with Transaction().set_context(active_test=False):
|
|
return func(*args, **kwargs)
|
|
return wrapper
|
|
|
|
|
|
class TypeTemplate(ModelSQL, ModelView):
|
|
'Account Type Template'
|
|
__name__ = 'account.account.type.template'
|
|
name = fields.Char('Name', required=True)
|
|
parent = fields.Many2One('account.account.type.template', 'Parent',
|
|
ondelete="RESTRICT")
|
|
childs = fields.One2Many('account.account.type.template', 'parent',
|
|
'Children')
|
|
sequence = fields.Integer('Sequence')
|
|
balance_sheet = fields.Boolean('Balance Sheet')
|
|
income_statement = fields.Boolean('Income Statement')
|
|
display_balance = fields.Selection([
|
|
('debit-credit', 'Debit - Credit'),
|
|
('credit-debit', 'Credit - Debit'),
|
|
], 'Display Balance', required=True)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(TypeTemplate, cls).__setup__()
|
|
cls._order.insert(0, ('sequence', 'ASC'))
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
TableHandler = backend.get('TableHandler')
|
|
table = TableHandler(cls, module_name)
|
|
|
|
super(TypeTemplate, cls).__register__(module_name)
|
|
|
|
# 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 order_sequence(tables):
|
|
table, _ = tables[None]
|
|
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
|
|
|
|
@staticmethod
|
|
def default_balance_sheet():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_income_statement():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_display_balance():
|
|
return 'debit-credit'
|
|
|
|
def get_rec_name(self, name):
|
|
if self.parent:
|
|
return self.parent.get_rec_name(name) + '\\' + self.name
|
|
else:
|
|
return self.name
|
|
|
|
def _get_type_value(self, type=None):
|
|
'''
|
|
Set the values for account creation.
|
|
'''
|
|
res = {}
|
|
if not type or type.name != self.name:
|
|
res['name'] = self.name
|
|
if not type or type.sequence != self.sequence:
|
|
res['sequence'] = self.sequence
|
|
if not type or type.balance_sheet != self.balance_sheet:
|
|
res['balance_sheet'] = self.balance_sheet
|
|
if not type or type.income_statement != self.income_statement:
|
|
res['income_statement'] = self.income_statement
|
|
if not type or type.display_balance != self.display_balance:
|
|
res['display_balance'] = self.display_balance
|
|
if not type or type.template != self:
|
|
res['template'] = self.id
|
|
return res
|
|
|
|
def create_type(self, company_id, template2type=None):
|
|
'''
|
|
Create recursively types based on template.
|
|
template2type is a dictionary with template id as key and type id as
|
|
value, used to convert template id into type. The dictionary is filled
|
|
with new types.
|
|
'''
|
|
pool = Pool()
|
|
Type = pool.get('account.account.type')
|
|
assert self.parent is None
|
|
|
|
if template2type is None:
|
|
template2type = {}
|
|
|
|
def create(templates):
|
|
values = []
|
|
created = []
|
|
for template in templates:
|
|
if template.id not in template2type:
|
|
vals = template._get_type_value()
|
|
vals['company'] = company_id
|
|
if template.parent:
|
|
vals['parent'] = template2type[template.parent.id]
|
|
else:
|
|
vals['parent'] = None
|
|
values.append(vals)
|
|
created.append(template)
|
|
|
|
types = Type.create(values)
|
|
for template, type_ in zip(created, types):
|
|
template2type[template.id] = type_.id
|
|
|
|
childs = [self]
|
|
while childs:
|
|
create(childs)
|
|
childs = sum((c.childs for c in childs), ())
|
|
|
|
|
|
class Type(ModelSQL, ModelView):
|
|
'Account Type'
|
|
__name__ = 'account.account.type'
|
|
name = fields.Char('Name', size=None, required=True)
|
|
parent = fields.Many2One('account.account.type', 'Parent',
|
|
ondelete="RESTRICT", domain=[
|
|
('company', '=', Eval('company')),
|
|
], depends=['company'])
|
|
childs = fields.One2Many('account.account.type', 'parent', 'Children',
|
|
domain=[
|
|
('company', '=', Eval('company')),
|
|
], depends=['company'])
|
|
sequence = fields.Integer('Sequence',
|
|
help='Use to order the account type')
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
amount = fields.Function(fields.Numeric('Amount',
|
|
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
|
|
'get_amount')
|
|
amount_cmp = fields.Function(fields.Numeric('Amount',
|
|
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
|
|
'get_amount_cmp')
|
|
balance_sheet = fields.Boolean('Balance Sheet')
|
|
income_statement = fields.Boolean('Income Statement')
|
|
display_balance = fields.Selection([
|
|
('debit-credit', 'Debit - Credit'),
|
|
('credit-debit', 'Credit - Debit'),
|
|
], 'Display Balance', required=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
ondelete="RESTRICT")
|
|
template = fields.Many2One('account.account.type.template', 'Template')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Type, cls).__setup__()
|
|
cls._order.insert(0, ('sequence', 'ASC'))
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
TableHandler = backend.get('TableHandler')
|
|
table = TableHandler(cls, module_name)
|
|
|
|
super(Type, cls).__register__(module_name)
|
|
|
|
# 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 order_sequence(tables):
|
|
table, _ = tables[None]
|
|
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
|
|
|
|
@staticmethod
|
|
def default_balance_sheet():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_income_statement():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_display_balance():
|
|
return 'debit-credit'
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.company.currency.digits
|
|
|
|
@classmethod
|
|
def get_amount(cls, types, name):
|
|
pool = Pool()
|
|
Account = pool.get('account.account')
|
|
GeneralLedger = pool.get('account.general_ledger.account')
|
|
|
|
res = {}
|
|
for type_ in types:
|
|
res[type_.id] = Decimal('0.0')
|
|
|
|
childs = cls.search([
|
|
('parent', 'child_of', [t.id for t in types]),
|
|
])
|
|
type_sum = {}
|
|
for type_ in childs:
|
|
type_sum[type_.id] = Decimal('0.0')
|
|
|
|
start_period_ids = GeneralLedger.get_period_ids('start_%s' % name)
|
|
end_period_ids = GeneralLedger.get_period_ids('end_%s' % name)
|
|
period_ids = list(
|
|
set(end_period_ids).difference(set(start_period_ids)))
|
|
|
|
with Transaction().set_context(periods=period_ids):
|
|
accounts = Account.search([
|
|
('type', 'in', [t.id for t in childs]),
|
|
('kind', '!=', 'view'),
|
|
])
|
|
for account in accounts:
|
|
type_sum[account.type.id] += (account.debit - account.credit)
|
|
|
|
for type_ in types:
|
|
childs = cls.search([
|
|
('parent', 'child_of', [type_.id]),
|
|
])
|
|
for child in childs:
|
|
res[type_.id] += type_sum[child.id]
|
|
res[type_.id] = type_.company.currency.round(res[type_.id])
|
|
if type_.display_balance == 'credit-debit':
|
|
res[type_.id] = - res[type_.id]
|
|
return res
|
|
|
|
@classmethod
|
|
def get_amount_cmp(cls, types, name):
|
|
transaction = Transaction()
|
|
current = transaction.context
|
|
if not current.get('comparison'):
|
|
return dict.fromkeys([t.id for t in types], None)
|
|
new = {}
|
|
for key, value in current.iteritems():
|
|
if key.endswith('_cmp'):
|
|
new[key[:-4]] = value
|
|
with transaction.set_context(new):
|
|
return cls.get_amount(types, name)
|
|
|
|
@classmethod
|
|
def view_attributes(cls):
|
|
return [
|
|
('/tree/field[@name="amount_cmp"]', 'tree_invisible',
|
|
~Eval('comparison', False)),
|
|
]
|
|
|
|
def get_rec_name(self, name):
|
|
if self.parent:
|
|
return self.parent.get_rec_name(name) + '\\' + self.name
|
|
else:
|
|
return self.name
|
|
|
|
@classmethod
|
|
def delete(cls, types):
|
|
types = cls.search([
|
|
('parent', 'child_of', [t.id for t in types]),
|
|
])
|
|
super(Type, cls).delete(types)
|
|
|
|
def update_type(self, template2type=None):
|
|
'''
|
|
Update recursively types based on template.
|
|
template2type is a dictionary with template id as key and type id as
|
|
value, used to convert template id into type. The dictionary is filled
|
|
with new types
|
|
'''
|
|
if template2type is None:
|
|
template2type = {}
|
|
|
|
values = []
|
|
childs = [self]
|
|
while childs:
|
|
for child in childs:
|
|
if child.template:
|
|
vals = child.template._get_type_value(type=child)
|
|
if vals:
|
|
values.append([child])
|
|
values.append(vals)
|
|
template2type[child.template.id] = child.id
|
|
childs = sum((c.childs for c in childs), ())
|
|
if values:
|
|
self.write(*values)
|
|
|
|
|
|
class OpenType(Wizard):
|
|
'Open Type'
|
|
__name__ = 'account.account.open_type'
|
|
start_state = 'open_'
|
|
open_ = StateAction('account.act_account_list2')
|
|
|
|
def do_open_(self, action):
|
|
action['pyson_domain'] = PYSONEncoder().encode([
|
|
('type', '=', Transaction().context['active_id']),
|
|
('kind', '!=', 'view'),
|
|
])
|
|
action['pyson_context'] = PYSONEncoder().encode({
|
|
'date': Transaction().context.get('date'),
|
|
'posted': Transaction().context.get('posted'),
|
|
'cumulate': Transaction().context.get('cumulate'),
|
|
})
|
|
return action, {}
|
|
|
|
|
|
class AccountTemplate(ModelSQL, ModelView):
|
|
'Account Template'
|
|
__name__ = 'account.account.template'
|
|
name = fields.Char('Name', size=None, required=True, select=True)
|
|
code = fields.Char('Code', size=None, select=True)
|
|
type = fields.Many2One('account.account.type.template', 'Type',
|
|
ondelete="RESTRICT",
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
'required': Eval('kind') != 'view',
|
|
}, depends=['kind'])
|
|
parent = fields.Many2One('account.account.template', 'Parent', select=True,
|
|
ondelete="RESTRICT")
|
|
childs = fields.One2Many('account.account.template', 'parent', 'Children')
|
|
reconcile = fields.Boolean('Reconcile',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
}, depends=['kind'])
|
|
kind = fields.Selection([
|
|
('other', 'Other'),
|
|
('payable', 'Payable'),
|
|
('revenue', 'Revenue'),
|
|
('receivable', 'Receivable'),
|
|
('expense', 'Expense'),
|
|
('stock', 'Stock'),
|
|
('view', 'View'),
|
|
], 'Kind', required=True)
|
|
deferral = fields.Boolean('Deferral', states={
|
|
'invisible': Eval('kind') == 'view',
|
|
}, depends=['kind'])
|
|
party_required = fields.Boolean('Party Required',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
},
|
|
depends=['kind'])
|
|
general_ledger_balance = fields.Boolean('General Ledger Balance',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
},
|
|
depends=['kind'],
|
|
help="Display only the balance in the general ledger report")
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(AccountTemplate, cls).__setup__()
|
|
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'
|
|
|
|
@staticmethod
|
|
def default_reconcile():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_deferral():
|
|
return True
|
|
|
|
@staticmethod
|
|
def default_party_required():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_general_ledger_balance():
|
|
return False
|
|
|
|
def get_rec_name(self, name):
|
|
if self.code:
|
|
return self.code + ' - ' + self.name
|
|
else:
|
|
return self.name
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
bool_op = 'AND'
|
|
else:
|
|
bool_op = 'OR'
|
|
return [bool_op,
|
|
('code',) + tuple(clause[1:]),
|
|
(cls._rec_name,) + tuple(clause[1:]),
|
|
]
|
|
|
|
def _get_account_value(self, account=None):
|
|
'''
|
|
Set the values for account creation.
|
|
'''
|
|
res = {}
|
|
if not account or account.name != self.name:
|
|
res['name'] = self.name
|
|
if not account or account.code != self.code:
|
|
res['code'] = self.code
|
|
if not account or account.kind != self.kind:
|
|
res['kind'] = self.kind
|
|
if not account or account.reconcile != self.reconcile:
|
|
res['reconcile'] = self.reconcile
|
|
if not account or account.deferral != self.deferral:
|
|
res['deferral'] = self.deferral
|
|
if not account or account.party_required != self.party_required:
|
|
res['party_required'] = self.party_required
|
|
if (not account
|
|
or account.general_ledger_balance !=
|
|
self.general_ledger_balance):
|
|
res['general_ledger_balance'] = self.general_ledger_balance
|
|
if not account or account.template != self:
|
|
res['template'] = self.id
|
|
return res
|
|
|
|
def create_account(self, company_id, template2account=None,
|
|
template2type=None):
|
|
'''
|
|
Create recursively accounts based on template.
|
|
template2account is a dictionary with template id as key and account id
|
|
as value, used to convert template id into account. The dictionary is
|
|
filled with new accounts
|
|
template2type is a dictionary with type template id as key and type id
|
|
as value, used to convert type template id into type.
|
|
'''
|
|
pool = Pool()
|
|
Account = pool.get('account.account')
|
|
assert self.parent is None
|
|
|
|
if template2account is None:
|
|
template2account = {}
|
|
|
|
if template2type is None:
|
|
template2type = {}
|
|
|
|
def create(templates):
|
|
values = []
|
|
created = []
|
|
for template in templates:
|
|
if template.id not in template2account:
|
|
vals = template._get_account_value()
|
|
vals['company'] = company_id
|
|
if template.parent:
|
|
vals['parent'] = template2account[template.parent.id]
|
|
else:
|
|
vals['parent'] = None
|
|
if template.type:
|
|
vals['type'] = template2type.get(template.type.id)
|
|
else:
|
|
vals['type'] = None
|
|
values.append(vals)
|
|
created.append(template)
|
|
|
|
accounts = Account.create(values)
|
|
for template, account in zip(created, accounts):
|
|
template2account[template.id] = account.id
|
|
|
|
childs = [self]
|
|
while childs:
|
|
create(childs)
|
|
childs = sum((c.childs for c in childs), ())
|
|
|
|
def update_account_taxes(self, template2account, template2tax,
|
|
template_done=None):
|
|
'''
|
|
Update recursively account taxes based on template.
|
|
template2account is a dictionary with template id as key and account id
|
|
as value, used to convert template id into account.
|
|
template2tax is a dictionary with tax template id as key and tax id as
|
|
value, used to convert tax template id into tax.
|
|
template_done is a list of template id already updated. The list is
|
|
filled.
|
|
'''
|
|
Account = Pool().get('account.account')
|
|
|
|
if template2account is None:
|
|
template2account = {}
|
|
if template2tax is None:
|
|
template2tax = {}
|
|
if template_done is None:
|
|
template_done = []
|
|
|
|
def update(templates):
|
|
to_write = []
|
|
for template in templates:
|
|
if template.id not in template_done:
|
|
if template.taxes:
|
|
tax_ids = [template2tax[x.id] for x in template.taxes]
|
|
to_write.append([Account(template2account[template.id])])
|
|
to_write.append({
|
|
'taxes': [
|
|
('add', tax_ids)],
|
|
})
|
|
template_done.append(template.id)
|
|
if to_write:
|
|
Account.write(to_write)
|
|
|
|
childs = [self]
|
|
while childs:
|
|
update(childs)
|
|
childs = sum((c.childs for c in childs), ())
|
|
|
|
|
|
class Account(ModelSQL, ModelView):
|
|
'Account'
|
|
__name__ = 'account.account'
|
|
name = fields.Char('Name', size=None, required=True, select=True)
|
|
code = fields.Char('Code', size=None, select=True)
|
|
active = fields.Boolean('Active', select=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
ondelete="RESTRICT")
|
|
currency = fields.Function(fields.Many2One('currency.currency',
|
|
'Currency'), 'get_currency')
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
second_currency = fields.Many2One('currency.currency',
|
|
'Secondary Currency', help='Force all moves for this account \n'
|
|
'to have this secondary currency.', ondelete="RESTRICT")
|
|
type = fields.Many2One('account.account.type', 'Type', ondelete="RESTRICT",
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
'required': Eval('kind') != 'view',
|
|
},
|
|
domain=[
|
|
('company', '=', Eval('company')),
|
|
], depends=['kind', 'company'])
|
|
parent = fields.Many2One('account.account', 'Parent', select=True,
|
|
left="left", right="right", ondelete="RESTRICT",
|
|
domain=[('company', '=', Eval('company'))],
|
|
depends=['company'])
|
|
left = fields.Integer('Left', required=True, select=True)
|
|
right = fields.Integer('Right', required=True, select=True)
|
|
childs = fields.One2Many('account.account', 'parent', 'Children')
|
|
balance = fields.Function(fields.Numeric('Balance',
|
|
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
|
|
'get_balance')
|
|
credit = fields.Function(fields.Numeric('Credit',
|
|
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
|
|
'get_credit_debit')
|
|
debit = fields.Function(fields.Numeric('Debit',
|
|
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
|
|
'get_credit_debit')
|
|
reconcile = fields.Boolean('Reconcile',
|
|
help='Allow move lines of this account \nto be reconciled.',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
}, depends=['kind'])
|
|
note = fields.Text('Note')
|
|
kind = fields.Selection([
|
|
('other', 'Other'),
|
|
('payable', 'Payable'),
|
|
('revenue', 'Revenue'),
|
|
('receivable', 'Receivable'),
|
|
('expense', 'Expense'),
|
|
('stock', 'Stock'),
|
|
('view', 'View'),
|
|
], 'Kind', required=True)
|
|
deferral = fields.Boolean('Deferral', states={
|
|
'invisible': Eval('kind') == 'view',
|
|
}, depends=['kind'])
|
|
deferrals = fields.One2Many('account.account.deferral', 'account',
|
|
'Deferrals', readonly=True, states={
|
|
'invisible': Eval('kind') == 'view',
|
|
}, depends=['kind'])
|
|
party_required = fields.Boolean('Party Required',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
},
|
|
depends=['kind'])
|
|
general_ledger_balance = fields.Boolean('General Ledger Balance',
|
|
states={
|
|
'invisible': Eval('kind') == 'view',
|
|
},
|
|
depends=['kind'],
|
|
help="Display only the balance in the general ledger report")
|
|
template = fields.Many2One('account.account.template', 'Template')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Account, cls).__setup__()
|
|
cls._error_messages.update({
|
|
'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.'),
|
|
})
|
|
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
|
|
|
|
@staticmethod
|
|
def default_right():
|
|
return 0
|
|
|
|
@staticmethod
|
|
def default_active():
|
|
return True
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company') or None
|
|
|
|
@staticmethod
|
|
def default_reconcile():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_deferral():
|
|
return True
|
|
|
|
@staticmethod
|
|
def default_party_required():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_general_ledger_balance():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_kind():
|
|
return 'view'
|
|
|
|
def get_currency(self, name):
|
|
return self.company.currency.id
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.company.currency.digits
|
|
|
|
@classmethod
|
|
def get_balance(cls, accounts, name):
|
|
pool = Pool()
|
|
MoveLine = pool.get('account.move.line')
|
|
FiscalYear = pool.get('account.fiscalyear')
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
table_a = cls.__table__()
|
|
table_c = cls.__table__()
|
|
line = MoveLine.__table__()
|
|
ids = [a.id for a in accounts]
|
|
balances = dict((i, 0) for i in ids)
|
|
line_query, fiscalyear_ids = MoveLine.query_get(line)
|
|
for sub_ids in grouped_slice(ids):
|
|
red_sql = reduce_ids(table_a.id, sub_ids)
|
|
cursor.execute(*table_a.join(table_c,
|
|
condition=(table_c.left >= table_a.left)
|
|
& (table_c.right <= table_a.right)
|
|
).join(line, condition=line.account == table_c.id
|
|
).select(
|
|
table_a.id,
|
|
Sum(Coalesce(line.debit, 0) - Coalesce(line.credit, 0)),
|
|
where=red_sql & line_query & (table_c.active == True),
|
|
group_by=table_a.id))
|
|
result = cursor.fetchall()
|
|
balances.update(dict(result))
|
|
|
|
# SQLite uses float for SUM
|
|
for account_id, balance in balances.iteritems():
|
|
if isinstance(balance, Decimal):
|
|
break
|
|
balances[account_id] = Decimal(str(balance))
|
|
|
|
for account in accounts:
|
|
balances[account.id] = account.company.currency.round(
|
|
balances[account.id])
|
|
|
|
fiscalyears = FiscalYear.browse(fiscalyear_ids)
|
|
func = lambda accounts, names: \
|
|
{names[0]: cls.get_balance(accounts, names[0])}
|
|
return cls._cumulate(fiscalyears, accounts, {name: balances},
|
|
func)[name]
|
|
|
|
@classmethod
|
|
def get_credit_debit(cls, accounts, names):
|
|
'''
|
|
Function to compute debit, credit for accounts.
|
|
If cumulate is set in the context, it is the cumulate amount over all
|
|
previous fiscal year.
|
|
'''
|
|
pool = Pool()
|
|
MoveLine = pool.get('account.move.line')
|
|
FiscalYear = pool.get('account.fiscalyear')
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
result = {}
|
|
ids = [a.id for a in accounts]
|
|
for name in names:
|
|
if name not in ('credit', 'debit'):
|
|
raise Exception('Bad argument')
|
|
result[name] = dict((i, 0) for i in ids)
|
|
|
|
table = cls.__table__()
|
|
line = MoveLine.__table__()
|
|
line_query, fiscalyear_ids = MoveLine.query_get(line)
|
|
columns = [table.id]
|
|
for name in names:
|
|
columns.append(Sum(Coalesce(Column(line, name), 0)))
|
|
for sub_ids in grouped_slice(ids):
|
|
red_sql = reduce_ids(table.id, sub_ids)
|
|
cursor.execute(*table.join(line, 'LEFT',
|
|
condition=line.account == table.id
|
|
).select(*columns,
|
|
where=red_sql & line_query,
|
|
group_by=table.id))
|
|
for row in cursor.fetchall():
|
|
account_id = row[0]
|
|
for i, name in enumerate(names, 1):
|
|
# SQLite uses float for SUM
|
|
if not isinstance(row[i], Decimal):
|
|
result[name][account_id] = Decimal(str(row[i]))
|
|
else:
|
|
result[name][account_id] = row[i]
|
|
for account in accounts:
|
|
for name in names:
|
|
result[name][account.id] = account.company.currency.round(
|
|
result[name][account.id])
|
|
|
|
if not Transaction().context.get('cumulate'):
|
|
return result
|
|
else:
|
|
fiscalyears = FiscalYear.browse(fiscalyear_ids)
|
|
return cls._cumulate(fiscalyears, accounts, result,
|
|
cls.get_credit_debit)
|
|
|
|
@classmethod
|
|
def _cumulate(cls, fiscalyears, accounts, values, func):
|
|
"""
|
|
Cumulate previous fiscalyear values into values
|
|
func is the method to compute values
|
|
"""
|
|
pool = Pool()
|
|
FiscalYear = pool.get('account.fiscalyear')
|
|
Deferral = pool.get('account.account.deferral')
|
|
names = values.keys()
|
|
|
|
youngest_fiscalyear = None
|
|
for fiscalyear in fiscalyears:
|
|
if (not youngest_fiscalyear
|
|
or (youngest_fiscalyear.start_date
|
|
> fiscalyear.start_date)):
|
|
youngest_fiscalyear = fiscalyear
|
|
|
|
fiscalyear = None
|
|
if youngest_fiscalyear:
|
|
fiscalyears = FiscalYear.search([
|
|
('end_date', '<=', youngest_fiscalyear.start_date),
|
|
('company', '=', youngest_fiscalyear.company),
|
|
], order=[('end_date', 'DESC')], limit=1)
|
|
if fiscalyears:
|
|
fiscalyear, = fiscalyears
|
|
|
|
if not fiscalyear:
|
|
return values
|
|
|
|
if fiscalyear.state == 'close':
|
|
id2deferral = {}
|
|
ids = [a.id for a in accounts]
|
|
for sub_ids in grouped_slice(ids):
|
|
deferrals = Deferral.search([
|
|
('fiscalyear', '=', fiscalyear.id),
|
|
('account', 'in', list(sub_ids)),
|
|
])
|
|
for deferral in deferrals:
|
|
id2deferral[deferral.account.id] = deferral
|
|
|
|
for account in accounts:
|
|
if account.id in id2deferral:
|
|
deferral = id2deferral[account.id]
|
|
for name in names:
|
|
values[name][account.id] += getattr(deferral, name)
|
|
else:
|
|
with Transaction().set_context(fiscalyear=fiscalyear.id,
|
|
date=None, periods=None):
|
|
previous_result = func(accounts, names)
|
|
for name in names:
|
|
vals = values[name]
|
|
for account in accounts:
|
|
vals[account.id] += previous_result[name][account.id]
|
|
|
|
return values
|
|
|
|
def get_rec_name(self, name):
|
|
if self.code:
|
|
return self.code + ' - ' + self.name
|
|
else:
|
|
return self.name
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
bool_op = 'AND'
|
|
else:
|
|
bool_op = 'OR'
|
|
return [bool_op,
|
|
('code',) + tuple(clause[1:]),
|
|
(cls._rec_name,) + tuple(clause[1:]),
|
|
]
|
|
|
|
@classmethod
|
|
def copy(cls, accounts, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
default.setdefault('template')
|
|
default.setdefault('deferrals', [])
|
|
new_accounts = super(Account, cls).copy(accounts, default=default)
|
|
cls._rebuild_tree('parent', None, 0)
|
|
return new_accounts
|
|
|
|
@classmethod
|
|
def write(cls, *args):
|
|
pool = Pool()
|
|
MoveLine = pool.get('account.move.line')
|
|
actions = iter(args)
|
|
args = []
|
|
for accounts, values in zip(actions, actions):
|
|
if not values.get('active', True):
|
|
childs = cls.search([
|
|
('parent', 'child_of', [a.id for a in accounts]),
|
|
])
|
|
if MoveLine.search([
|
|
('account', 'in', [a.id for a in childs]),
|
|
]):
|
|
values = values.copy()
|
|
del values['active']
|
|
args.extend((accounts, values))
|
|
super(Account, cls).write(*args)
|
|
|
|
@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:
|
|
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):
|
|
'''
|
|
Update recursively accounts based on template.
|
|
template2account is a dictionary with template id as key and account id
|
|
as value, used to convert template id into account. The dictionary is
|
|
filled with new accounts.
|
|
template2type is a dictionary with type template id as key and type id
|
|
as value, used to convert type template id into type.
|
|
'''
|
|
if template2account is None:
|
|
template2account = {}
|
|
|
|
if template2type is None:
|
|
template2type = {}
|
|
|
|
values = []
|
|
childs = [self]
|
|
while childs:
|
|
for child in childs:
|
|
if child.template:
|
|
vals = child.template._get_account_value(account=child)
|
|
current_type = child.type.id if child.type else None
|
|
if child.template.type:
|
|
template_type = template2type.get(
|
|
child.template.type.id)
|
|
else:
|
|
template_type = None
|
|
if current_type != template_type:
|
|
vals['type'] = template_type
|
|
if vals:
|
|
values.append([child])
|
|
values.append(vals)
|
|
template2account[child.template.id] = child.id
|
|
childs = sum((c.childs for c in childs), ())
|
|
if values:
|
|
self.write(*values)
|
|
|
|
def update_account_taxes(self, template2account, template2tax):
|
|
'''
|
|
Update recursively account taxes base on template.
|
|
template2account is a dictionary with template id as key and account id
|
|
as value, used to convert template id into account.
|
|
template2tax is a dictionary with tax template id as key and tax id as
|
|
value, used to convert tax template id into tax.
|
|
'''
|
|
if template2account is None:
|
|
template2account = {}
|
|
|
|
if template2tax is None:
|
|
template2tax = {}
|
|
|
|
values = []
|
|
childs = [self]
|
|
while childs:
|
|
for child in childs:
|
|
if not child.template:
|
|
continue
|
|
if not child.template.taxes:
|
|
continue
|
|
tax_ids = [template2tax[x.id] for x in child.template.taxes
|
|
if x.id in template2tax]
|
|
old_tax_ids = [x.id for x in child.taxes]
|
|
for tax_id in tax_ids:
|
|
if tax_id not in old_tax_ids:
|
|
values.append([child])
|
|
values.append({
|
|
'taxes': [
|
|
('add', template2tax[x.id])
|
|
for x in self.template.taxes
|
|
if x.id in template2tax],
|
|
})
|
|
break
|
|
childs = sum((c.childs for c in childs), ())
|
|
if values:
|
|
self.write(*values)
|
|
|
|
|
|
class AccountDeferral(ModelSQL, ModelView):
|
|
'''
|
|
Account Deferral
|
|
|
|
It is used to deferral the debit/credit of account by fiscal year.
|
|
'''
|
|
__name__ = 'account.account.deferral'
|
|
account = fields.Many2One('account.account', 'Account', required=True,
|
|
select=True)
|
|
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
|
required=True, select=True)
|
|
debit = fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)),
|
|
required=True, depends=['currency_digits'])
|
|
credit = fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)),
|
|
required=True, depends=['currency_digits'])
|
|
balance = fields.Function(fields.Numeric('Balance',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_balance')
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(AccountDeferral, cls).__setup__()
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('deferral_uniq', Unique(t, t.account, t.fiscalyear),
|
|
'Deferral must be unique by account and fiscal year'),
|
|
]
|
|
cls._error_messages.update({
|
|
'write_deferral': 'You can not modify Account Deferral records',
|
|
})
|
|
|
|
def get_balance(self, name):
|
|
return self.debit - self.credit
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.account.currency_digits
|
|
|
|
def get_rec_name(self, name):
|
|
return '%s - %s' % (self.account.rec_name, self.fiscalyear.rec_name)
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
bool_op = 'AND'
|
|
else:
|
|
bool_op = 'OR'
|
|
return [bool_op,
|
|
('account.rec_name',) + tuple(clause[1:]),
|
|
('fiscalyear.rec_name',) + tuple(clause[1:]),
|
|
]
|
|
|
|
@classmethod
|
|
def write(cls, deferrals, values, *args):
|
|
cls.raise_user_error('write_deferral')
|
|
|
|
|
|
class OpenChartAccountStart(ModelView):
|
|
'Open Chart of Accounts'
|
|
__name__ = 'account.open_chart.start'
|
|
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
|
help='Leave empty for all open fiscal year')
|
|
posted = fields.Boolean('Posted Moves', help='Show posted moves only')
|
|
|
|
@staticmethod
|
|
def default_posted():
|
|
return False
|
|
|
|
|
|
class OpenChartAccount(Wizard):
|
|
'Open Chart of Accounts'
|
|
__name__ = 'account.open_chart'
|
|
start = StateView('account.open_chart.start',
|
|
'account.open_chart_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Open', 'open_', 'tryton-ok', default=True),
|
|
])
|
|
open_ = StateAction('account.act_account_tree2')
|
|
|
|
def do_open_(self, action):
|
|
action['pyson_context'] = PYSONEncoder().encode({
|
|
'fiscalyear': (self.start.fiscalyear.id
|
|
if self.start.fiscalyear else None),
|
|
'posted': self.start.posted,
|
|
})
|
|
if self.start.fiscalyear:
|
|
action['name'] += ' - %s' % self.start.fiscalyear.rec_name
|
|
if self.start.posted:
|
|
action['name'] += '*'
|
|
return action, {}
|
|
|
|
def transition_open_(self):
|
|
return 'end'
|
|
|
|
|
|
class GeneralLedgerAccount(ModelSQL, ModelView):
|
|
'General Ledger Account'
|
|
__name__ = 'account.general_ledger.account'
|
|
|
|
# TODO reuse rec_name of Account
|
|
name = fields.Char('Name')
|
|
code = fields.Char('Code')
|
|
active = fields.Boolean('Active')
|
|
company = fields.Many2One('company.company', 'Company')
|
|
start_debit = fields.Function(fields.Numeric('Start Debit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
debit = fields.Function(fields.Numeric('Debit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_debit_credit')
|
|
end_debit = fields.Function(fields.Numeric('End Debit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
start_credit = fields.Function(fields.Numeric('Start Credit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
credit = fields.Function(fields.Numeric('Credit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_debit_credit')
|
|
end_credit = fields.Function(fields.Numeric('End Credit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
start_balance = fields.Function(fields.Numeric('Start Balance',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
end_balance = fields.Function(fields.Numeric('End Balance',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits']), 'get_account')
|
|
lines = fields.One2Many('account.general_ledger.line', 'account', 'Lines',
|
|
readonly=True)
|
|
general_ledger_balance = fields.Boolean('General Ledger Balance')
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(GeneralLedgerAccount, cls).__setup__()
|
|
cls._order.insert(0, ('code', 'ASC'))
|
|
cls._order.insert(1, ('name', 'ASC'))
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
pool = Pool()
|
|
context = Transaction().context
|
|
Account = pool.get('account.account')
|
|
account = Account.__table__()
|
|
columns = []
|
|
for fname, field in cls._fields.iteritems():
|
|
if not hasattr(field, 'set'):
|
|
columns.append(Column(account, fname).as_(fname))
|
|
return account.select(*columns,
|
|
where=(account.company == context.get('company'))
|
|
& (account.kind != 'view'))
|
|
|
|
@classmethod
|
|
def get_period_ids(cls, name):
|
|
pool = Pool()
|
|
Period = pool.get('account.period')
|
|
context = Transaction().context
|
|
|
|
period = None
|
|
if name.startswith('start_'):
|
|
period_ids = [0]
|
|
if context.get('start_period'):
|
|
period = Period(context['start_period'])
|
|
elif name.startswith('end_'):
|
|
period_ids = []
|
|
if context.get('end_period'):
|
|
period = Period(context['end_period'])
|
|
|
|
if period:
|
|
periods = Period.search([
|
|
('fiscalyear', '=', context.get('fiscalyear')),
|
|
('end_date', '<=', period.start_date),
|
|
])
|
|
if period.start_date == period.end_date:
|
|
periods.append(period)
|
|
if periods:
|
|
period_ids = [p.id for p in periods]
|
|
return period_ids
|
|
|
|
@classmethod
|
|
def get_account(cls, records, name):
|
|
pool = Pool()
|
|
Account = pool.get('account.account')
|
|
|
|
period_ids = cls.get_period_ids(name)
|
|
with Transaction().set_context(periods=period_ids):
|
|
accounts = Account.browse(records)
|
|
fname = name
|
|
for test in ['start_', 'end_']:
|
|
if name.startswith(test):
|
|
fname = name[len(test):]
|
|
break
|
|
return {a.id: getattr(a, fname) for a in accounts}
|
|
|
|
@classmethod
|
|
def get_debit_credit(cls, records, name):
|
|
pool = Pool()
|
|
Account = pool.get('account.account')
|
|
|
|
start_period_ids = cls.get_period_ids('start_%s' % name)
|
|
end_period_ids = cls.get_period_ids('end_%s' % name)
|
|
periods_ids = list(
|
|
set(end_period_ids).difference(set(start_period_ids)))
|
|
with Transaction().set_context(periods=periods_ids):
|
|
accounts = Account.browse(records)
|
|
return {a.id: getattr(a, name) for a in accounts}
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.company.currency.digits
|
|
|
|
def get_rec_name(self, name):
|
|
# TODO add start/end balance
|
|
return self.name
|
|
|
|
|
|
class GeneralLedgerAccountContext(ModelView):
|
|
'General Ledger Account Context'
|
|
__name__ = 'account.general_ledger.account.context'
|
|
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
|
required=True)
|
|
start_period = fields.Many2One('account.period', 'Start Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear')),
|
|
('start_date', '<=', (Eval('end_period'), 'start_date')),
|
|
], depends=['fiscalyear', 'end_period'])
|
|
end_period = fields.Many2One('account.period', 'End Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear')),
|
|
('start_date', '>=', (Eval('start_period'), 'start_date'))
|
|
],
|
|
depends=['fiscalyear', 'start_period'])
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
posted = fields.Boolean('Posted Move', help='Show only posted move')
|
|
|
|
@classmethod
|
|
def default_fiscalyear(cls):
|
|
pool = Pool()
|
|
FiscalYear = pool.get('account.fiscalyear')
|
|
context = Transaction().context
|
|
return context.get(
|
|
'fiscalyear',
|
|
FiscalYear.find(context.get('company'), exception=False))
|
|
|
|
@classmethod
|
|
def default_start_period(cls):
|
|
return Transaction().context.get('start_period')
|
|
|
|
@classmethod
|
|
def default_end_period(cls):
|
|
return Transaction().context.get('end_period')
|
|
|
|
@classmethod
|
|
def default_company(cls):
|
|
return Transaction().context.get('company')
|
|
|
|
@classmethod
|
|
def default_posted(cls):
|
|
return Transaction().context.get('posted', False)
|
|
|
|
@fields.depends('fiscalyear', 'start_period', 'end_period')
|
|
def on_change_fiscalyear(self):
|
|
if (self.start_period
|
|
and self.start_period.fiscalyear != self.fiscalyear):
|
|
self.start_period = None
|
|
if (self.end_period
|
|
and self.end_period.fiscalyear != self.fiscalyear):
|
|
self.end_period = None
|
|
|
|
|
|
class GeneralLedgerLine(ModelSQL, ModelView):
|
|
'General Ledger Line'
|
|
__name__ = 'account.general_ledger.line'
|
|
|
|
move = fields.Many2One('account.move', 'Move')
|
|
date = fields.Date('Date')
|
|
account = fields.Many2One('account.general_ledger.account', 'Account')
|
|
party = fields.Many2One('party.party', 'Party',
|
|
states={
|
|
'invisible': ~Eval('party_required', False),
|
|
},
|
|
depends=['party_required'])
|
|
party_required = fields.Boolean('Party Required')
|
|
company = fields.Many2One('company.company', 'Company')
|
|
debit = fields.Numeric('Debit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
credit = fields.Numeric('Credit',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
balance = fields.Numeric('Balance',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
origin = fields.Reference('Origin', selection='get_origin')
|
|
description = fields.Char('Description')
|
|
move_description = fields.Char('Move Description')
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('posted', 'Posted'),
|
|
], 'State')
|
|
state_string = state.translated('state')
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(GeneralLedgerLine, cls).__setup__()
|
|
cls._order.insert(0, ('date', 'ASC'))
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
pool = Pool()
|
|
Line = pool.get('account.move.line')
|
|
Move = pool.get('account.move')
|
|
LedgerAccount = pool.get('account.general_ledger.account')
|
|
Account = pool.get('account.account')
|
|
context = Transaction().context
|
|
line = Line.__table__()
|
|
move = Move.__table__()
|
|
account = Account.__table__()
|
|
columns = []
|
|
for fname, field in cls._fields.iteritems():
|
|
if hasattr(field, 'set'):
|
|
continue
|
|
field_line = getattr(Line, fname, None)
|
|
if fname == 'balance':
|
|
# TODO replace the test by a generic one on backend
|
|
if backend.name() == 'postgresql':
|
|
w_columns = [line.account]
|
|
if context.get('party_cumulate', False):
|
|
w_columns.append(line.party)
|
|
column = Sum(line.debit - line.credit,
|
|
window=Window(w_columns,
|
|
order_by=[move.date.asc, line.id])).as_('balance')
|
|
else:
|
|
column = (line.debit - line.credit).as_('balance')
|
|
elif fname == 'move_description':
|
|
column = Column(move, 'description').as_(fname)
|
|
elif fname == 'party_required':
|
|
column = Column(account, 'party_required').as_(fname)
|
|
elif (not field_line
|
|
or fname == 'state'
|
|
or isinstance(field_line, fields.Function)):
|
|
column = Column(move, fname).as_(fname)
|
|
else:
|
|
column = Column(line, fname).as_(fname)
|
|
columns.append(column)
|
|
start_period_ids = set(LedgerAccount.get_period_ids('start_balance'))
|
|
end_period_ids = set(LedgerAccount.get_period_ids('end_balance'))
|
|
period_ids = list(end_period_ids.difference(start_period_ids))
|
|
with Transaction().set_context(periods=period_ids):
|
|
line_query, fiscalyear_ids = Line.query_get(line)
|
|
return line.join(move, condition=line.move == move.id
|
|
).join(account, condition=line.account == account.id
|
|
).select(*columns, where=line_query)
|
|
|
|
def get_party_required(self, name):
|
|
return self.account.party_required
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.company.currency.digits
|
|
|
|
@classmethod
|
|
def get_origin(cls):
|
|
Line = Pool().get('account.move.line')
|
|
return Line.get_origin()
|
|
|
|
|
|
class GeneralLedgerLineContext(GeneralLedgerAccountContext):
|
|
'General Ledger Line Context'
|
|
__name__ = 'account.general_ledger.line.context'
|
|
|
|
party_cumulate = fields.Boolean('Cumulate per Party')
|
|
|
|
@classmethod
|
|
def default_party_cumulate(cls):
|
|
return False
|
|
|
|
|
|
class GeneralLedger(Report):
|
|
__name__ = 'account.general_ledger'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Company = pool.get('company.company')
|
|
Fiscalyear = pool.get('account.fiscalyear')
|
|
Period = pool.get('account.period')
|
|
context = Transaction().context
|
|
|
|
report_context = super(GeneralLedger, cls).get_context(records, data)
|
|
|
|
report_context['company'] = Company(context['company'])
|
|
report_context['fiscalyear'] = Fiscalyear(context['fiscalyear'])
|
|
|
|
for period in ['start_period', 'end_period']:
|
|
if context.get(period):
|
|
report_context[period] = Period(context[period])
|
|
else:
|
|
report_context[period] = None
|
|
|
|
report_context['accounts'] = records
|
|
return report_context
|
|
|
|
|
|
class TrialBalance(Report):
|
|
__name__ = 'account.trial_balance'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Company = pool.get('company.company')
|
|
Fiscalyear = pool.get('account.fiscalyear')
|
|
Period = pool.get('account.period')
|
|
context = Transaction().context
|
|
|
|
report_context = super(TrialBalance, cls).get_context(records, data)
|
|
|
|
report_context['company'] = Company(context['company'])
|
|
report_context['fiscalyear'] = Fiscalyear(context['fiscalyear'])
|
|
|
|
for period in ['start_period', 'end_period']:
|
|
if context.get(period):
|
|
report_context[period] = Period(context[period])
|
|
else:
|
|
report_context[period] = None
|
|
|
|
report_context['accounts'] = records
|
|
report_context['sum'] = cls.sum
|
|
return report_context
|
|
|
|
@classmethod
|
|
def sum(cls, accounts, field):
|
|
return sum((getattr(a, field) for a in accounts), Decimal('0'))
|
|
|
|
|
|
class BalanceSheetContext(ModelView):
|
|
'Balance Sheet Context'
|
|
__name__ = 'account.balance_sheet.context'
|
|
date = fields.Date('Date', required=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
posted = fields.Boolean('Posted Move', help='Show only posted move')
|
|
comparison = fields.Boolean('Comparison')
|
|
date_cmp = fields.Date('Date', states={
|
|
'required': Eval('comparison', False),
|
|
'invisible': ~Eval('comparison', False),
|
|
},
|
|
depends=['comparison'])
|
|
|
|
@staticmethod
|
|
def default_date():
|
|
Date_ = Pool().get('ir.date')
|
|
return Date_.today()
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
@staticmethod
|
|
def default_posted():
|
|
return False
|
|
|
|
@classmethod
|
|
def default_comparison(cls):
|
|
return False
|
|
|
|
@fields.depends('comparison', 'date', 'date_cmp')
|
|
def on_change_comparison(self):
|
|
self.date_cmp = None
|
|
if self.comparison and self.date:
|
|
self.date_cmp = self.date - relativedelta(years=1)
|
|
|
|
@classmethod
|
|
def view_attributes(cls):
|
|
return [
|
|
('/form/separator[@id="comparison"]', 'states', {
|
|
'invisible': ~Eval('comparison', False),
|
|
}),
|
|
]
|
|
|
|
|
|
class IncomeStatementContext(ModelView):
|
|
'Income Statement Context'
|
|
__name__ = 'account.income_statement.context'
|
|
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
|
required=True)
|
|
start_period = fields.Many2One('account.period', 'Start Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear')),
|
|
('start_date', '<=', (Eval('end_period'), 'start_date'))
|
|
],
|
|
depends=['end_period', 'fiscalyear'])
|
|
end_period = fields.Many2One('account.period', 'End Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear')),
|
|
('start_date', '>=', (Eval('start_period'), 'start_date')),
|
|
],
|
|
depends=['start_period', 'fiscalyear'])
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
posted = fields.Boolean('Posted Move', help='Show only posted move')
|
|
comparison = fields.Boolean('Comparison')
|
|
fiscalyear_cmp = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
|
states={
|
|
'required': Eval('comparison', False),
|
|
'invisible': ~Eval('comparison', False),
|
|
})
|
|
start_period_cmp = fields.Many2One('account.period', 'Start Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear_cmp')),
|
|
('start_date', '<=', (Eval('end_period_cmp'), 'start_date'))
|
|
],
|
|
states={
|
|
'invisible': ~Eval('comparison', False),
|
|
},
|
|
depends=['end_period_cmp', 'fiscalyear_cmp'])
|
|
end_period_cmp = fields.Many2One('account.period', 'End Period',
|
|
domain=[
|
|
('fiscalyear', '=', Eval('fiscalyear_cmp')),
|
|
('start_date', '>=', (Eval('start_period_cmp'), 'start_date')),
|
|
],
|
|
states={
|
|
'invisible': ~Eval('comparison', False),
|
|
},
|
|
depends=['start_period_cmp', 'fiscalyear_cmp'])
|
|
|
|
@staticmethod
|
|
def default_fiscalyear():
|
|
FiscalYear = Pool().get('account.fiscalyear')
|
|
return FiscalYear.find(
|
|
Transaction().context.get('company'), exception=False)
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
@staticmethod
|
|
def default_posted():
|
|
return False
|
|
|
|
@classmethod
|
|
def default_comparison(cls):
|
|
return False
|
|
|
|
@fields.depends('fiscalyear')
|
|
def on_change_fiscalyear(self):
|
|
self.start_period = None
|
|
self.end_period = None
|
|
|
|
@classmethod
|
|
def view_attributes(cls):
|
|
return [
|
|
('/form/separator[@id="comparison"]', 'states', {
|
|
'invisible': ~Eval('comparison', False),
|
|
}),
|
|
]
|
|
|
|
|
|
class AgedBalanceContext(ModelView):
|
|
'Aged Balance Context'
|
|
__name__ = 'account.aged_balance.context'
|
|
type = fields.Selection([
|
|
('customer', 'Customers'),
|
|
('supplier', 'Suppliers'),
|
|
('customer_supplier', 'Customers and Suppliers'),
|
|
],
|
|
"Type", required=True)
|
|
date = fields.Date('Date', required=True)
|
|
term1 = fields.Integer("First Term", required=True)
|
|
term2 = fields.Integer("Second Term", required=True,
|
|
domain=[
|
|
('term2', '>', Eval('term1', 0)),
|
|
],
|
|
depends=['term1'])
|
|
term3 = fields.Integer("Third Term", required=True,
|
|
domain=[
|
|
('term3', '>', Eval('term2', 0)),
|
|
],
|
|
depends=['term2'])
|
|
unit = fields.Selection([
|
|
('day', 'Days'),
|
|
('month', 'Months'),
|
|
], "Unit", required=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
posted = fields.Boolean('Posted Move', help='Show only posted move')
|
|
|
|
@classmethod
|
|
def default_type(cls):
|
|
return 'customer'
|
|
|
|
@classmethod
|
|
def default_posted(cls):
|
|
return False
|
|
|
|
@classmethod
|
|
def default_date(cls):
|
|
return Pool().get('ir.date').today()
|
|
|
|
@staticmethod
|
|
def default_term1():
|
|
return 30
|
|
|
|
@staticmethod
|
|
def default_term2():
|
|
return 60
|
|
|
|
@staticmethod
|
|
def default_term3():
|
|
return 90
|
|
|
|
@staticmethod
|
|
def default_unit():
|
|
return 'day'
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
class AgedBalance(ModelSQL, ModelView):
|
|
'Aged Balance'
|
|
__name__ = 'account.aged_balance'
|
|
|
|
party = fields.Many2One('party.party', 'Party')
|
|
company = fields.Many2One('company.company', 'Company')
|
|
term0 = fields.Numeric('Now',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
term1 = fields.Numeric('First Term',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
term2 = fields.Numeric('Second Term',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
term3 = fields.Numeric('Third Term',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
balance = fields.Numeric('Balance',
|
|
digits=(16, Eval('currency_digits', 2)),
|
|
depends=['currency_digits'])
|
|
currency_digits = fields.Function(fields.Integer('Currency Digits'),
|
|
'get_currency_digits')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(AgedBalance, cls).__setup__()
|
|
cls._order.insert(0, ('party', 'ASC'))
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
pool = Pool()
|
|
context = Transaction().context
|
|
MoveLine = pool.get('account.move.line')
|
|
Move = pool.get('account.move')
|
|
Reconciliation = pool.get('account.move.reconciliation')
|
|
Account = pool.get('account.account')
|
|
|
|
line = MoveLine.__table__()
|
|
move = Move.__table__()
|
|
reconciliation = Reconciliation.__table__()
|
|
account = Account.__table__()
|
|
|
|
company_id = context.get('company')
|
|
date = context.get('date')
|
|
with Transaction().set_context(date=None):
|
|
line_query, _ = MoveLine.query_get(line)
|
|
kind = cls.get_kind()
|
|
columns = [
|
|
line.party.as_('id'),
|
|
Literal(0).as_('create_uid'),
|
|
Max(line.create_date).as_('create_date'),
|
|
Literal(0).as_('write_uid'),
|
|
Max(line.write_date).as_('write_date'),
|
|
line.party.as_('party'),
|
|
move.company.as_('company'),
|
|
(Sum(line.debit) - Sum(line.credit)).as_('balance'),
|
|
]
|
|
|
|
terms = cls.get_terms()
|
|
factor = cls.get_unit_factor()
|
|
term_values = sorted(terms.values(), key=lambda x: x or 0)
|
|
|
|
for name, value in terms.iteritems():
|
|
if value is None or factor is None or date is None:
|
|
columns.append(Literal(None).as_(name))
|
|
continue
|
|
cond = line.maturity_date <= (date - value * factor)
|
|
idx = term_values.index(value)
|
|
if idx + 1 < len(terms):
|
|
cond &= line.maturity_date > (
|
|
date - term_values[idx + 1] * factor)
|
|
else:
|
|
cond |= line.maturity_date == Null
|
|
columns.append(
|
|
Sum(Case((cond, line.debit - line.credit), else_=0)).as_(name))
|
|
|
|
return line.join(move, condition=line.move == move.id
|
|
).join(account, condition=line.account == account.id
|
|
).join(reconciliation, 'LEFT',
|
|
condition=reconciliation.id == line.reconciliation
|
|
).select(*columns,
|
|
where=(line.party != Null)
|
|
& (account.active == True)
|
|
& account.kind.in_(kind)
|
|
& ((line.reconciliation == Null)
|
|
| (reconciliation.date > date))
|
|
& (move.date <= date)
|
|
& (account.company == company_id)
|
|
& line_query,
|
|
group_by=(line.party, move.company))
|
|
|
|
@classmethod
|
|
def get_terms(cls):
|
|
context = Transaction().context
|
|
return {
|
|
'term0': 0,
|
|
'term1': context.get('term1'),
|
|
'term2': context.get('term2'),
|
|
'term3': context.get('term3'),
|
|
}
|
|
|
|
@classmethod
|
|
def get_unit_factor(cls):
|
|
context = Transaction().context
|
|
unit = context.get('unit', 'day')
|
|
if unit == 'month':
|
|
return datetime.timedelta(days=30)
|
|
elif unit == 'day':
|
|
return datetime.timedelta(days=1)
|
|
|
|
@classmethod
|
|
def get_kind(cls):
|
|
context = Transaction().context
|
|
type_ = context.get('type', 'customer')
|
|
if type_ == 'customer_supplier':
|
|
return ['payable', 'receivable']
|
|
elif type_ == 'supplier':
|
|
return ['payable']
|
|
elif type_ == 'customer':
|
|
return ['receivable']
|
|
else:
|
|
return []
|
|
|
|
def get_currency_digits(self, name):
|
|
return self.company.currency.digits
|
|
|
|
|
|
class AgedBalanceReport(Report):
|
|
__name__ = 'account.aged_balance'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Company = pool.get('company.company')
|
|
Context = pool.get('account.aged_balance.context')
|
|
AgedBalance = pool.get('account.aged_balance')
|
|
context = Transaction().context
|
|
|
|
report_context = super(AgedBalanceReport, cls).get_context(
|
|
records, data)
|
|
|
|
context_fields = Context.fields_get(['type', 'unit'])
|
|
|
|
report_context['company'] = Company(context['company'])
|
|
report_context['date'] = context['date']
|
|
report_context['type'] = dict(
|
|
context_fields['type']['selection'])[context['type']]
|
|
report_context['unit'] = dict(
|
|
context_fields['unit']['selection'])[context['unit']]
|
|
report_context.update(AgedBalance.get_terms())
|
|
report_context['sum'] = cls.sum
|
|
return report_context
|
|
|
|
@classmethod
|
|
def sum(cls, records, field):
|
|
return sum((getattr(r, field) for r in records), Decimal('0'))
|
|
|
|
|
|
class CreateChartStart(ModelView):
|
|
'Create Chart'
|
|
__name__ = 'account.create_chart.start'
|
|
|
|
|
|
class CreateChartAccount(ModelView):
|
|
'Create Chart'
|
|
__name__ = 'account.create_chart.account'
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
account_template = fields.Many2One('account.account.template',
|
|
'Account Template', required=True, domain=[('parent', '=', None)])
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
class CreateChartProperties(ModelView):
|
|
'Create Chart'
|
|
__name__ = 'account.create_chart.properties'
|
|
company = fields.Many2One('company.company', 'Company')
|
|
account_receivable = fields.Many2One('account.account',
|
|
'Default Receivable Account',
|
|
domain=[
|
|
('kind', '=', 'receivable'),
|
|
('company', '=', Eval('company')),
|
|
],
|
|
depends=['company'])
|
|
account_payable = fields.Many2One('account.account',
|
|
'Default Payable Account',
|
|
domain=[
|
|
('kind', '=', 'payable'),
|
|
('company', '=', Eval('company')),
|
|
],
|
|
depends=['company'])
|
|
|
|
|
|
class CreateChart(Wizard):
|
|
'Create Chart'
|
|
__name__ = 'account.create_chart'
|
|
start = StateView('account.create_chart.start',
|
|
'account.create_chart_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('OK', 'account', 'tryton-ok', default=True),
|
|
])
|
|
account = StateView('account.create_chart.account',
|
|
'account.create_chart_account_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Create', 'create_account', 'tryton-ok', default=True),
|
|
])
|
|
create_account = StateTransition()
|
|
properties = StateView('account.create_chart.properties',
|
|
'account.create_chart_properties_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Create', 'create_properties', 'tryton-ok', default=True),
|
|
])
|
|
create_properties = StateTransition()
|
|
|
|
def transition_create_account(self):
|
|
pool = Pool()
|
|
TaxCodeTemplate = pool.get('account.tax.code.template')
|
|
TaxTemplate = pool.get('account.tax.template')
|
|
TaxRuleTemplate = pool.get('account.tax.rule.template')
|
|
TaxRuleLineTemplate = \
|
|
pool.get('account.tax.rule.line.template')
|
|
Config = pool.get('ir.configuration')
|
|
|
|
with Transaction().set_context(language=Config.get_language(),
|
|
company=self.account.company.id):
|
|
account_template = self.account.account_template
|
|
company = self.account.company
|
|
|
|
# Create account types
|
|
template2type = {}
|
|
account_template.type.create_type(
|
|
company.id,
|
|
template2type=template2type)
|
|
|
|
# Create accounts
|
|
template2account = {}
|
|
account_template.create_account(
|
|
company.id,
|
|
template2account=template2account,
|
|
template2type=template2type)
|
|
|
|
# Create tax codes
|
|
template2tax_code = {}
|
|
TaxCodeTemplate.create_tax_code(
|
|
account_template.id, company.id,
|
|
template2tax_code=template2tax_code)
|
|
|
|
# Create taxes
|
|
template2tax = {}
|
|
TaxTemplate.create_tax(
|
|
account_template.id, company.id,
|
|
template2tax_code=template2tax_code,
|
|
template2account=template2account,
|
|
template2tax=template2tax)
|
|
|
|
# Update taxes on accounts
|
|
account_template.update_account_taxes(template2account,
|
|
template2tax)
|
|
|
|
# Create tax rules
|
|
template2rule = {}
|
|
TaxRuleTemplate.create_rule(
|
|
account_template.id, company.id,
|
|
template2rule=template2rule)
|
|
|
|
# Create tax rule lines
|
|
template2rule_line = {}
|
|
TaxRuleLineTemplate.create_rule_line(
|
|
account_template.id, template2tax, template2rule,
|
|
template2rule_line=template2rule_line)
|
|
return 'properties'
|
|
|
|
def default_properties(self, fields):
|
|
return {
|
|
'company': self.account.company.id,
|
|
}
|
|
|
|
def transition_create_properties(self):
|
|
pool = Pool()
|
|
Property = pool.get('ir.property')
|
|
ModelField = pool.get('ir.model.field')
|
|
|
|
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,
|
|
}])
|
|
return 'end'
|
|
|
|
|
|
class UpdateChartStart(ModelView):
|
|
'Update Chart'
|
|
__name__ = 'account.update_chart.start'
|
|
account = fields.Many2One('account.account', 'Root Account',
|
|
required=True, domain=[('parent', '=', None)])
|
|
|
|
|
|
class UpdateChartSucceed(ModelView):
|
|
'Update Chart'
|
|
__name__ = 'account.update_chart.succeed'
|
|
|
|
|
|
class UpdateChart(Wizard):
|
|
'Update Chart'
|
|
__name__ = 'account.update_chart'
|
|
start = StateView('account.update_chart.start',
|
|
'account.update_chart_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Update', 'update', 'tryton-ok', default=True),
|
|
])
|
|
update = StateTransition()
|
|
succeed = StateView('account.update_chart.succeed',
|
|
'account.update_chart_succeed_view_form', [
|
|
Button('OK', 'end', 'tryton-ok', default=True),
|
|
])
|
|
|
|
@inactive_records
|
|
def transition_update(self):
|
|
pool = Pool()
|
|
TaxCode = pool.get('account.tax.code')
|
|
TaxCodeTemplate = pool.get('account.tax.code.template')
|
|
Tax = pool.get('account.tax')
|
|
TaxTemplate = pool.get('account.tax.template')
|
|
TaxRule = pool.get('account.tax.rule')
|
|
TaxRuleTemplate = pool.get('account.tax.rule.template')
|
|
TaxRuleLine = pool.get('account.tax.rule.line')
|
|
TaxRuleLineTemplate = \
|
|
pool.get('account.tax.rule.line.template')
|
|
|
|
account = self.start.account
|
|
company = account.company
|
|
|
|
# Update account types
|
|
template2type = {}
|
|
account.type.update_type(template2type=template2type)
|
|
# Create missing account types
|
|
if account.type.template:
|
|
account.type.template.create_type(
|
|
company.id,
|
|
template2type=template2type)
|
|
|
|
# Update accounts
|
|
template2account = {}
|
|
account.update_account(template2account=template2account,
|
|
template2type=template2type)
|
|
# Create missing accounts
|
|
if account.template:
|
|
account.template.create_account(
|
|
company.id,
|
|
template2account=template2account,
|
|
template2type=template2type)
|
|
|
|
# Update tax codes
|
|
template2tax_code = {}
|
|
TaxCode.update_tax_code(
|
|
company.id,
|
|
template2tax_code=template2tax_code)
|
|
# Create missing tax codes
|
|
if account.template:
|
|
TaxCodeTemplate.create_tax_code(
|
|
account.template.id, company.id,
|
|
template2tax_code=template2tax_code)
|
|
|
|
# Update taxes
|
|
template2tax = {}
|
|
Tax.update_tax(
|
|
company.id,
|
|
template2tax_code=template2tax_code,
|
|
template2account=template2account,
|
|
template2tax=template2tax)
|
|
# Create missing taxes
|
|
if account.template:
|
|
TaxTemplate.create_tax(
|
|
account.template.id, account.company.id,
|
|
template2tax_code=template2tax_code,
|
|
template2account=template2account,
|
|
template2tax=template2tax)
|
|
|
|
# Update taxes on accounts
|
|
account.update_account_taxes(template2account, template2tax)
|
|
|
|
# Update tax rules
|
|
template2rule = {}
|
|
TaxRule.update_rule(company.id, template2rule=template2rule)
|
|
# Create missing tax rules
|
|
if account.template:
|
|
TaxRuleTemplate.create_rule(
|
|
account.template.id, account.company.id,
|
|
template2rule=template2rule)
|
|
|
|
# Update tax rule lines
|
|
template2rule_line = {}
|
|
TaxRuleLine.update_rule_line(
|
|
company.id, template2tax, template2rule,
|
|
template2rule_line=template2rule_line)
|
|
# Create missing tax rule lines
|
|
if account.template:
|
|
TaxRuleLineTemplate.create_rule_line(
|
|
account.template.id, template2tax, template2rule,
|
|
template2rule_line=template2rule_line)
|
|
return 'succeed'
|