diff --git a/CHANGELOG b/CHANGELOG
index 6e31d04411..ebe154d366 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Add tax rule to replace vat and supplier_vat on party
+ Previous vat configuration will be lost
* Allow to update chart of account from template
* Change sequence on Journal into Property for multi-company
* Allow egg installation
diff --git a/account.py b/account.py
index d45f0517c6..7136acffbc 100644
--- a/account.py
+++ b/account.py
@@ -1739,21 +1739,27 @@ class CreateChartAccount(Wizard):
account_template_obj = self.pool.get('account.account.template')
tax_code_template_obj = self.pool.get('account.tax.code.template')
tax_template_obj = self.pool.get('account.tax.template')
+ tax_rule_template_obj = self.pool.get('account.tax.rule.template')
+ tax_rule_line_template_obj = \
+ self.pool.get('account.tax.rule.line.template')
account_template = account_template_obj.browse(cursor, user,
datas['form']['account_template'], context=context)
+ # Create account types
template2type = {}
account_type_template_obj.create_type(cursor, user,
account_template.type, datas['form']['company'],
context=context, template2type=template2type)
+ # Create accounts
template2account = {}
account_template_obj.create_account(cursor, user,
account_template, datas['form']['company'],
context=context, template2account=template2account,
template2type=template2type)
+ # Create tax codes
template2tax_code = {}
tax_code_template_ids = tax_code_template_obj.search(cursor, user, [
('account', '=', datas['form']['account_template']),
@@ -1765,6 +1771,7 @@ class CreateChartAccount(Wizard):
tax_code_template, datas['form']['company'],
context=context, template2tax_code=template2tax_code)
+ # Create taxes
template2tax = {}
tax_template_ids = tax_template_obj.search(cursor, user, [
('account', '=', datas['form']['account_template']),
@@ -1778,9 +1785,33 @@ class CreateChartAccount(Wizard):
template2account=template2account,
context=context, template2tax=template2tax)
+ # Update taxes on accounts
account_template_obj.update_account_taxes(cursor, user,
account_template, template2account, template2tax, context=context)
+ # Create tax rules
+ template2rule = {}
+ tax_rule_template_ids = tax_rule_template_obj.search(cursor, user, [
+ ('account', '=', datas['form']['account_template']),
+ ], context=context)
+ for tax_rule_template in tax_rule_template_obj.browse(cursor, user,
+ tax_rule_template_ids, context=context):
+ tax_rule_template_obj.create_rule(cursor, user, tax_rule_template,
+ datas['form']['company'], context=context,
+ template2rule=template2rule)
+
+ # Create tax rule lines
+ template2rule_line = {}
+ tax_rule_line_template_ids = tax_rule_line_template_obj.search(cursor,
+ user, [
+ ('rule.account', '=', datas['form']['account_template']),
+ ], context=context)
+ for tax_rule_line_template in tax_rule_line_template_obj.browse(cursor,
+ user, tax_rule_line_template_ids, context=context):
+ tax_rule_line_template_obj.create_rule_line(cursor, user,
+ tax_rule_line_template, template2tax, template2rule,
+ context=context, template2rule_line=template2rule_line)
+
return {'company': datas['form']['company']}
def _action_create_properties(self, cursor, user, datas, context=None):
@@ -1862,34 +1893,45 @@ class UpdateChartAccount(Wizard):
def _action_update_account(self, cursor, user, datas, context=None):
account_type_obj = self.pool.get('account.account.type')
- account_type_template_obj = self.pool.get('account.account.type.template')
+ account_type_template_obj = \
+ self.pool.get('account.account.type.template')
account_obj = self.pool.get('account.account')
account_template_obj = self.pool.get('account.account.template')
tax_code_obj = self.pool.get('account.tax.code')
tax_code_template_obj = self.pool.get('account.tax.code.template')
tax_obj = self.pool.get('account.tax')
tax_template_obj = self.pool.get('account.tax.template')
+ tax_rule_obj = self.pool.get('account.tax.rule')
+ tax_rule_template_obj = self.pool.get('account.tax.rule.template')
+ tax_rule_line_obj = self.pool.get('account.tax.rule.line')
+ tax_rule_line_template_obj = \
+ self.pool.get('account.tax.rule.line.template')
account = account_obj.browse(cursor, user, datas['form']['account'],
context=context)
+ # Update account types
template2type = {}
account_type_obj.update_type(cursor, user, account.type,
context=context, template2type=template2type)
+ # Create missing account types
if account.type.template:
account_type_template_obj.create_type(cursor, user,
account.type.template, account.company.id, context=context,
template2type=template2type)
+ # Update accounts
template2account = {}
account_obj.update_account(cursor, user, account, context=context,
template2account=template2account, template2type=template2type)
+ # Create missing accounts
if account.template:
account_template_obj.create_account(cursor, user, account.template,
account.company.id, context=context,
template2account=template2account,
template2type=template2type)
+ # Update tax codes
template2tax_code = {}
tax_code_ids = tax_code_obj.search(cursor, user, [
('company', '=', account.company.id),
@@ -1899,11 +1941,19 @@ class UpdateChartAccount(Wizard):
context=context):
tax_code_obj.update_tax_code(cursor, user, tax_code, context=context,
template2tax_code=template2tax_code)
- if tax_code.template:
+ # Create missing tax codes
+ if account.template:
+ tax_code_template_ids = tax_code_template_obj.search(cursor, user, [
+ ('account', '=', account.template.id),
+ ('parent', '=', False),
+ ], context=context)
+ for tax_code_template in tax_code_template_obj.browse(cursor, user,
+ tax_code_template_ids, context=context):
tax_code_template_obj.create_tax_code(cursor, user,
- tax_code.template, account.company.id, context=context,
+ tax_code_template, account.company.id, context=context,
template2tax_code=template2tax_code)
+ # Update taxes
template2tax = {}
tax_ids = tax_obj.search(cursor, user, [
('company', '=', account.company.id),
@@ -1915,14 +1965,64 @@ class UpdateChartAccount(Wizard):
template2account=template2account,
context=context,
template2tax=template2tax)
- if tax.template:
- tax_template_obj.create_tax(cursor, user, tax.template,
+ # Create missing taxes
+ if account.template:
+ tax_template_ids = tax_template_obj.search(cursor, user, [
+ ('account', '=', account.template.id),
+ ('parent', '=', False),
+ ], context=context)
+ for tax_template in tax_template_obj.browse(cursor, user,
+ tax_template_ids, context=context):
+ tax_template_obj.create_tax(cursor, user, tax_template,
account.company.id, template2tax_code=template2tax_code,
template2account=template2account, context=context,
template2tax=template2tax)
+ # Update taxes on accounts
account_obj.update_account_taxes(cursor, user, account,
template2account, template2tax, context=context)
+
+ # Update tax rules
+ template2rule = {}
+ tax_rule_ids = tax_rule_obj.search(cursor, user, [
+ ('company', '=', account.company.id),
+ ], context=context)
+ for tax_rule in tax_rule_obj.browse(cursor, user, tax_rule_ids,
+ context=context):
+ tax_rule_obj.update_rule(cursor, user, tax_rule, context=context,
+ template2rule=template2rule)
+ # Create missing tax rules
+ if account.template:
+ tax_rule_template_ids = tax_rule_template_obj.search(cursor, user, [
+ ('account', '=', account.template.id),
+ ], context=context)
+ for tax_rule_template in tax_rule_template_obj.browse(cursor, user,
+ tax_rule_template_ids, context=context):
+ tax_rule_template_obj.create_rule(cursor, user,
+ tax_rule_template, account.company.id, context=context,
+ template2rule=template2rule)
+
+ # Update tax rule lines
+ template2rule_line = {}
+ tax_rule_line_ids = tax_rule_line_obj.search(cursor, user, [
+ ('rule.company', '=', account.company.id),
+ ], context=context)
+ for tax_rule_line in tax_rule_line_obj.browse(cursor, user,
+ tax_rule_line_ids, context=context):
+ tax_rule_line_obj.update_rule_line(cursor, user, tax_rule_line,
+ template2tax, template2rule, context=context,
+ template2rule_line=template2rule_line)
+ # Create missing tax rule lines
+ if account.template:
+ tax_rule_line_template_ids = tax_rule_line_template_obj.search(
+ cursor, user, [
+ ('rule.account', '=', account.template.id),
+ ], context=context)
+ for tax_rule_line_template in tax_rule_line_template_obj.browse(
+ cursor, user, tax_rule_line_template_ids, context=context):
+ tax_rule_line_template_obj.create_rule_line(cursor, user,
+ tax_rule_line_template, template2tax, template2rule,
+ context=context, template2rule_line=template2rule_line)
return {}
UpdateChartAccount()
diff --git a/party.py b/party.py
index fc339e114b..5dda938dc9 100644
--- a/party.py
+++ b/party.py
@@ -20,21 +20,18 @@ class Party(ModelSQL, ModelView):
'required': "globals().get('company') and bool(company)",
'invisible': "not globals().get('company') or not bool(company)",
})
- vat = fields.Property(type='many2one',
- relation='account.tax', string='VAT',
- domain="[('group.code', '=', 'vat'), ('company', '=', company), " \
- "('parent', '=', False)]",
+ customer_tax_rule = fields.Property(type='many2one',
+ relation='account.tax.rule', string='Customer Tax Rule',
+ domain="[('company', '=', company)]",
states={
'invisible': "not globals().get('company') or not bool(company)",
- }, help='This tax will be used, instead of the default VAT.')
- supplier_vat = fields.Property(type='many2one',
- relation='account.tax', string='Supplier VAT',
- domain="[('group.code', '=', 'vat'), ('company', '=', company), " \
- "('parent', '=', False)]",
+ }, help='Apply this rule on taxes when party is customer.')
+ supplier_tax_rule = fields.Property(type='many2one',
+ relation='account.tax.rule', string='Supplier Tax Rule',
+ domain="[('company', '=', company)]",
states={
'invisible': "not globals().get('company') or not bool(company)",
- }, help='This tax will be used, instead of the default VAT ' \
- 'for supplier invoices.')
+ }, help='Apply this rule on taxes when party is supplier.')
receivable = fields.Function('get_receivable_payable',
fnct_search='search_receivable_payable', string='Receivable')
payable = fields.Function('get_receivable_payable',
diff --git a/party.xml b/party.xml
index f1d1b2705b..2426b48229 100644
--- a/party.xml
+++ b/party.xml
@@ -44,10 +44,10 @@ this repository contains the full copyright notices and license terms. -->
-
-
-
-
+
+
+
+
]]>
diff --git a/tax.py b/tax.py
index 8b1ff22d74..7e3fa21bb4 100644
--- a/tax.py
+++ b/tax.py
@@ -57,7 +57,7 @@ class CodeTemplate(ModelSQL, ModelView):
:param template: the BrowseRecord of the template
:param context: the context
:param code: the BrowseRecord of the code to update
- :return: a dictionary with account fields as key and values as value
+ :return: a dictionary with tax code fields as key and values as value
'''
res = {}
if not code or code.name != template.name:
@@ -907,6 +907,322 @@ class Line(ModelSQL, ModelView):
Line()
+class RuleTemplate(ModelSQL, ModelView):
+ 'Tax Rule Template'
+ _name = 'account.tax.rule.template'
+ _description = __doc__
+ name = fields.Char('Name', required=True)
+ lines = fields.One2Many('account.tax.rule.line.template', 'rule', 'Lines')
+ account = fields.Many2One('account.account.template', 'Account Template',
+ domain=[('parent', '=', False)], required=True)
+
+ def _get_tax_rule_value(self, cursor, user, template, context=None,
+ rule=None):
+ '''
+ Set values for tax rule creation.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param template: the BrowseRecord of the template
+ :param context: the context
+ :param rule: the BrowseRecord of the rule to update
+ :return: a dictionary with rule fields as key and values as value
+ '''
+ res = {}
+ if not rule or rule.name != template.name:
+ res['name'] = template.name
+ if not rule or rule.template.id != template.id:
+ res['template'] = template.id
+ return res
+
+ def create_rule(self, cursor, user, template, company_id, context=None,
+ template2rule=None):
+ '''
+ Create tax rule based on template.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param template: the template id or the BrowseRecord of template
+ used for tax rule creation
+ :param company_id: the id of the company for which tax rules are
+ created
+ :param context: the context
+ :param template2rule: 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
+ '''
+ rule_obj = self.pool.get('account.tax.rule')
+
+ if template2rule is None:
+ template2rule = {}
+
+ if isinstance(template, (int, long)):
+ template = self.browse(cursor, user, template, context=context)
+
+ if template.id not in template2rule:
+ vals = self._get_tax_rule_value(cursor, user, template,
+ context=context)
+ vals['company'] = company_id
+ new_id = rule_obj.create(cursor, user, vals, context=context)
+ template2rule[template.id] = new_id
+ else:
+ new_id = template2rule[template.id]
+ return new_id
+
+RuleTemplate()
+
+
+class Rule(ModelSQL, ModelView):
+ 'Tax Rule'
+ _name = 'account.tax.rule'
+ _description = __doc__
+ name = fields.Char('Name', required=True)
+ company = fields.Many2One('company.company', 'Company', required=True,
+ select=1)
+ lines = fields.One2Many('account.tax.rule.line', 'rule', 'Lines')
+ template = fields.Many2One('account.tax.rule.template', 'Template')
+
+ def apply(self, cursor, user, rule, tax, pattern, context=None):
+ '''
+ Apply rule on tax
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param rule: a rule id or the BrowseRecord of the rule
+ :param tax: a tax id or the BrowseRecord of the tax
+ :param pattern: a dictonary with rule line field as key
+ and match value as value
+ :param context: the context
+ :return: the tax id to use
+ '''
+ tax_obj = self.pool.get('account.tax')
+ rule_line_obj = self.pool.get('account.tax.rule.line')
+
+ if isinstance(rule, (int, long)):
+ rule = self.browse(cursor, user, rule, context=context)
+
+ if isinstance(tax, (int, long)):
+ tax = tax_obj.browse(cursor, user, tax, context=context)
+
+ pattern = pattern.copy()
+ pattern['group'] = tax.group.id
+
+ for line in rule.lines:
+ if rule_line_obj.match(cursor, user, line, pattern,
+ context=context):
+ if line.tax:
+ return line.tax.id
+ return False
+ return tax.id
+
+ def update_rule(self, cursor, user, rule, context=None, template2rule=None):
+ '''
+ Update tax rule based on template.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param rule: a rule id or the BrowseRecord of the rule
+ :param context: the context
+ :param template2rule: 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
+ '''
+ template_obj = self.pool.get('account.tax.rule.template')
+
+ if template2rule is None:
+ template2rule = {}
+
+ if isinstance(rule, (int, long)):
+ rule = self.browse(cursor, user, rule, context=context)
+
+ if rule.template:
+ vals = template_obj._get_tax_rule_value(cursor, user,
+ rule.template, context=context, rule=rule)
+ if vals:
+ self.write(cursor, user, rule.id, vals, context=context)
+ template2rule[rule.template.id] = rule.id
+
+Rule()
+
+
+class RuleLineTemplate(ModelSQL, ModelView):
+ 'Tax Rule Line Template'
+ _name = 'account.tax.rule.line.template'
+ _description = __doc__
+ rule = fields.Many2One('account.tax.rule.template', 'Rule', required=True,
+ ondelete='CASCADE')
+ group = fields.Many2One('account.tax.group', 'Tax Group', required=True)
+ tax = fields.Many2One('account.tax.template', 'Substitution Tax',
+ domain="[('account', '=', _parent_rule.account), " \
+ "('group', '=', group)]")
+ sequence = fields.Integer('Sequence')
+
+ def __init__(self):
+ super(RuleLineTemplate, self).__init__()
+ self._order.insert(0, ('rule', 'ASC'))
+ self._order.insert(0, ('sequence', 'ASC'))
+
+ def _get_tax_rule_line_value(self, cursor, user, template, context=None,
+ rule_line=None):
+ '''
+ Set values for tax rule line creation.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param template: the BrowseRecord of the template
+ :param context: the context
+ :param rule_line: the BrowseRecord of the rule line to update
+ :return: a dictionary with rule line fields as key and values as value
+ '''
+ res = {}
+ if not rule_line or rule_line.group.id != template.group.id:
+ res['group'] = template.group.id
+ if not rule_line or rule_line.sequence != template.sequence:
+ res['sequence'] = template.sequence
+ if not rule_line or rule_line.template.id != template.id:
+ res['template'] = template.id
+ return res
+
+ def create_rule_line(self, cursor, user, template, template2tax,
+ template2rule, context=None, template2rule_line=None):
+ '''
+ Create tax rule line based on template.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param template: the template id or the BrowseRecord of template
+ used for tax rule line creation
+ :param template2tax: a dictionary with tax template id as key
+ and tax id as value, used to convert template id into
+ tax.
+ :param template2rule: a dictionary with tax rule template id as key
+ and tax rule id as value, used to convert template id into
+ tax rule.
+ :param context: the context
+ :param template2rule_line: 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
+ '''
+ rule_line_obj = self.pool.get('account.tax.rule.line')
+
+ if template2rule_line is None:
+ template2rule_line = {}
+
+ if isinstance(template, (int, long)):
+ template = self.browse(cursor, user, template, context=context)
+
+ if template.id not in template2rule_line:
+ vals = self._get_tax_rule_line_value(cursor, user, template,
+ context=context)
+ vals['rule'] = template2rule[template.rule.id]
+ if template.tax:
+ vals['tax'] = template2tax[template.tax.id]
+ else:
+ vals['tax'] = False
+ new_id = rule_line_obj.create(cursor, user, vals, context=context)
+ template2rule_line[template.id] = new_id
+ else:
+ new_id = template2rule_line[template.id]
+ return new_id
+
+RuleLineTemplate()
+
+
+class RuleLine(ModelSQL, ModelView):
+ 'Tax Rule Line'
+ _name = 'account.tax.rule.line'
+ _description = __doc__
+ _rec_name = 'tax'
+ rule = fields.Many2One('account.tax.rule', 'Rule', required=True,
+ select=1, ondelete='CASCADE')
+ group = fields.Many2One('account.tax.group', 'Tax Group', required=True)
+ tax = fields.Many2One('account.tax', 'Substitution Tax',
+ domain="[('company', '=', _parent_rule.company), " \
+ "('group', '=', group)]")
+ sequence = fields.Integer('Sequence')
+ template = fields.Many2One('account.tax.rule.line.template', 'Template')
+
+ def __init__(self):
+ super(RuleLine, self).__init__()
+ self._order.insert(0, ('rule', 'ASC'))
+ self._order.insert(0, ('sequence', 'ASC'))
+
+ def match(self, cursor, user, line, pattern, context=None):
+ '''
+ Match line on pattern
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param line: a BrowseRecord of rule line
+ :param pattern: a dictonary with rule line field as key
+ and match value as value
+ :param context: the context
+ :return: a boolean
+ '''
+ res = True
+ for field in pattern.keys():
+ if field not in self._columns:
+ continue
+ if not line[field]:
+ continue
+ if self._columns[field]._type == 'many2one':
+ if line[field].id != pattern[field]:
+ res = False
+ break
+ else:
+ if line[field] != pattern[field]:
+ res = False
+ break
+ return res
+
+ def update_rule_line(self, cursor, user, rule_line, template2tax,
+ template2rule, context=None, template2rule_line=None):
+ '''
+ Update tax rule line based on template.
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param rule_line: a rule line id or the BrowseRecord of the rule line
+ :param template2tax: a dictionary with tax template id as key
+ and tax id as value, used to convert template id into
+ tax.
+ :param template2rule: a dictionary with tax rule template id as key
+ and tax rule id as value, used to convert template id into
+ tax rule.
+ :param context: the context
+ :param template2rule_line: 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
+ '''
+ template_obj = self.pool.get('account.tax.rule.line.template')
+
+ if template2rule_line is None:
+ template2rule_line = {}
+
+ if isinstance(rule_line, (int, long)):
+ rule_line = self.browse(cursor, user, rule_line, context=context)
+
+ if rule_line.template:
+ vals = template_obj._get_tax_rule_line_value(cursor, user,
+ rule_line.template, context=context, rule_line=rule_line)
+ if rule_line.rule.id != template2rule[rule_line.template.rule.id]:
+ vals['rule'] = template2rule[rule_line.template.rule.id]
+ if rule_line.tax:
+ if rule_line.template.tax:
+ if rule_line.tax.id != \
+ template2tax[rule_line.template.tax.id]:
+ vals['tax'] = template2tax[rule_line.template.tax.id]
+ if vals:
+ self.write(cursor, user, rule_line.id, vals, context=context)
+ template2rule_line[rule_line.template.id] = rule_line.id
+
+RuleLine()
+
+
class OpenCode(Wizard):
'Open Code'
_name = 'account.tax.open_code'
diff --git a/tax.xml b/tax.xml
index 106822c3a2..17ee82cb8f 100644
--- a/tax.xml
+++ b/tax.xml
@@ -6,6 +6,65 @@ this repository contains the full copyright notices and license terms. -->
+
+ account.tax.group
+ form
+
+
+
+
+
+
+
+ ]]>
+
+
+
+ account.tax.group
+ tree
+
+
+
+
+
+ ]]>
+
+
+
+ Tax Groups
+ account.tax.group
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
account.tax.code.template
form
@@ -441,10 +500,6 @@ this repository contains the full copyright notices and license terms. -->
None
none
-
- VAT
- vat
-
account.tax.line
@@ -514,10 +569,233 @@ this repository contains the full copyright notices and license terms. -->
-
- vat
-
+
+ account.tax.rule.template
+ form
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+ account.tax.rule.template
+ tree
+
+
+
+
+
+ ]]>
+
+
+
+
+ Tax Rule Templates
+ account.tax.rule.template
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ account.tax.rule
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+ account.tax.rule
+ tree
+
+
+
+
+ ]]>
+
+
+
+
+ Tax Rules
+ account.tax.rule
+ form
+ [('company', '=', locals().get('company', False))]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =
+ User/Current Company
+
+
+
+
+ account.tax.rule.line.template
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+ account.tax.rule.line.template
+ tree
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ account.tax.rule.line
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+ account.tax.rule.line
+ tree
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+