Files
tryton/tax.py
T

1583 lines
60 KiB
Python
Raw Normal View History

2014-12-02 12:54:29 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2014-01-31 22:30:34 +01:00
import datetime
2014-12-19 19:42:37 +01:00
from collections import namedtuple
2010-08-19 22:02:00 +02:00
from decimal import Decimal
from sql import Null
2013-09-06 22:33:47 +02:00
from sql.aggregate import Sum
2014-05-09 23:16:44 +02:00
from trytond.model import ModelView, ModelSQL, MatchMixin, fields
2012-01-02 17:54:33 +01:00
from trytond.wizard import Wizard, StateView, StateAction, Button
2013-09-06 22:33:47 +02:00
from trytond import backend
2011-08-21 11:20:29 +02:00
from trytond.pyson import Eval, If, Bool, PYSONEncoder
2010-08-19 22:02:00 +02:00
from trytond.transaction import Transaction
2012-09-24 22:17:29 +02:00
from trytond.pool import Pool, PoolMeta
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
__all__ = ['TaxGroup', 'TaxCodeTemplate', 'TaxCode',
'OpenChartTaxCodeStart', 'OpenChartTaxCode',
'TaxTemplate', 'Tax', 'TaxLine', 'TaxRuleTemplate', 'TaxRule',
'TaxRuleLineTemplate', 'TaxRuleLine',
'OpenTaxCode',
'AccountTemplateTaxTemplate', 'AccountTemplate2', 'AccountTax', 'Account2']
__metaclass__ = PoolMeta
2008-03-23 23:05:12 +01:00
KINDS = [
('sale', 'Sale'),
('purchase', 'Purchase'),
('both', 'Both'),
]
2012-09-24 22:17:29 +02:00
class TaxGroup(ModelSQL, ModelView):
2008-03-23 23:05:12 +01:00
'Tax Group'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.group'
2008-03-23 23:05:12 +01:00
name = fields.Char('Name', size=None, required=True, translate=True)
code = fields.Char('Code', size=None, required=True)
kind = fields.Selection(KINDS, 'Kind', required=True)
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:47 +02:00
TableHandler = backend.get('TableHandler')
2012-09-24 22:17:29 +02:00
super(TaxGroup, cls).__register__(module_name)
2010-08-19 22:02:00 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:29 +02:00
table = TableHandler(cursor, cls, module_name)
# Migration from 1.4 drop code_uniq constraint
table.drop_constraint('code_uniq')
2008-03-23 23:05:12 +01:00
@staticmethod
def default_kind():
return 'both'
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
class TaxCodeTemplate(ModelSQL, ModelView):
2008-07-21 13:59:50 +02:00
'Tax Code Template'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.code.template'
name = fields.Char('Name', required=True, translate=True)
2008-07-21 13:59:50 +02:00
code = fields.Char('Code')
parent = fields.Many2One('account.tax.code.template', 'Parent')
2009-03-19 19:51:42 +01:00
childs = fields.One2Many('account.tax.code.template', 'parent', 'Children')
2008-07-21 13:59:50 +02:00
account = fields.Many2One('account.account.template', 'Account Template',
2012-03-27 10:43:03 +02:00
domain=[('parent', '=', None)], required=True)
description = fields.Text('Description', translate=True)
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(TaxCodeTemplate, cls).__setup__()
cls._order.insert(0, ('code', 'ASC'))
cls._order.insert(0, ('account', 'ASC'))
2008-07-21 13:59:50 +02:00
@classmethod
def validate(cls, templates):
2013-03-10 19:01:35 +01:00
super(TaxCodeTemplate, cls).validate(templates)
cls.check_recursion(templates)
2012-09-24 22:17:29 +02:00
def _get_tax_code_value(self, code=None):
2008-07-21 13:59:50 +02:00
'''
Set values for tax code creation.
'''
res = {}
2012-09-24 22:17:29 +02:00
if not code or code.name != self.name:
res['name'] = self.name
if not code or code.code != self.code:
res['code'] = self.code
if not code or code.description != self.description:
res['description'] = self.description
if not code or code.template.id != self.id:
2012-10-17 17:31:05 +02:00
res['template'] = self.id
2008-07-21 13:59:50 +02:00
return res
2012-09-24 22:17:29 +02:00
def create_tax_code(self, company_id, template2tax_code=None,
2012-03-27 10:43:03 +02:00
parent_id=None):
2008-07-21 13:59:50 +02:00
'''
Create recursively tax codes based on template.
2012-09-24 22:17:29 +02:00
template2tax_code is a dictionary with tax code template id as key and
tax code id as value, used to convert template id into tax code. The
dictionary is filled with new tax codes.
Return the id of the tax code created
2008-07-21 13:59:50 +02:00
'''
2012-09-24 22:17:29 +02:00
pool = Pool()
TaxCode = pool.get('account.tax.code')
Lang = pool.get('ir.lang')
2012-11-23 18:00:16 +01:00
Config = pool.get('ir.configuration')
2008-07-21 13:59:50 +02:00
if template2tax_code is None:
template2tax_code = {}
2012-09-24 22:17:29 +02:00
if self.id not in template2tax_code:
vals = self._get_tax_code_value()
2008-07-21 13:59:50 +02:00
vals['company'] = company_id
vals['parent'] = parent_id
2013-01-06 22:18:13 +01:00
new_tax_code, = TaxCode.create([vals])
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
template = self.__class__(self.id)
data = {}
for field_name, field in template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(template, field_name) !=
prev_data[field_name])):
data[field_name] = getattr(template, field_name)
if data:
TaxCode.write([new_tax_code], data)
template2tax_code[self.id] = new_tax_code.id
new_id = template2tax_code[self.id]
2008-07-21 13:59:50 +02:00
new_childs = []
for child in self.childs:
2012-09-24 22:17:29 +02:00
new_childs.append(child.create_tax_code(company_id,
template2tax_code=template2tax_code, parent_id=new_id))
2008-07-21 13:59:50 +02:00
return new_id
2012-09-24 22:17:29 +02:00
class TaxCode(ModelSQL, ModelView):
2008-03-23 23:05:12 +01:00
'Tax Code'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.code'
2012-03-05 10:52:56 +01:00
name = fields.Char('Name', size=None, required=True, select=True,
translate=True)
2012-03-05 10:52:56 +01:00
code = fields.Char('Code', size=None, select=True)
active = fields.Boolean('Active', select=True)
2012-03-14 09:23:04 +01:00
company = fields.Many2One('company.company', 'Company', required=True,
select=True)
2012-03-05 10:52:56 +01:00
parent = fields.Many2One('account.tax.code', 'Parent', select=True,
2010-01-20 10:56:33 +01:00
domain=[('company', '=', Eval('company', 0))], depends=['company'])
2009-03-19 19:51:42 +01:00
childs = fields.One2Many('account.tax.code', 'parent', 'Children',
2010-01-20 10:56:33 +01:00
domain=[('company', '=', Eval('company', 0))], depends=['company'])
2014-01-26 14:28:46 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'on_change_with_currency_digits')
2010-03-11 16:45:27 +01:00
sum = fields.Function(fields.Numeric('Sum',
digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
'get_sum')
template = fields.Many2One('account.tax.code.template', 'Template')
description = fields.Text('Description', translate=True)
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(TaxCode, cls).__setup__()
cls._order.insert(0, ('code', 'ASC'))
2008-03-23 23:05:12 +01:00
@classmethod
def validate(cls, codes):
super(TaxCode, cls).validate(codes)
cls.check_recursion(codes)
2012-09-24 22:17:29 +02:00
@staticmethod
def default_active():
2008-03-23 23:05:12 +01:00
return True
2012-09-24 22:17:29 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:03 +02:00
return Transaction().context.get('company')
2008-03-23 23:05:12 +01:00
2014-01-26 14:28:46 +01:00
@fields.depends('company')
2012-09-24 22:17:29 +02:00
def on_change_with_currency_digits(self, name=None):
if self.company:
return self.company.currency.digits
2008-07-29 11:40:24 +02:00
return 2
2012-09-24 22:17:29 +02:00
@classmethod
def get_sum(cls, codes, name):
2010-08-19 22:02:00 +02:00
cursor = Transaction().cursor
2008-03-23 23:05:12 +01:00
res = {}
2012-09-24 22:17:29 +02:00
pool = Pool()
MoveLine = pool.get('account.move.line')
2013-09-06 22:33:47 +02:00
TaxLine = pool.get('account.tax.line')
code = cls.__table__()
tax_line = TaxLine.__table__()
move_line = MoveLine.__table__()
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
childs = cls.search([
('parent', 'child_of', [c.id for c in codes]),
])
all_codes = list(set(codes) | set(childs))
2013-09-06 22:33:47 +02:00
line_query, _ = MoveLine.query_get(move_line)
cursor.execute(*code.join(tax_line, condition=tax_line.code == code.id
).join(move_line, condition=tax_line.move_line == move_line.id
).select(code.id, Sum(tax_line.amount),
where=code.id.in_([c.id for c in all_codes])
& code.active & line_query,
group_by=code.id))
2008-03-23 23:05:12 +01:00
code_sum = {}
for code_id, sum in cursor.fetchall():
2009-09-22 16:08:11 +02:00
# SQLite uses float for SUM
if not isinstance(sum, Decimal):
sum = Decimal(str(sum))
2008-03-23 23:05:12 +01:00
code_sum[code_id] = sum
2012-09-24 22:17:29 +02:00
for code in codes:
2008-03-23 23:05:12 +01:00
res.setdefault(code.id, Decimal('0.0'))
2012-09-24 22:17:29 +02:00
childs = cls.search([
('parent', 'child_of', [code.id]),
])
for child in childs:
res[code.id] += code.company.currency.round(
code_sum.get(child.id, Decimal('0.0')))
res[code.id] = code.company.currency.round(res[code.id])
2008-03-23 23:05:12 +01:00
return res
2012-09-24 22:17:29 +02:00
def get_rec_name(self, name):
if self.code:
return self.code + ' - ' + self.name
else:
return self.name
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def search_rec_name(cls, name, clause):
2014-01-31 14:06:56 +01:00
return ['OR',
('code',) + tuple(clause[1:]),
(cls._rec_name,) + tuple(clause[1:]),
]
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def delete(cls, codes):
codes = cls.search([
('parent', 'child_of', [c.id for c in codes]),
])
super(TaxCode, cls).delete(codes)
2008-11-14 22:00:15 +01:00
2012-09-24 22:17:29 +02:00
def update_tax_code(self, template2tax_code=None):
'''
Update recursively tax code based on template.
2012-09-24 22:17:29 +02:00
template2tax_code is a dictionary with tax code template id as key and
tax code id as value, used to convert template id into tax code. The
dictionary is filled with new tax codes
'''
2012-11-23 18:00:16 +01:00
pool = Pool()
Lang = pool.get('ir.lang')
Config = pool.get('ir.configuration')
if template2tax_code is None:
template2tax_code = {}
2012-09-24 22:17:29 +02:00
if self.template:
vals = self.template._get_tax_code_value(code=self)
if vals:
2012-09-24 22:17:29 +02:00
self.write([self], vals)
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self.template._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self.template, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
code = self.__class__(self.id)
data = {}
for field_name, field in code.template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(code.template, field_name) !=
prev_data[field_name])):
data[field_name] = getattr(code.template,
field_name)
if data:
self.write([code], data)
template2tax_code[self.template.id] = self.id
for child in self.childs:
2012-09-24 22:17:29 +02:00
child.update_tax_code(template2tax_code=template2tax_code)
2008-03-23 23:05:12 +01:00
2012-01-02 17:54:33 +01:00
class OpenChartTaxCodeStart(ModelView):
'Open Chart of Tax Codes'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.code.open_chart.start'
2008-03-23 23:05:12 +01:00
method = fields.Selection([
('fiscalyear', 'By Fiscal Year'),
('periods', 'By Periods'),
], 'Method', required=True)
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
2011-08-21 11:20:29 +02:00
help='Leave empty for all open fiscal year',
states={
'invisible': Eval('method') != 'fiscalyear',
}, depends=['method'])
2009-03-05 14:51:19 +01:00
periods = fields.Many2Many('account.period', None, None, 'Periods',
2011-08-21 11:20:29 +02:00
help='Leave empty for all periods of all open fiscal year',
states={
'invisible': Eval('method') != 'periods',
}, depends=['method'])
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_method():
2008-03-23 23:05:12 +01:00
return 'periods'
2012-01-02 17:54:33 +01:00
class OpenChartTaxCode(Wizard):
'Open Chart of Tax Codes'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.code.open_chart'
2012-01-02 17:54:33 +01:00
start = StateView('account.tax.code.open_chart.start',
'account.tax_code_open_chart_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Open', 'open_', 'tryton-ok', default=True),
])
open_ = StateAction('account.act_tax_code_tree2')
2012-09-24 22:17:29 +02:00
def do_open_(self, action):
if self.start.method == 'fiscalyear':
2012-01-02 17:54:33 +01:00
action['pyson_context'] = PYSONEncoder().encode({
'fiscalyear': (self.start.fiscalyear.id
if self.start.fiscalyear else None),
2012-01-02 17:54:33 +01:00
})
2012-09-27 11:07:08 +02:00
if self.start.fiscalyear:
action['name'] += ' - %s' % self.start.fiscalyear.rec_name
2008-03-23 23:05:12 +01:00
else:
2012-01-02 17:54:33 +01:00
action['pyson_context'] = PYSONEncoder().encode({
2012-09-24 22:17:29 +02:00
'periods': [x.id for x in self.start.periods],
2012-01-02 17:54:33 +01:00
})
2012-09-27 11:07:08 +02:00
period_str = ', '.join(p.rec_name for p in self.start.periods)
if period_str:
action['name'] += ' (%s)' % period_str
2012-01-02 17:54:33 +01:00
return action, {}
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
def transition_open_(self):
2012-01-02 17:54:33 +01:00
return 'end'
2008-03-23 23:05:12 +01:00
2009-02-28 16:57:10 +01:00
class TaxTemplate(ModelSQL, ModelView):
2008-09-05 00:09:57 +02:00
'Account Tax Template'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.template'
2008-07-21 13:59:50 +02:00
name = fields.Char('Name', required=True, translate=True)
description = fields.Char('Description', required=True, translate=True)
2009-04-06 10:32:43 +02:00
group = fields.Many2One('account.tax.group', 'Group')
2013-09-06 22:33:47 +02:00
sequence = fields.Integer('Sequence')
2014-01-31 22:30:34 +01:00
start_date = fields.Date('Starting Date')
end_date = fields.Date('Ending Date')
2013-06-26 22:59:55 +02:00
amount = fields.Numeric('Amount', digits=(16, 8),
states={
'required': Eval('type') == 'fixed',
'invisible': Eval('type') != 'fixed',
},
depends=['type'])
rate = fields.Numeric('Rate', digits=(14, 10),
states={
'required': Eval('type') == 'percentage',
'invisible': Eval('type') != 'percentage',
}, depends=['type'])
2008-07-21 13:59:50 +02:00
type = fields.Selection([
('percentage', 'Percentage'),
('fixed', 'Fixed'),
('none', 'None'),
], 'Type', required=True)
parent = fields.Many2One('account.tax.template', 'Parent')
2009-03-19 19:51:42 +01:00
childs = fields.One2Many('account.tax.template', 'parent', 'Children')
2008-07-21 13:59:50 +02:00
invoice_account = fields.Many2One('account.account.template',
'Invoice Account')
2008-10-03 17:11:53 +02:00
credit_note_account = fields.Many2One('account.account.template',
'Credit Note Account')
2008-07-21 13:59:50 +02:00
invoice_base_code = fields.Many2One('account.tax.code.template',
'Invoice Base Code')
invoice_base_sign = fields.Numeric('Invoice Base Sign', digits=(2, 0))
invoice_tax_code = fields.Many2One('account.tax.code.template',
'Invoice Tax Code')
invoice_tax_sign = fields.Numeric('Invoice Tax Sign', digits=(2, 0))
2008-10-03 17:11:53 +02:00
credit_note_base_code = fields.Many2One('account.tax.code.template',
'Credit Note Base Code')
2012-04-03 20:06:15 +02:00
credit_note_base_sign = fields.Numeric('Credit Note Base Sign',
digits=(2, 0))
2008-10-03 17:11:53 +02:00
credit_note_tax_code = fields.Many2One('account.tax.code.template',
'Credit Note Tax Code')
2012-04-03 20:06:15 +02:00
credit_note_tax_sign = fields.Numeric('Credit Note Tax Sign',
digits=(2, 0))
2008-07-21 13:59:50 +02:00
account = fields.Many2One('account.account.template', 'Account Template',
2012-03-27 10:43:03 +02:00
domain=[('parent', '=', None)], required=True)
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(TaxTemplate, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
cls._order.insert(0, ('account', 'ASC'))
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:47 +02:00
TableHandler = backend.get('TableHandler')
2012-09-24 22:17:29 +02:00
super(TaxTemplate, cls).__register__(module_name)
2010-08-19 22:02:00 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:29 +02:00
table = TableHandler(cursor, cls, module_name)
2009-04-06 10:32:43 +02:00
# Migration from 1.0 group is no more required
table.not_null_action('group', action='remove')
2008-07-21 13:59:50 +02:00
2014-12-02 12:54:29 +01:00
# Migration from 2.4: drop required on sequence
2012-05-14 15:36:13 +02:00
table.not_null_action('sequence', action='remove')
2013-06-26 22:59:55 +02:00
# Migration from 2.8: rename percentage into rate
if table.column_exist('percentage'):
2013-09-06 22:33:47 +02:00
sql_table = cls.__table__()
cursor.execute(*sql_table.update(
columns=[sql_table.rate],
values=[sql_table.percentage / 100]))
2013-06-26 22:59:55 +02:00
table.drop_column('percentage')
2013-09-06 22:33:47 +02:00
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == Null, table.sequence]
2013-09-06 22:33:47 +02:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_type():
2008-07-21 13:59:50 +02:00
return 'percentage'
2012-09-24 22:17:29 +02:00
@staticmethod
def default_include_base_amount():
2008-07-21 13:59:50 +02:00
return False
2012-09-24 22:17:29 +02:00
@staticmethod
def default_invoice_base_sign():
return Decimal('1')
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_invoice_tax_sign():
return Decimal('1')
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_credit_note_base_sign():
return Decimal('1')
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_credit_note_tax_sign():
return Decimal('1')
2008-07-21 13:59:50 +02:00
2012-09-24 22:17:29 +02:00
def _get_tax_value(self, tax=None):
2008-07-21 13:59:50 +02:00
'''
Set values for tax creation.
'''
res = {}
for field in ('name', 'description', 'sequence', 'amount',
2013-06-26 22:59:55 +02:00
'rate', 'type', 'invoice_base_sign', 'invoice_tax_sign',
2014-01-31 22:30:34 +01:00
'credit_note_base_sign', 'credit_note_tax_sign',
'start_date', 'end_date'):
if not tax or getattr(tax, field) != getattr(self, field):
2012-09-24 22:17:29 +02:00
res[field] = getattr(self, field)
2008-07-21 13:59:50 +02:00
for field in ('group',):
if not tax or getattr(tax, field) != getattr(self, field):
2012-10-21 21:11:39 +02:00
value = getattr(self, field)
if value:
res[field] = getattr(self, field).id
else:
res[field] = None
if not tax or tax.template != self:
2012-09-24 22:17:29 +02:00
res['template'] = self.id
2008-07-21 13:59:50 +02:00
return res
2012-09-24 22:17:29 +02:00
def create_tax(self, company_id, template2tax_code, template2account,
template2tax=None, parent_id=None):
2008-07-21 13:59:50 +02:00
'''
Create recursively taxes based on template.
2012-09-24 22:17:29 +02:00
template2tax_code is a dictionary with tax code template id as key and
tax code id as value, used to convert tax code template into tax code.
template2account is a dictionary with account template id as key and
account id as value, used to convert account template into account
code.
template2tax is a dictionary with tax template id as key and tax id as
value, used to convert template id into tax. The dictionary is filled
with new taxes.
Return id of the tax created
2008-07-21 13:59:50 +02:00
'''
2012-09-24 22:17:29 +02:00
pool = Pool()
Tax = pool.get('account.tax')
Lang = pool.get('ir.lang')
2012-11-23 18:00:16 +01:00
Config = pool.get('ir.configuration')
2008-07-21 13:59:50 +02:00
if template2tax is None:
template2tax = {}
2012-09-24 22:17:29 +02:00
if self.id not in template2tax:
vals = self._get_tax_value()
2008-07-21 13:59:50 +02:00
vals['company'] = company_id
vals['parent'] = parent_id
2012-09-24 22:17:29 +02:00
if self.invoice_account:
vals['invoice_account'] = \
2013-02-16 18:54:35 +01:00
template2account[self.invoice_account.id]
else:
2012-03-27 10:43:03 +02:00
vals['invoice_account'] = None
2012-09-24 22:17:29 +02:00
if self.credit_note_account:
vals['credit_note_account'] = \
2013-02-16 18:54:35 +01:00
template2account[self.credit_note_account.id]
else:
2012-03-27 10:43:03 +02:00
vals['credit_note_account'] = None
2012-09-24 22:17:29 +02:00
if self.invoice_base_code:
vals['invoice_base_code'] = \
2013-02-16 18:54:35 +01:00
template2tax_code[self.invoice_base_code.id]
else:
2012-03-27 10:43:03 +02:00
vals['invoice_base_code'] = None
2012-09-24 22:17:29 +02:00
if self.invoice_tax_code:
vals['invoice_tax_code'] = \
2013-02-16 18:54:35 +01:00
template2tax_code[self.invoice_tax_code.id]
else:
2012-03-27 10:43:03 +02:00
vals['invoice_tax_code'] = None
2012-09-24 22:17:29 +02:00
if self.credit_note_base_code:
vals['credit_note_base_code'] = \
2013-02-16 18:54:35 +01:00
template2tax_code[self.credit_note_base_code.id]
else:
2012-03-27 10:43:03 +02:00
vals['credit_note_base_code'] = None
2012-09-24 22:17:29 +02:00
if self.credit_note_tax_code:
vals['credit_note_tax_code'] = \
2013-02-16 18:54:35 +01:00
template2tax_code[self.credit_note_tax_code.id]
else:
2012-03-27 10:43:03 +02:00
vals['credit_note_tax_code'] = None
2008-07-21 13:59:50 +02:00
2013-01-06 22:18:13 +01:00
new_tax, = Tax.create([vals])
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
template = self.__class__(self.id)
data = {}
for field_name, field in template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(template, field_name)
!= prev_data[field_name])):
data[field_name] = getattr(template, field_name)
if data:
Tax.write([new_tax], data)
template2tax[self.id] = new_tax.id
new_id = template2tax[self.id]
2008-07-21 13:59:50 +02:00
new_childs = []
2012-09-24 22:17:29 +02:00
for child in self.childs:
new_childs.append(child.create_tax(company_id, template2tax_code,
template2account, template2tax=template2tax,
parent_id=new_id))
2008-07-21 13:59:50 +02:00
return new_id
2009-02-28 16:57:10 +01:00
class Tax(ModelSQL, ModelView):
2008-03-23 23:05:12 +01:00
'''
Account Tax
Type:
2013-06-26 22:59:55 +02:00
percentage: tax = price * rate
2008-03-23 23:05:12 +01:00
fixed: tax = amount
none: tax = none
'''
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax'
2008-06-18 18:55:41 +02:00
name = fields.Char('Name', required=True, translate=True)
description = fields.Char('Description', required=True, translate=True,
help="The name that will be used in reports")
2009-04-06 10:32:43 +02:00
group = fields.Many2One('account.tax.group', 'Group',
2008-03-23 23:05:12 +01:00
states={
2010-01-20 10:56:33 +01:00
'invisible': Bool(Eval('parent')),
}, depends=['parent'])
2008-03-23 23:05:12 +01:00
active = fields.Boolean('Active')
2013-09-06 22:33:47 +02:00
sequence = fields.Integer('Sequence', help='Use to order the taxes')
2014-01-31 22:30:34 +01:00
start_date = fields.Date('Starting Date')
end_date = fields.Date('Ending Date')
2014-01-26 14:28:46 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'on_change_with_currency_digits')
2010-01-20 10:56:33 +01:00
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
2011-08-21 11:20:29 +02:00
states={
'required': Eval('type') == 'fixed',
2011-08-21 11:20:29 +02:00
'invisible': Eval('type') != 'fixed',
}, help='In company\'s currency',
depends=['type', 'currency_digits'])
2013-06-26 22:59:55 +02:00
rate = fields.Numeric('Rate', digits=(14, 10),
2011-08-21 11:20:29 +02:00
states={
'required': Eval('type') == 'percentage',
2011-08-21 11:20:29 +02:00
'invisible': Eval('type') != 'percentage',
2013-06-26 22:59:55 +02:00
}, depends=['type'])
2008-03-23 23:05:12 +01:00
type = fields.Selection([
('percentage', 'Percentage'),
('fixed', 'Fixed'),
('none', 'None'),
], 'Type', required=True)
2008-11-14 22:00:15 +01:00
parent = fields.Many2One('account.tax', 'Parent', ondelete='CASCADE')
2009-03-19 19:51:42 +01:00
childs = fields.One2Many('account.tax', 'parent', 'Children')
company = fields.Many2One('company.company', 'Company', required=True,
2011-08-21 11:20:29 +02:00
domain=[
('id', If(Eval('context', {}).contains('company'), '=', '!='),
2013-10-09 18:25:04 +02:00
Eval('context', {}).get('company', -1)),
2012-03-14 09:23:04 +01:00
], select=True)
2008-07-21 13:59:50 +02:00
invoice_account = fields.Many2One('account.account', 'Invoice Account',
2011-08-04 16:57:14 +02:00
domain=[
('company', '=', Eval('company')),
2012-03-30 14:18:10 +02:00
('kind', 'not in', ['view', 'receivable', 'payable']),
2010-01-20 10:56:33 +01:00
],
2011-08-04 16:57:14 +02:00
states={
2011-08-21 11:20:29 +02:00
'readonly': (Eval('type') == 'none') | ~Eval('company'),
'required': (Eval('type') != 'none') & Eval('company'),
2011-08-04 16:57:14 +02:00
},
depends=['company', 'type'])
2012-04-03 20:06:15 +02:00
credit_note_account = fields.Many2One('account.account',
'Credit Note Account',
2011-08-04 16:57:14 +02:00
domain=[
('company', '=', Eval('company')),
2012-03-30 14:18:10 +02:00
('kind', 'not in', ['view', 'receivable', 'payable']),
2010-01-20 10:56:33 +01:00
],
2011-08-04 16:57:14 +02:00
states={
2011-08-21 11:20:29 +02:00
'readonly': (Eval('type') == 'none') | ~Eval('company'),
'required': (Eval('type') != 'none') & Eval('company'),
2011-08-04 16:57:14 +02:00
},
depends=['company', 'type'])
2008-03-23 23:05:12 +01:00
invoice_base_code = fields.Many2One('account.tax.code',
2011-08-21 11:20:29 +02:00
'Invoice Base Code',
2014-12-03 16:13:08 +01:00
domain=[
('company', '=', Eval('company', -1)),
],
2011-08-21 11:20:29 +02:00
states={
'readonly': Eval('type') == 'none',
2014-12-03 16:13:08 +01:00
},
depends=['type', 'company'])
2008-03-23 23:05:12 +01:00
invoice_base_sign = fields.Numeric('Invoice Base Sign', digits=(2, 0),
2011-08-21 11:20:29 +02:00
help='Usualy 1 or -1',
states={
'required': Eval('type') != 'none',
2011-08-21 11:20:29 +02:00
'readonly': Eval('type') == 'none',
}, depends=['type'])
2008-03-23 23:05:12 +01:00
invoice_tax_code = fields.Many2One('account.tax.code',
2011-08-21 11:20:29 +02:00
'Invoice Tax Code',
2014-12-03 16:13:08 +01:00
domain=[
('company', '=', Eval('company', -1)),
],
2011-08-21 11:20:29 +02:00
states={
'readonly': Eval('type') == 'none',
2014-12-03 16:13:08 +01:00
},
depends=['type', 'company'])
2008-03-23 23:05:12 +01:00
invoice_tax_sign = fields.Numeric('Invoice Tax Sign', digits=(2, 0),
2011-08-21 11:20:29 +02:00
help='Usualy 1 or -1',
states={
'required': Eval('type') != 'none',
2011-08-21 11:20:29 +02:00
'readonly': Eval('type') == 'none',
}, depends=['type'])
2008-10-03 17:11:53 +02:00
credit_note_base_code = fields.Many2One('account.tax.code',
2011-08-21 11:20:29 +02:00
'Credit Note Base Code',
2014-12-03 16:13:08 +01:00
domain=[
('company', '=', Eval('company', -1)),
],
2011-08-21 11:20:29 +02:00
states={
'readonly': Eval('type') == 'none',
2014-12-03 16:13:08 +01:00
},
depends=['type', 'company'])
credit_note_base_sign = fields.Numeric('Credit Note Base Sign',
digits=(2, 0), help='Usualy 1 or -1',
states={
'required': Eval('type') != 'none',
'readonly': Eval('type') == 'none',
}, depends=['type'])
2008-10-03 17:11:53 +02:00
credit_note_tax_code = fields.Many2One('account.tax.code',
2011-08-21 11:20:29 +02:00
'Credit Note Tax Code',
2014-12-03 16:13:08 +01:00
domain=[
('company', '=', Eval('company', -1)),
],
2011-08-21 11:20:29 +02:00
states={
'readonly': Eval('type') == 'none',
2014-12-03 16:13:08 +01:00
},
depends=['type', 'company'])
2012-04-03 20:06:15 +02:00
credit_note_tax_sign = fields.Numeric('Credit Note Tax Sign',
digits=(2, 0), help='Usualy 1 or -1',
2011-08-21 11:20:29 +02:00
states={
'required': Eval('type') != 'none',
2011-08-21 11:20:29 +02:00
'readonly': Eval('type') == 'none',
}, depends=['type'])
template = fields.Many2One('account.tax.template', 'Template')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(Tax, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
2008-04-22 17:47:52 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:47 +02:00
TableHandler = backend.get('TableHandler')
2012-09-24 22:17:29 +02:00
super(Tax, cls).__register__(module_name)
2010-08-19 22:02:00 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:29 +02:00
table = TableHandler(cursor, cls, module_name)
2009-04-06 10:32:43 +02:00
# Migration from 1.0 group is no more required
table.not_null_action('group', action='remove')
2012-05-14 15:36:13 +02:00
# Migration from 2.4: drop required on sequence
table.not_null_action('sequence', action='remove')
2013-06-26 22:59:55 +02:00
# Migration from 2.8: rename percentage into rate
if table.column_exist('percentage'):
2013-09-06 22:33:47 +02:00
sql_table = cls.__table__()
cursor.execute(*sql_table.update(
columns=[sql_table.rate],
values=[sql_table.percentage / 100]))
2013-06-26 22:59:55 +02:00
table.drop_column('percentage')
2013-09-06 22:33:47 +02:00
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == Null, table.sequence]
2013-09-06 22:33:47 +02:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_active():
2008-03-23 23:05:12 +01:00
return True
2012-09-24 22:17:29 +02:00
@staticmethod
def default_type():
2008-03-23 23:05:12 +01:00
return 'percentage'
2012-09-24 22:17:29 +02:00
@staticmethod
def default_include_base_amount():
2008-03-23 23:05:12 +01:00
return False
2012-09-24 22:17:29 +02:00
@staticmethod
def default_invoice_base_sign():
return Decimal('1')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_invoice_tax_sign():
return Decimal('1')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_credit_note_base_sign():
return Decimal('1')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_credit_note_tax_sign():
return Decimal('1')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:03 +02:00
return Transaction().context.get('company')
2008-03-23 23:05:12 +01:00
2014-01-26 14:28:46 +01:00
@fields.depends('company')
2012-09-24 22:17:29 +02:00
def on_change_with_currency_digits(self, name=None):
if self.company:
return self.company.currency.digits
2009-06-05 19:10:11 +02:00
return 2
2012-09-24 22:17:29 +02:00
def _process_tax(self, price_unit):
if self.type == 'percentage':
2013-06-26 22:59:55 +02:00
amount = price_unit * self.rate
2008-03-23 23:05:12 +01:00
return {
'base': price_unit,
'amount': amount,
2012-09-24 22:17:29 +02:00
'tax': self,
}
if self.type == 'fixed':
amount = self.amount
2008-03-23 23:05:12 +01:00
return {
'base': price_unit,
'amount': amount,
2012-09-24 22:17:29 +02:00
'tax': self,
}
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
2014-01-31 22:30:34 +01:00
def _unit_compute(cls, taxes, price_unit, date):
2008-03-23 23:05:12 +01:00
res = []
for tax in taxes:
2014-01-31 22:30:34 +01:00
start_date = tax.start_date or datetime.date.min
end_date = tax.end_date or datetime.date.max
if not (start_date <= date <= end_date):
continue
2008-03-23 23:05:12 +01:00
if tax.type != 'none':
2012-09-24 22:17:29 +02:00
res.append(tax._process_tax(price_unit))
2008-03-23 23:05:12 +01:00
if len(tax.childs):
2014-01-31 22:30:34 +01:00
res.extend(cls._unit_compute(tax.childs, price_unit, date))
2008-03-23 23:05:12 +01:00
return res
2012-09-24 22:17:29 +02:00
@classmethod
2015-01-15 23:40:42 +01:00
def _reverse_rate_amount(cls, taxes, date):
rate = None
amount = 0
for tax in taxes:
start_date = tax.start_date or datetime.date.min
end_date = tax.end_date or datetime.date.max
if not (start_date <= date <= end_date):
continue
if tax.type == 'percentage':
if rate is None:
rate = tax.rate
else:
rate *= tax.rate
elif tax.type == 'fixed':
amount += tax.amount
if tax.childs:
child_rate, child_amount = cls._reverse_rate_amount(
tax.childs, date)
if rate is None:
rate = child_rate
else:
rate *= child_rate
amount += child_amount
return rate, amount
@classmethod
def _reverse_unit_compute(cls, price_unit, taxes, date):
rate, amount = cls._reverse_rate_amount(taxes, date)
price_unit -= amount
if rate is not None:
price_unit = price_unit / (1 + rate)
return price_unit
@classmethod
def sort_taxes(cls, taxes, reverse=False):
'''
2012-09-24 22:17:29 +02:00
Return a list of taxes sorted
'''
2015-01-15 23:40:42 +01:00
return sorted(taxes, key=lambda t: (t.sequence, t.id), reverse=reverse)
2012-09-24 22:17:29 +02:00
@classmethod
2014-01-31 22:30:34 +01:00
def compute(cls, taxes, price_unit, quantity, date=None):
2008-03-23 23:05:12 +01:00
'''
2014-01-31 22:30:34 +01:00
Compute taxes for price_unit and quantity at the date.
2008-03-23 23:05:12 +01:00
Return list of dict for each taxes and their childs with:
base
amount
tax
'''
2014-01-31 22:30:34 +01:00
pool = Pool()
Date = pool.get('ir.date')
if date is None:
date = Date.today()
2012-09-24 22:17:29 +02:00
taxes = cls.sort_taxes(taxes)
2014-01-31 22:30:34 +01:00
res = cls._unit_compute(taxes, price_unit, date)
quantity = Decimal(str(quantity or 0.0))
2008-03-23 23:05:12 +01:00
for row in res:
row['base'] *= quantity
row['amount'] *= quantity
2008-03-23 23:05:12 +01:00
return res
2015-01-15 23:40:42 +01:00
@classmethod
def reverse_compute(cls, price_unit, taxes, date=None):
'''
Reverse compute the price_unit for taxes at the date.
'''
pool = Pool()
Date = pool.get('ir.date')
if date is None:
date = Date.today()
taxes = cls.sort_taxes(taxes, reverse=True)
return cls._reverse_unit_compute(price_unit, taxes, date)
2012-09-24 22:17:29 +02:00
def update_tax(self, template2tax_code, template2account,
2010-08-19 22:02:00 +02:00
template2tax=None):
'''
Update recursively taxes based on template.
2012-09-24 22:17:29 +02:00
template2tax_code is a dictionary with tax code template id as key and
tax code id as value, used to convert tax code template into tax code.
template2account is a dictionary with account template id as key and
account id as value, used to convert account template into account
code.
template2tax is a dictionary with tax template id as key and tax id as
value, used to convert template id into tax. The dictionary is filled
with new taxes.
'''
2012-11-23 18:00:16 +01:00
pool = Pool()
Lang = pool.get('ir.lang')
Config = pool.get('ir.configuration')
if template2tax is None:
template2tax = {}
2012-09-24 22:17:29 +02:00
if self.template:
vals = self.template._get_tax_value(tax=self)
invoice_account_id = (self.invoice_account.id
if self.invoice_account else None)
if (self.template.invoice_account and
invoice_account_id != template2account.get(
self.template.invoice_account.id)):
2012-04-03 20:06:15 +02:00
vals['invoice_account'] = template2account.get(
2012-09-24 22:17:29 +02:00
self.template.invoice_account.id)
elif (not self.template.invoice_account
and self.invoice_account):
2012-03-27 10:43:03 +02:00
vals['invoice_account'] = None
credit_note_account_id = (self.credit_note_account.id
if self.credit_note_account else None)
if (self.template.credit_note_account and
credit_note_account_id != template2account.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_account.id)):
2012-04-03 20:06:15 +02:00
vals['credit_note_account'] = template2account.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_account.id)
elif (not self.template.credit_note_account
and self.credit_note_account):
2012-03-27 10:43:03 +02:00
vals['credit_note_account'] = None
invoice_base_code_id = (self.invoice_base_code.id
if self.invoice_base_code else None)
if (self.template.invoice_base_code and
invoice_base_code_id != template2tax_code.get(
self.template.invoice_base_code.id)):
2012-04-03 20:06:15 +02:00
vals['invoice_base_code'] = template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.invoice_base_code.id)
elif (not self.template.invoice_base_code
and self.invoice_base_code):
2012-03-27 10:43:03 +02:00
vals['invoice_base_code'] = None
invoice_tax_code_id = (self.invoice_tax_code.id
if self.invoice_tax_code else None)
2012-09-24 22:17:29 +02:00
if (self.template.invoice_tax_code
and invoice_tax_code_id != template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.invoice_tax_code.id)):
2012-04-03 20:06:15 +02:00
vals['invoice_tax_code'] = template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.invoice_tax_code.id)
elif (not self.template.invoice_tax_code
and self.invoice_tax_code):
2012-03-27 10:43:03 +02:00
vals['invoice_tax_code'] = None
credit_note_base_code_id = (self.credit_note_base_code.id
if self.credit_note_base_code else None)
2012-09-24 22:17:29 +02:00
if (self.template.credit_note_base_code
and credit_note_base_code_id != template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_base_code.id)):
2012-04-03 20:06:15 +02:00
vals['credit_note_base_code'] = template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_base_code.id)
elif (not self.template.credit_note_base_code
and self.credit_note_base_code):
2012-03-27 10:43:03 +02:00
vals['credit_note_base_code'] = None
credit_note_tax_code_id = (self.credit_note_tax_code.id
if self.credit_note_tax_code else None)
2012-09-24 22:17:29 +02:00
if (self.template.credit_note_tax_code
and credit_note_tax_code_id != template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_tax_code.id)):
2012-04-03 20:06:15 +02:00
vals['credit_note_tax_code'] = template2tax_code.get(
2012-09-24 22:17:29 +02:00
self.template.credit_note_tax_code.id)
elif (not self.template.credit_note_tax_code
and self.credit_note_tax_code):
2012-03-27 10:43:03 +02:00
vals['credit_note_tax_code'] = None
if vals:
2012-09-24 22:17:29 +02:00
self.write([self], vals)
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self.template._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self.template, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
tax = self.__class__(self.id)
data = {}
for field_name, field in tax.template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(tax.template, field_name)
!= prev_data[field_name])):
data[field_name] = getattr(tax.template,
field_name)
if data:
self.write([tax], data)
2012-10-21 21:11:39 +02:00
template2tax[self.template.id] = self.id
for child in self.childs:
2012-09-24 22:17:29 +02:00
child.update_tax(template2tax_code, template2account,
template2tax=template2tax)
2008-03-23 23:05:12 +01:00
2014-12-19 19:42:37 +01:00
class _TaxKey(dict):
def __init__(self, **kwargs):
self.update(kwargs)
def _key(self):
return (self['base_code'], self['base_sign'],
self['tax_code'], self['tax_sign'],
self['account'], self['tax'])
def __eq__(self, other):
if isinstance(other, _TaxKey):
return self._key() == other._key()
return self._key() == other
def __hash__(self):
return hash(self._key())
_TaxableLine = namedtuple('_TaxableLine', ('taxes', 'unit_price', 'quantity'))
class TaxableMixin(object):
@property
def taxable_lines(self):
"""A list of tuples where
- the first element is the taxes applicable
- the second element is the line unit price
- the third element is the line quantity
"""
return []
@property
def tax_type(self):
"The taxation type it can be 'invoice' or 'credit_note'"
return None
@property
def currency(self):
"The currency used by the taxable object"
return None
@property
def tax_date(self):
"Date to use when computing the tax"
pool = Pool()
Date = pool.get('ir.date')
return Date.today()
def _get_tax_context(self):
return {}
@staticmethod
def _compute_tax_line(type_, amount, base, tax):
assert type_ in ('invoice', 'credit_note')
line = {}
line['manual'] = False
line['description'] = tax.description
line['base'] = base
line['amount'] = amount
line['tax'] = tax.id if tax else None
for attribute in ['base_code', 'tax_code', 'account']:
value = getattr(tax, '%s_%s' % (type_, attribute), None)
line[attribute] = value.id if value else None
for attribute in ['base_sign', 'tax_sign']:
value = getattr(tax, '%s_%s' % (type_, attribute), None)
line[attribute] = value
return _TaxKey(**line)
def _round_taxes(self, taxes):
if not self.currency:
return
for taxline in taxes.itervalues():
for attribute in ('base', 'amount'):
taxline[attribute] = self.currency.round(taxline[attribute])
def _get_taxes(self):
pool = Pool()
Tax = pool.get('account.tax')
Configuration = pool.get('account.configuration')
config = Configuration(1)
taxes = {}
with Transaction().set_context(self._get_tax_context()):
taxable_lines = [_TaxableLine(*params)
for params in self.taxable_lines]
for line in taxable_lines:
l_taxes = Tax.compute(line.taxes, line.unit_price,
line.quantity, self.tax_date)
for tax in l_taxes:
taxline = self._compute_tax_line(self.tax_type, **tax)
if taxline not in taxes:
taxes[taxline] = taxline
else:
taxes[taxline]['base'] += taxline['base']
taxes[taxline]['amount'] += taxline['amount']
if config.tax_rounding == 'line':
self._round_taxes(taxes)
if config.tax_rounding == 'document':
self._round_taxes(taxes)
return taxes
2012-09-24 22:17:29 +02:00
class TaxLine(ModelSQL, ModelView):
2008-03-23 23:05:12 +01:00
'Tax Line'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.line'
2008-03-23 23:05:12 +01:00
_rec_name = 'amount'
2014-01-26 14:28:46 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'on_change_with_currency_digits')
2010-01-20 10:56:33 +01:00
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
required=True, depends=['currency_digits'])
2012-03-05 10:52:56 +01:00
code = fields.Many2One('account.tax.code', 'Code', select=True,
2014-12-03 16:13:08 +01:00
required=True,
domain=[
('company', '=', Eval('company', -1)),
],
depends=['company'])
2012-03-05 10:52:56 +01:00
tax = fields.Many2One('account.tax', 'Tax', select=True,
2014-12-03 16:13:08 +01:00
ondelete='RESTRICT',
domain=[
('company', '=', Eval('company', -1)),
],
depends=['company'])
2008-03-23 23:05:12 +01:00
move_line = fields.Many2One('account.move.line', 'Move Line',
2012-03-05 10:52:56 +01:00
required=True, select=True, ondelete='CASCADE')
2014-12-03 16:13:08 +01:00
company = fields.Function(fields.Many2One('company.company', 'Company'),
'on_change_with_company')
2008-03-23 23:05:12 +01:00
2014-01-26 14:28:46 +01:00
@fields.depends('move_line')
2012-09-24 22:17:29 +02:00
def on_change_with_currency_digits(self, name=None):
if self.move_line:
return self.move_line.currency_digits
2009-06-05 19:10:11 +02:00
return 2
2014-01-26 14:28:46 +01:00
@fields.depends('tax')
2012-09-24 22:17:29 +02:00
def on_change_tax(self):
2014-10-29 20:39:03 +01:00
self.code = None
2008-03-23 23:05:12 +01:00
2014-12-03 16:13:08 +01:00
@fields.depends('_parent_move_line.account')
def on_change_with_company(self, name=None):
if self.move_line:
return self.move_line.account.company.id
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
class TaxRuleTemplate(ModelSQL, ModelView):
'Tax Rule Template'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.rule.template'
name = fields.Char('Name', required=True, translate=True)
kind = fields.Selection(KINDS, 'Kind', required=True)
lines = fields.One2Many('account.tax.rule.line.template', 'rule', 'Lines')
account = fields.Many2One('account.account.template', 'Account Template',
2012-03-27 10:43:03 +02:00
domain=[('parent', '=', None)], required=True)
@staticmethod
def default_kind():
return 'both'
2012-09-24 22:17:29 +02:00
def _get_tax_rule_value(self, rule=None):
'''
Set values for tax rule creation.
'''
res = {}
2012-09-24 22:17:29 +02:00
if not rule or rule.name != self.name:
res['name'] = self.name
if not rule or rule.kind != self.kind:
res['kind'] = self.kind
2012-09-24 22:17:29 +02:00
if not rule or rule.template.id != self.id:
res['template'] = self.id
return res
2012-09-24 22:17:29 +02:00
def create_rule(self, company_id, template2rule=None):
'''
Create tax rule based on template.
2012-09-24 22:17:29 +02:00
template2rule is a dictionary with tax rule template id as key and tax
rule id as value, used to convert template id into tax rule. The
dictionary is filled with new tax rules.
Return id of the tax rule created
'''
2012-09-24 22:17:29 +02:00
pool = Pool()
Rule = pool.get('account.tax.rule')
Lang = pool.get('ir.lang')
2012-11-23 18:00:16 +01:00
Config = pool.get('ir.configuration')
if template2rule is None:
template2rule = {}
2012-09-24 22:17:29 +02:00
if self.id not in template2rule:
vals = self._get_tax_rule_value()
vals['company'] = company_id
2013-01-06 22:18:13 +01:00
new_rule, = Rule.create([vals])
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
template = self.__class__(self.id)
data = {}
for field_name, field in template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(self, field_name)
!= prev_data[field_name])):
data[field_name] = getattr(self, field_name)
if data:
Rule.write([new_rule], data)
template2rule[self.id] = new_rule.id
return template2rule[self.id]
2012-09-24 22:17:29 +02:00
class TaxRule(ModelSQL, ModelView):
'Tax Rule'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.rule'
name = fields.Char('Name', required=True, translate=True)
kind = fields.Selection(KINDS, 'Kind', required=True)
company = fields.Many2One('company.company', 'Company', required=True,
2012-03-05 10:52:56 +01:00
select=True, domain=[
2011-08-21 11:20:29 +02:00
('id', If(Eval('context', {}).contains('company'), '=', '!='),
2013-10-09 18:25:04 +02:00
Eval('context', {}).get('company', -1)),
2010-01-20 10:56:33 +01:00
])
lines = fields.One2Many('account.tax.rule.line', 'rule', 'Lines')
template = fields.Many2One('account.tax.rule.template', 'Template')
2013-03-20 12:24:33 +01:00
@staticmethod
def default_kind():
return 'both'
2012-09-24 22:17:29 +02:00
def apply(self, tax, pattern):
'''
Apply rule on tax
2012-09-24 22:17:29 +02:00
pattern is a dictonary with rule line field as key and match value as
value.
Return a list of the tax id to use or None
'''
pattern = pattern.copy()
pattern['group'] = tax.group.id if tax and tax.group else None
pattern['origin_tax'] = tax.id if tax else None
2012-09-24 22:17:29 +02:00
for line in self.lines:
if line.match(pattern):
return line.get_taxes()
2012-03-27 10:43:03 +02:00
return tax and [tax.id] or None
2012-09-24 22:17:29 +02:00
def update_rule(self, template2rule=None):
'''
Update tax rule based on template.
2012-09-24 22:17:29 +02:00
template2rule is a dictionary with tax rule template id as key and tax
rule id as value, used to convert template id into tax rule. The
dictionary is filled with new tax rules.
'''
2012-11-23 18:00:16 +01:00
pool = Pool()
Lang = pool.get('ir.lang')
Config = pool.get('ir.configuration')
if template2rule is None:
template2rule = {}
2012-09-24 22:17:29 +02:00
if self.template:
vals = self.template._get_tax_rule_value(rule=self)
if vals:
2012-09-24 22:17:29 +02:00
self.write([self], vals)
2012-11-23 18:00:16 +01:00
prev_lang = self._context.get('language') or Config.get_language()
prev_data = {}
2012-09-24 22:17:29 +02:00
for field_name, field in self.template._fields.iteritems():
if getattr(field, 'translate', False):
2012-09-24 22:17:29 +02:00
prev_data[field_name] = getattr(self.template, field_name)
for lang in Lang.get_translatable_languages():
if lang == prev_lang:
continue
2012-09-24 22:17:29 +02:00
with Transaction().set_context(language=lang):
rule = self.__class__(self.id)
data = {}
for field_name, field in rule.template._fields.iteritems():
if (getattr(field, 'translate', False)
and (getattr(rule.template, field_name)
!= prev_data[field_name])):
data[field_name] = getattr(rule.template,
field_name)
if data:
self.write([rule], data)
2012-10-21 21:11:39 +02:00
template2rule[self.template.id] = self.id
2012-09-24 22:17:29 +02:00
class TaxRuleLineTemplate(ModelSQL, ModelView):
'Tax Rule Line Template'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.rule.line.template'
rule = fields.Many2One('account.tax.rule.template', 'Rule', required=True,
ondelete='CASCADE')
group = fields.Many2One('account.tax.group', 'Tax Group',
ondelete='RESTRICT')
origin_tax = fields.Many2One('account.tax.template', 'Original Tax',
2011-08-04 16:57:14 +02:00
domain=[
2011-08-21 11:20:29 +02:00
('account', '=', Eval('_parent_rule', {}).get('account', 0)),
('group', '=', Eval('group')),
['OR',
('group', '=', None),
If(Eval('_parent_rule', {}).get('kind', 'both') == 'sale',
('group.kind', 'in', ['sale', 'both']),
If(Eval('_parent_rule', {}).get('kind', 'both') ==
'purchase',
('group.kind', 'in', ['purchase', 'both']),
('group.kind', 'in', ['sale', 'purchase', 'both']))),
],
2010-01-20 10:56:33 +01:00
],
2013-02-16 18:54:35 +01:00
help=('If the original tax template is filled, the rule will be '
'applied only for this tax template.'),
depends=['group'],
ondelete='RESTRICT')
tax = fields.Many2One('account.tax.template', 'Substitution Tax',
2011-08-04 16:57:14 +02:00
domain=[
2011-08-21 11:20:29 +02:00
('account', '=', Eval('_parent_rule', {}).get('account', 0)),
2011-08-04 16:57:14 +02:00
('group', '=', Eval('group')),
['OR',
('group', '=', None),
If(Eval('_parent_rule', {}).get('kind', 'both') == 'sale',
('group.kind', 'in', ['sale', 'both']),
If(Eval('_parent_rule', {}).get('kind', 'both') ==
'purchase',
('group.kind', 'in', ['purchase', 'both']),
('group.kind', 'in', ['sale', 'purchase', 'both']))),
],
2011-08-04 16:57:14 +02:00
],
depends=['group'],
ondelete='RESTRICT')
2013-09-06 22:33:47 +02:00
sequence = fields.Integer('Sequence')
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(TaxRuleLineTemplate, cls).__setup__()
cls._order.insert(0, ('rule', 'ASC'))
cls._order.insert(0, ('sequence', 'ASC'))
2012-09-24 22:17:29 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:47 +02:00
TableHandler = backend.get('TableHandler')
2012-05-14 15:36:13 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:29 +02:00
table = TableHandler(cursor, cls, module_name)
2012-05-14 15:36:13 +02:00
2012-09-24 22:17:29 +02:00
super(TaxRuleLineTemplate, cls).__register__(module_name)
2012-05-14 15:36:13 +02:00
# Migration from 2.4: drop required on sequence
table.not_null_action('sequence', action='remove')
2013-09-06 22:33:47 +02:00
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == Null, table.sequence]
2013-09-06 22:33:47 +02:00
2012-09-24 22:17:29 +02:00
def _get_tax_rule_line_value(self, rule_line=None):
'''
Set values for tax rule line creation.
'''
res = {}
2012-10-21 21:11:39 +02:00
if not rule_line or rule_line.group != self.group:
res['group'] = self.group.id if self.group else None
2012-09-24 22:17:29 +02:00
if not rule_line or rule_line.sequence != self.sequence:
res['sequence'] = self.sequence
2012-10-21 21:11:39 +02:00
if not rule_line or rule_line.template != self:
2012-09-24 22:17:29 +02:00
res['template'] = self.id
return res
2012-09-24 22:17:29 +02:00
def create_rule_line(self, template2tax, template2rule,
2010-08-19 22:02:00 +02:00
template2rule_line=None):
'''
Create tax rule line based on template.
2012-09-24 22:17:29 +02:00
template2tax is a dictionary with tax template id as key and tax id as
value, used to convert template id into tax.
template2rule is a dictionary with tax rule template id as key and tax
rule id as value, used to convert template id into tax rule.
template2rule_line is a dictionary with tax rule line template id as
key and tax rule line id as value, used to convert template id into tax
rule line. The dictionary is filled with new tax rule lines.
Return id of the tax rule line created
'''
2012-09-24 22:17:29 +02:00
RuleLine = Pool().get('account.tax.rule.line')
if template2rule_line is None:
template2rule_line = {}
2012-09-24 22:17:29 +02:00
if self.id not in template2rule_line:
vals = self._get_tax_rule_line_value()
vals['rule'] = template2rule[self.rule.id]
if self.origin_tax:
vals['origin_tax'] = template2tax[self.origin_tax.id]
else:
2012-03-27 10:43:03 +02:00
vals['origin_tax'] = None
2012-09-24 22:17:29 +02:00
if self.tax:
vals['tax'] = template2tax[self.tax.id]
else:
2012-03-27 10:43:03 +02:00
vals['tax'] = None
2013-01-06 22:18:13 +01:00
new_rule_line, = RuleLine.create([vals])
2012-09-24 22:17:29 +02:00
template2rule_line[self.id] = new_rule_line.id
return template2rule_line[self.id]
2014-05-09 23:16:44 +02:00
class TaxRuleLine(ModelSQL, ModelView, MatchMixin):
'Tax Rule Line'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.rule.line'
_rec_name = 'tax'
rule = fields.Many2One('account.tax.rule', 'Rule', required=True,
2012-03-05 10:52:56 +01:00
select=True, ondelete='CASCADE')
group = fields.Many2One('account.tax.group', 'Tax Group',
ondelete='RESTRICT')
origin_tax = fields.Many2One('account.tax', 'Original Tax',
2011-08-04 16:57:14 +02:00
domain=[
2011-08-21 11:20:29 +02:00
('company', '=', Eval('_parent_rule', {}).get('company')),
2011-08-04 16:57:14 +02:00
('group', '=', Eval('group')),
['OR',
('group', '=', None),
If(Eval('_parent_rule', {}).get('kind', 'both') == 'sale',
('group.kind', 'in', ['sale', 'both']),
If(Eval('_parent_rule', {}).get('kind', 'both') ==
'purchase',
('group.kind', 'in', ['purchase', 'both']),
('group.kind', 'in', ['sale', 'purchase', 'both']))),
],
2010-01-20 10:56:33 +01:00
],
2013-02-16 18:54:35 +01:00
help=('If the original tax is filled, the rule will be applied '
'only for this tax.'),
depends=['group'],
ondelete='RESTRICT')
tax = fields.Many2One('account.tax', 'Substitution Tax',
2011-08-04 16:57:14 +02:00
domain=[
2011-08-21 11:20:29 +02:00
('company', '=', Eval('_parent_rule', {}).get('company')),
2011-08-04 16:57:14 +02:00
('group', '=', Eval('group')),
['OR',
('group', '=', None),
If(Eval('_parent_rule', {}).get('kind', 'both') == 'sale',
('group.kind', 'in', ['sale', 'both']),
If(Eval('_parent_rule', {}).get('kind', 'both') ==
'purchase',
('group.kind', 'in', ['purchase', 'both']),
('group.kind', 'in', ['sale', 'purchase', 'both']))),
],
2011-08-04 16:57:14 +02:00
],
depends=['group'],
ondelete='RESTRICT')
2013-09-06 22:33:47 +02:00
sequence = fields.Integer('Sequence')
template = fields.Many2One('account.tax.rule.line.template', 'Template')
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(TaxRuleLine, cls).__setup__()
cls._order.insert(0, ('rule', 'ASC'))
cls._order.insert(0, ('sequence', 'ASC'))
2012-09-24 22:17:29 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:47 +02:00
TableHandler = backend.get('TableHandler')
2012-05-14 15:36:13 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:29 +02:00
table = TableHandler(cursor, cls, module_name)
2012-05-14 15:36:13 +02:00
2012-09-24 22:17:29 +02:00
super(TaxRuleLine, cls).__register__(module_name)
2012-05-14 15:36:13 +02:00
# Migration from 2.4: drop required on sequence
table.not_null_action('sequence', action='remove')
2013-09-06 22:33:47 +02:00
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == Null, table.sequence]
2013-09-06 22:33:47 +02:00
2012-09-24 22:17:29 +02:00
def match(self, pattern):
2014-05-09 23:16:44 +02:00
if 'group' in pattern and not self.group:
if pattern['group']:
return False
return super(TaxRuleLine, self).match(pattern)
2012-09-24 22:17:29 +02:00
def get_taxes(self):
2009-04-06 10:32:43 +02:00
'''
Return list of taxes for a line
'''
2012-09-24 22:17:29 +02:00
if self.tax:
return [self.tax.id]
2012-03-27 10:43:03 +02:00
return None
2009-04-06 10:32:43 +02:00
2012-09-24 22:17:29 +02:00
def update_rule_line(self, template2tax, template2rule,
2010-08-19 22:02:00 +02:00
template2rule_line=None):
'''
Update tax rule line based on template.
2012-09-24 22:17:29 +02:00
template2tax is a dictionary with tax template id as key and tax id as
value, used to convert template id into tax.
template2rule is a dictionary with tax rule template id as key and tax
rule id as value, used to convert template id into tax rule.
template2rule_line is a dictionary with tax rule line template id as
key and tax rule line id as value, used to convert template id into tax
rule line. The dictionary is filled with new tax rule lines.
'''
if template2rule_line is None:
template2rule_line = {}
2012-09-24 22:17:29 +02:00
if self.template:
vals = self.template._get_tax_rule_line_value(rule_line=self)
if self.rule.id != template2rule[self.template.rule.id]:
vals['rule'] = template2rule[self.template.rule.id]
if self.origin_tax:
if self.template.origin_tax:
if (self.origin_tax.id !=
template2tax[self.template.origin_tax.id]):
vals['origin_tax'] = template2tax[
2012-09-24 22:17:29 +02:00
self.template.origin_tax.id]
else:
vals['origin_tax'] = None
elif self.template.origin_tax:
vals['origin_tax'] = template2tax[self.template.origin_tax.id]
2012-09-24 22:17:29 +02:00
if self.tax:
if self.template.tax:
if self.tax.id != template2tax[self.template.tax.id]:
vals['tax'] = template2tax[self.template.tax.id]
else:
vals['tax'] = None
elif self.template.tax:
vals['tax'] = template2tax[self.template.tax.id]
if vals:
2012-09-24 22:17:29 +02:00
self.write([self], vals)
template2rule_line[self.template.id] = self.id
2012-09-24 22:17:29 +02:00
class OpenTaxCode(Wizard):
2008-03-23 23:05:12 +01:00
'Open Code'
2012-09-24 22:17:29 +02:00
__name__ = 'account.tax.open_code'
2012-01-02 17:54:33 +01:00
start_state = 'open_'
open_ = StateAction('account.act_tax_line_form')
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
def do_open_(self, action):
2011-07-03 13:18:40 +02:00
pool = Pool()
2012-09-24 22:17:29 +02:00
FiscalYear = pool.get('account.fiscalyear')
Period = pool.get('account.period')
2008-03-23 23:05:12 +01:00
2010-08-19 22:02:00 +02:00
if not Transaction().context.get('fiscalyear'):
2012-09-24 22:17:29 +02:00
fiscalyears = FiscalYear.search([
('state', '=', 'open'),
])
2008-03-23 23:05:12 +01:00
else:
2012-09-24 22:17:29 +02:00
fiscalyears = [FiscalYear(Transaction().context['fiscalyear'])]
2008-03-23 23:05:12 +01:00
2012-09-24 22:17:29 +02:00
periods = []
2010-08-19 22:02:00 +02:00
if not Transaction().context.get('periods'):
2012-09-24 22:17:29 +02:00
periods = Period.search([
('fiscalyear', 'in', [f.id for f in fiscalyears]),
])
2008-03-23 23:05:12 +01:00
else:
2012-09-24 22:17:29 +02:00
periods = Period.browse(Transaction().context['periods'])
2008-03-23 23:05:12 +01:00
2012-01-02 17:54:33 +01:00
action['pyson_domain'] = PYSONEncoder().encode([
2012-09-24 22:17:29 +02:00
('move_line.period', 'in', [p.id for p in periods]),
2012-01-02 17:54:33 +01:00
('code', '=', Transaction().context['active_id']),
])
2010-08-19 22:02:00 +02:00
if Transaction().context.get('fiscalyear'):
2012-01-02 17:54:33 +01:00
action['pyson_context'] = PYSONEncoder().encode({
'fiscalyear': Transaction().context['fiscalyear'],
})
2008-03-23 23:05:12 +01:00
else:
2012-01-02 17:54:33 +01:00
action['pyson_context'] = PYSONEncoder().encode({
2012-09-24 22:17:29 +02:00
'periods': [p.id for p in periods],
2012-01-02 17:54:33 +01:00
})
return action, {}
2008-03-23 23:05:12 +01:00
2009-03-05 14:51:19 +01:00
class AccountTemplateTaxTemplate(ModelSQL):
2009-03-17 13:09:06 +01:00
'Account Template - Tax Template'
2012-09-24 22:17:29 +02:00
__name__ = 'account.account.template-account.tax.template'
2009-03-05 14:51:19 +01:00
_table = 'account_account_template_tax_rel'
account = fields.Many2One('account.account.template', 'Account Template',
2012-03-05 10:52:56 +01:00
ondelete='CASCADE', select=True, required=True)
2009-03-05 14:51:19 +01:00
tax = fields.Many2One('account.tax.template', 'Tax Template',
2012-03-05 10:52:56 +01:00
ondelete='RESTRICT', select=True, required=True)
2009-03-05 14:51:19 +01:00
2012-09-24 22:17:29 +02:00
class AccountTemplate2:
__name__ = 'account.account.template'
2009-03-05 14:51:19 +01:00
taxes = fields.Many2Many('account.account.template-account.tax.template',
'account', 'tax', 'Default Taxes',
2012-03-27 10:43:03 +02:00
domain=[('parent', '=', None)])
2009-03-05 14:51:19 +01:00
class AccountTax(ModelSQL):
2009-04-09 20:17:55 +02:00
'Account - Tax'
2012-09-24 22:17:29 +02:00
__name__ = 'account.account-account.tax'
2009-03-05 14:51:19 +01:00
_table = 'account_account_tax_rel'
account = fields.Many2One('account.account', 'Account', ondelete='CASCADE',
2012-03-05 10:52:56 +01:00
select=True, required=True)
2009-03-05 14:51:19 +01:00
tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
2012-03-05 10:52:56 +01:00
select=True, required=True)
2009-03-05 14:51:19 +01:00
2012-09-24 22:17:29 +02:00
class Account2:
__name__ = 'account.account'
2009-03-05 14:51:19 +01:00
taxes = fields.Many2Many('account.account-account.tax',
2008-03-23 23:05:12 +01:00
'account', 'tax', 'Default Taxes',
2010-01-20 10:56:33 +01:00
domain=[
('company', '=', Eval('company')),
2012-03-27 10:43:03 +02:00
('parent', '=', None),
2010-01-20 10:56:33 +01:00
],
2013-02-16 18:54:35 +01:00
help=('Default tax for manual encoding of move lines \n'
'for journal types: "expense" and "revenue"'),
depends=['company'])