From 67bfabe1b2e305618f39a9443a7b0459599bac71 Mon Sep 17 00:00:00 2001
From: ced <>
Date: Fri, 11 May 2007 11:52:16 +0000
Subject: [PATCH 1/1] ACCOUNT,ACCOUNT_TAX_INCLUDE: improve invoice encoding
(supplier invoice, default value, changing taxes,
etc...)
bzr revid: ced-15fcc3c1b60f5ded8d0fd8a501061cb05a8fa249
---
addons/account/account.py | 70 ++++++-
addons/account/account_invoice_view.xml | 128 ++++++------
addons/account/invoice.py | 249 +++++++++++++++--------
addons/account_tax_include/__init__.py | 1 -
addons/account_tax_include/account.py | 108 ----------
addons/account_tax_include/invoice_tax_incl.py | 203 +++++++-----------
addons/account_tax_include/invoice_tax_incl.xml | 38 +++-
7 files changed, 413 insertions(+), 384 deletions(-)
delete mode 100644 addons/account_tax_include/account.py
diff --git a/addons/account/account.py b/addons/account/account.py
index cf0b0cc..656f5e4 100644
--- a/addons/account/account.py
+++ b/addons/account/account.py
@@ -845,6 +845,7 @@ class account_tax(osv.osv):
'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'),
'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."),
'python_compute':fields.text('Python Code'),
+ 'python_compute_inv':fields.text('Python Code (reverse)'),
'python_applicable':fields.text('Python Code'),
'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."),
@@ -873,9 +874,10 @@ class account_tax(osv.osv):
return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
_defaults = {
'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''',
+ 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''',
'applicable_type': lambda *a: 'true',
'type': lambda *a: 'percent',
- 'amount': lambda *a: 0.196,
+ 'amount': lambda *a: 0,
'active': lambda *a: 1,
'sequence': lambda *a: 1,
'tax_group': lambda *a: 'vat',
@@ -948,7 +950,6 @@ class account_tax(osv.osv):
cur_price_unit+=amount2
return res
-
def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
"""
@@ -963,6 +964,71 @@ class account_tax(osv.osv):
for r in res:
r['amount'] *= quantity
return res
+
+ def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
+ taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
+
+ res = []
+ taxes.reverse()
+ cur_price_unit=price_unit
+ for tax in taxes:
+ # we compute the amount for the current tax object and append it to the result
+ if tax.type=='percent':
+ amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
+ res.append({'id':tax.id, 'name':tax.name, 'amount':amount, 'account_collected_id':tax.account_collected_id.id, 'account_paid_id':tax.account_paid_id.id, 'base_code_id': tax.base_code_id.id, 'ref_base_code_id': tax.ref_base_code_id.id, 'sequence': tax.sequence, 'base_sign': tax.base_sign, 'tax_sign': tax.tax_sign, 'ref_base_sign': tax.ref_base_sign, 'ref_tax_sign': tax.ref_tax_sign, 'price_unit': cur_price_unit - amount, 'tax_code_id': tax.tax_code_id.id, 'ref_tax_code_id': tax.ref_tax_code_id.id,})
+ elif tax.type=='fixed':
+ res.append({'id':tax.id, 'name':tax.name, 'amount':tax.amount, 'account_collected_id':tax.account_collected_id.id, 'account_paid_id':tax.account_paid_id.id, 'base_code_id': tax.base_code_id.id, 'ref_base_code_id': tax.ref_base_code_id.id, 'sequence': tax.sequence, 'base_sign': tax.base_sign, 'tax_sign': tax.tax_sign, 'ref_base_sign': tax.ref_base_sign, 'ref_tax_sign': tax.ref_tax_sign, 'price_unit': 1, 'tax_code_id': tax.tax_code_id.id, 'ref_tax_code_id': tax.ref_tax_code_id.id,})
+ elif tax.type=='code':
+ address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
+ localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
+ exec tax.python_compute_inv in localdict
+ amount = localdict['result']
+ res.append({
+ 'id': tax.id,
+ 'name': tax.name,
+ 'amount': amount,
+ 'account_collected_id': tax.account_collected_id.id,
+ 'account_paid_id': tax.account_paid_id.id,
+ 'base_code_id': tax.base_code_id.id,
+ 'ref_base_code_id': tax.ref_base_code_id.id,
+ 'sequence': tax.sequence,
+ 'base_sign': tax.base_sign,
+ 'tax_sign': tax.tax_sign,
+ 'ref_base_sign': tax.ref_base_sign,
+ 'ref_tax_sign': tax.ref_tax_sign,
+ 'price_unit': cur_price_unit - amount,
+ 'tax_code_id': tax.tax_code_id.id,
+ 'ref_tax_code_id': tax.ref_tax_code_id.id,
+ })
+ amount2 = res[-1]['amount']
+ if len(tax.child_ids):
+ if tax.child_depend:
+ del res[-1]
+ amount = amount2
+ else:
+ amount = amount2
+ for t in tax.child_ids:
+ parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id)
+ res.extend(parent_tax)
+ if tax.include_base_amount:
+ cur_price_unit-=amount
+ taxes.reverse()
+ return res
+
+ def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
+ """
+ Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
+ Price Unit is a VAT included price
+
+ RETURN:
+ [ tax ]
+ tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
+ one tax for each tax id in IDS and their childs
+ """
+ res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
+ for r in res:
+ r['amount'] *= quantity
+ return res
account_tax()
# ---------------------------------------------------------
diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml
index 318e0bc..f5705fb 100644
--- a/addons/account/account_invoice_view.xml
+++ b/addons/account/account_invoice_view.xml
@@ -88,7 +88,7 @@
-
+
@@ -120,29 +120,30 @@
account.invoice.supplier.form
- account.invoice.supplier
+ account.invoice
form
+ 2
-
+
account.invoice
@@ -362,7 +370,7 @@
[('state','=','open'),('type','=','in_invoice')]
{'type':'in_invoice'}
-
+
account.invoice
@@ -372,7 +380,7 @@
[('state','=','draft'),('type','=','out_refund')]
{'type':'out_refund'}
-
+
account.invoice
@@ -382,7 +390,7 @@
[('state','=','open'),('type','=','out_refund')]
{'type':'out_refund'}
-
+
account.invoice
@@ -392,7 +400,7 @@
[('state','=','draft'),('type','=','in_refund')]
{'type':'in_refund'}
-
+
account.invoice
@@ -402,7 +410,7 @@
[('state','=','open'),('type','=','in_refund')]
{'type':'in_refund'}
-
+
diff --git a/addons/account/invoice.py b/addons/account/invoice.py
index 523ca18..c64ba815 100644
--- a/addons/account/invoice.py
+++ b/addons/account/invoice.py
@@ -36,22 +36,24 @@ import pooler
import mx.DateTime
from mx.DateTime import RelativeDateTime
+from tools import config
+
class account_invoice(osv.osv):
- def _amount_untaxed(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_untaxed(self, cr, uid, ids, name, args, context={}):
id_set=",".join(map(str,ids))
cr.execute("SELECT s.id,COALESCE(SUM(l.price_unit*l.quantity*(100-l.discount))/100.0,0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_line l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ")
res=dict(cr.fetchall())
return res
- def _amount_tax(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_tax(self, cr, uid, ids, name, args, context={}):
id_set=",".join(map(str,ids))
cr.execute("SELECT s.id,COALESCE(SUM(l.amount),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_tax l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ")
res=dict(cr.fetchall())
return res
- def _amount_total(self, cr, uid, ids, prop, unknow_none,unknow_dict):
- untax = self._amount_untaxed(cr, uid, ids, prop, unknow_none,unknow_dict)
- tax = self._amount_tax(cr, uid, ids, prop, unknow_none,unknow_dict)
+ def _amount_total(self, cr, uid, ids, name, args, context={}):
+ untax = self._amount_untaxed(cr, uid, ids, name, args, context)
+ tax = self._amount_tax(cr, uid, ids, name, args, context)
res = {}
for id in ids:
res[id] = untax.get(id,0.0) + tax.get(id,0.0)
@@ -94,7 +96,7 @@ class account_invoice(osv.osv):
('in_invoice','Supplier Invoice'),
('out_refund','Customer Refund'),
('in_refund','Supplier Refund'),
- ],'Type', readonly=True, states={'draft':[('readonly',False)]}, select=True),
+ ],'Type', readonly=True, states={'draft':[('readonly', False)]}, select=True),
'number': fields.char('Invoice Number', size=32, readonly=True),
'reference': fields.char('Invoice Reference', size=64),
@@ -116,9 +118,6 @@ class account_invoice(osv.osv):
'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
- 'partner_contact': fields.char('Partner Contact', size=64),
- 'partner_ref': fields.char('Partner Reference', size=64),
-
'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]} ),
'period_id': fields.many2one('account.period', 'Force Period', help="Keep empty to use the period of the validation date."),
@@ -134,7 +133,7 @@ class account_invoice(osv.osv):
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Journal', required=True, relate=True,readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=True),
-
+ 'check_total': fields.float('Total', digits=(16,2)),
}
_defaults = {
'type': lambda *a: 'out_invoice',
@@ -212,6 +211,9 @@ class account_invoice(osv.osv):
return res
+ def onchange_invoice_line(self, cr, uid, ids, lines):
+ return {}
+
# go from canceled state to draft state
def action_cancel_draft(self, cr, uid, ids, *args):
self.write(cr, uid, ids, {'state':'draft'})
@@ -251,18 +253,47 @@ class account_invoice(osv.osv):
ok = ok and bool(cr.fetchone()[0])
return ok
- def button_compute(self, cr, uid, ids, context={}):
+ def button_reset_taxes(self, cr, uid, ids, context={}):
+ ait_obj = self.pool.get('account.invoice.tax')
for id in ids:
- self.pool.get('account.invoice.line').move_line_get(cr, uid, id)
+ cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%d", (id,))
+ for taxe in ait_obj.compute(cr, uid, id).values():
+ ait_obj.create(cr, uid, taxe)
+ return True
+
+ def button_compute(self, cr, uid, ids, context={}):
+ ait_obj = self.pool.get('account.invoice.tax')
+ for inv in self.browse(cr, uid, ids):
+ compute_taxes = ait_obj.compute(cr, uid, inv.id)
+ if not inv.tax_line:
+ for tax in compute_taxes.values():
+ ait_obj.create(cr, uid, tax)
+ else:
+ tax_key = []
+ for tax in inv.tax_line:
+ if tax.manual:
+ continue
+ key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
+ tax_key.append(key)
+ if not key in compute_taxes:
+ ait_obj.unlink(cr, uid, [tax.id])
+ continue
+ if compute_taxes[key]['base'] != tax.base:
+ ait_obj.write(cr, uid, [tax.id], compute_taxes[key])
+ for key in compute_taxes:
+ if not key in tax_key:
+ ait_obj.create(cr, uid, compute_taxes[key])
return True
def action_move_create(self, cr, uid, ids, *args):
+ ait_obj = self.pool.get('account.invoice.tax')
cur_obj = self.pool.get('res.currency')
for inv in self.browse(cr, uid, ids):
if inv.move_id:
continue
-
- company_currency = inv.account_id.company_id.currency_id.id
+ if inv.type in ('in_invoice', 'in_refund') and not inv.check_total == inv.amount_total:
+ raise osv.except_osv('Bad total !', 'Please verify the price of the invoice !\nThe real total does not match the computed total.')
+ company_currency = inv.company_id.currency_id.id
# create the analytical lines
line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
ils = self.pool.get('account.invoice.line').read(cr, uid, line_ids)
@@ -283,11 +314,33 @@ class account_invoice(osv.osv):
'product_id': il['product_id'],
'product_uom_id': il['uos_id'],
'general_account_id': il['account_id'],
- 'journal_id': self._get_journal_analytic(cr, uid, inv.type)
+ 'journal_id': self._get_journal_analytic(cr, uid, inv.type),
+ 'ref': inv['number'],
})]
+ # check if taxes are all computed
+ compute_taxes = ait_obj.compute(cr, uid, inv.id)
+ if not inv.tax_line:
+ for tax in compute_taxes.values():
+ ait_obj.create(cr, uid, tax)
+ else:
+ tax_key = []
+ for tax in inv.tax_line:
+ if tax.manual:
+ continue
+ key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
+ tax_key.append(key)
+ if not key in compute_taxes:
+ raise osv.except_osv('Warning !', 'Too much taxes !')
+ base = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, compute_taxes[key]['base'], context={'date': inv.date_invoice})
+ if abs(base - tax.base) > inv.company_id.currency_id.rounding:
+ raise osv.except_osv('Warning !', 'Base different !')
+ for key in compute_taxes:
+ if not key in tax_key:
+ raise osv.except_osv('Warning !', 'Taxes missing !')
+
# one move line per tax line
- iml += self.pool.get('account.invoice.tax').move_line_get(cr, uid, inv.id)
+ iml += ait_obj.move_line_get(cr, uid, inv.id)
# create one move line for the total and possibly adjust the other lines amount
@@ -519,30 +572,29 @@ class account_invoice(osv.osv):
return True
account_invoice()
-class account_invoice_supplier(osv.osv):
- _name = "account.invoice.supplier"
- _description = "Supplier invoice"
- _inherits = {'account.invoice': 'account_invoice_id'}
- _columns = {
- 'account_invoice_id': fields.many2one('account.invoice', 'Account invoice', required=True),
- 'check_total': fields.float('Total', digits=(16,2)),
- }
-
- def action_move_create(self, cr, uid, ids, context={}):
- for inv in self.browse(cr, uid, ids):
- if inv.move_id:
- continue
- if inv.check_total <> inv.amount_total:
- raise osv.except_osv('Bad total !', 'Please verify the price of the invoice !\nThe real total does not match the computed total.')
- return super(account_invoice_supplier, self).action_move_create(cr, uid, ids, context=context)
-account_invoice_supplier()
-
class account_invoice_line(osv.osv):
def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
res = {}
for line in self.browse(cr, uid, ids):
res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),2)
return res
+
+ def _price_unit_default(self, cr, uid, context={}):
+ if 'check_total' in context:
+ t = context['check_total']
+ for l in context.get('invoice_line', {}):
+ if len(l) >= 3 and l[2]:
+ tax_obj = self.pool.get('account.tax')
+ p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
+ t = t - (p * l[2].get('quantity'))
+ taxes = l[2].get('invoice_line_tax_id')
+ if len(taxes[0]) >= 3 and taxes[0][2]:
+ taxes=tax_obj.browse(cr, uid, taxes[0][2])
+ for tax in tax_obj.compute(cr, uid, taxes, p,l[2].get('quantity'), context.get('address_invoice_id', False), l[2].get('product_id', False), context.get('partner_id', False)):
+ t = t - tax['amount']
+ return t
+ return 0
+
_name = "account.invoice.line"
_description = "Invoice line"
_columns = {
@@ -551,7 +603,7 @@ class account_invoice_line(osv.osv):
'uos_id': fields.many2one('product.uom', 'Unit', ondelete='set null'),
'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
'account_id': fields.many2one('account.account', 'Source Account', required=True, domain=[('type','<>','view')]),
- 'price_unit': fields.float('Unit Price', required=True),
+ 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'),
'quantity': fields.float('Quantity', required=True),
'discount': fields.float('Discount (%)', digits=(16,2)),
@@ -561,24 +613,38 @@ class account_invoice_line(osv.osv):
}
_defaults = {
'quantity': lambda *a: 1,
- 'discount': lambda *a: 0.0
+ 'discount': lambda *a: 0.0,
+ 'price_unit': _price_unit_default,
}
- def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False):
+
+ def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}):
+ tax_obj = self.pool.get('account.tax')
+ if price_unit:
+ taxes = tax_obj.browse(cr, uid, tax_id)
+ for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id):
+ price_unit = price_unit - tax['amount']
+ return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
+
+ def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, context={}):
if not product:
- return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
+ if type in ('in_invoice', 'in_refund'):
+ return {'domain':{'product_uom':[]}}
+ else:
+ return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
lang=False
- context={'lang': lang}
+ context.update({'lang': lang})
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
taxep=None
if partner_id:
lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang']
taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax
+ tax_obj = self.pool.get('account.tax')
if type in ('out_invoice', 'out_refund'):
if not taxep or not taxep[0]:
tax_id = map(lambda x: x.id, res.taxes_id)
else:
tax_id = [taxep[0]]
- tp = self.pool.get('account.tax').browse(cr, uid, taxep[0])
+ tp = tax_obj.browse(cr, uid, taxep[0])
for t in res.taxes_id:
if not t.tax_group==tp.tax_group:
tax_id.append(t.id)
@@ -587,11 +653,14 @@ class account_invoice_line(osv.osv):
tax_id = map(lambda x: x.id, res.supplier_taxes_id)
else:
tax_id = [taxep[0]]
- tp = self.pool.get('account.tax').browse(cr, uid, taxep[0])
+ tp = tax_obj.browse(cr, uid, taxep[0])
for t in res.supplier_taxes_id:
if not t.tax_group==tp.tax_group:
tax_id.append(t.id)
- result = {'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}
+ if type in ('in_invoice', 'in_refund'):
+ result = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context)
+ else:
+ result = {'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}
if not name:
result['name'] = res.name
@@ -600,11 +669,11 @@ class account_invoice_line(osv.osv):
a = res.product_tmpl_id.property_account_income
if not a:
a = res.categ_id.property_account_income_categ
- result['account_id'] = a[0]
else:
a = res.product_tmpl_id.property_account_expense
if not a:
a = res.categ_id.property_account_expense_categ
+ if a:
result['account_id'] = a[0]
domain = {}
@@ -626,56 +695,23 @@ class account_invoice_line(osv.osv):
for line in inv.invoice_line:
res.append( {
- 'type':'src',
- 'name':line.name,
- 'price_unit':line.price_unit,
- 'quantity':line.quantity,
- 'price':cur_obj.round(cr, uid, cur, line.quantity*line.price_unit * (1.0- (line.discount or 0.0)/100.0)),
+ 'type':'src',
+ 'name':line.name,
+ 'price_unit':line.price_unit,
+ 'quantity':line.quantity,
+ 'price':line.price_subtotal,
'account_id':line.account_id.id,
'product_id':line.product_id.id,
'uos_id':line.uos_id.id,
'account_analytic_id':line.account_analytic_id.id,
})
for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit *(1.0-(line['discount'] or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id):
- val={}
- val['invoice_id'] = inv.id
- val['name'] = tax['name']
- val['amount'] = cur_obj.round(cr, uid, cur, tax['amount'])
- val['manual'] = False
- val['sequence'] = tax['sequence']
- val['base'] = tax['price_unit'] * line['quantity']
-
- #
- # Setting the tax account and amount for the line
- #
- if inv.type in ('out_invoice','in_invoice'):
- val['base_code_id'] = tax['base_code_id']
- val['tax_code_id'] = tax['tax_code_id']
- val['base_amount'] = val['base'] * tax['base_sign']
- val['tax_amount'] = val['amount'] * tax['tax_sign']
- val['account_id'] = tax['account_collected_id'] or line.account_id.id
- else:
- val['base_code_id'] = tax['ref_base_code_id']
- val['tax_code_id'] = tax['ref_tax_code_id']
- val['base_amount'] = val['base'] * tax['ref_base_sign']
- val['tax_amount'] = val['amount'] * tax['ref_tax_sign']
- val['account_id'] = tax['account_paid_id'] or line.account_id.id
-
- res[-1]['tax_code_id'] = val['base_code_id']
- res[-1]['tax_amount'] = val['base_amount']
-
- key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
- if not key in tax_grouped:
- tax_grouped[key] = val
+ if inv.type in ('out_invoice', 'in_invoice'):
+ res[-1]['tax_code_id'] = tax['base_code_id']
+ res[-1]['tax_amount'] = tax['price_unit'] * line['quantity'] * tax['base_sign']
else:
- tax_grouped[key]['amount'] += val['amount']
- tax_grouped[key]['base'] += val['base']
- tax_grouped[key]['base_amount'] += val['base_amount']
- tax_grouped[key]['tax_amount'] += val['tax_amount']
- # delete automatic tax lines for this invoice
- cr.execute("DELETE FROM account_invoice_tax WHERE NOT manual AND invoice_id=%d", (invoice_id,))
- for t in tax_grouped.values():
- ait_obj.create(cr, uid, t)
+ res[-1]['tax_code_id'] = tax['ref_base_code_id']
+ res[-1]['tax_amount'] = tax['price_unit'] * line['quantity'] * tax['ref_base_sign']
return res
#
@@ -724,6 +760,47 @@ class account_invoice_tax(osv.osv):
'base_amount': lambda *a: 0.0,
'tax_amount': lambda *a: 0.0,
}
+ def compute(self, cr, uid, invoice_id):
+ tax_grouped = {}
+ tax_obj = self.pool.get('account.tax')
+ cur_obj = self.pool.get('res.currency')
+ inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
+ cur = inv.currency_id
+
+ for line in inv.invoice_line:
+ for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, line.price_subtotal, line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id):
+ val={}
+ val['invoice_id'] = inv.id
+ val['name'] = tax['name']
+ val['amount'] = cur_obj.round(cr, uid, cur, tax['amount'])
+ val['manual'] = False
+ val['sequence'] = tax['sequence']
+ val['base'] = tax['price_unit'] * line['quantity']
+
+ if inv.type in ('out_invoice','in_invoice'):
+ val['base_code_id'] = tax['base_code_id']
+ val['tax_code_id'] = tax['tax_code_id']
+ val['base_amount'] = val['base'] * tax['base_sign']
+ val['tax_amount'] = val['amount'] * tax['tax_sign']
+ val['account_id'] = tax['account_collected_id'] or line.account_id.id
+ else:
+ val['base_code_id'] = tax['ref_base_code_id']
+ val['tax_code_id'] = tax['ref_tax_code_id']
+ val['base_amount'] = val['base'] * tax['ref_base_sign']
+ val['tax_amount'] = val['amount'] * tax['ref_tax_sign']
+ val['account_id'] = tax['account_paid_id'] or line.account_id.id
+
+ key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
+ if not key in tax_grouped:
+ tax_grouped[key] = val
+ else:
+ tax_grouped[key]['amount'] += val['amount']
+ tax_grouped[key]['base'] += val['base']
+ tax_grouped[key]['base_amount'] += val['base_amount']
+ tax_grouped[key]['tax_amount'] += val['tax_amount']
+
+ return tax_grouped
+
def move_line_get(self, cr, uid, invoice_id):
res = []
cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%d', (invoice_id,))
diff --git a/addons/account_tax_include/__init__.py b/addons/account_tax_include/__init__.py
index f40f9bb..49b7b14 100644
--- a/addons/account_tax_include/__init__.py
+++ b/addons/account_tax_include/__init__.py
@@ -27,4 +27,3 @@
##############################################################################
import invoice_tax_incl
-import account
diff --git a/addons/account_tax_include/account.py b/addons/account_tax_include/account.py
deleted file mode 100644
index a0a1559..0000000
--- a/addons/account_tax_include/account.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- encoding: utf-8 -*-
-##############################################################################
-#
-# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
-#
-# $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
-#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-##############################################################################
-
-import netsvc
-from osv import fields, osv
-
-class account_tax(osv.osv):
- _inherit = 'account.tax'
- _description = 'Tax'
- _columns = {
- 'python_compute_inv':fields.text('Python Code (VAT Incl)'),
- }
- _defaults = {
- 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''',
- }
- def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None):
- taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner)
-
- res = []
- taxes.reverse()
- cur_price_unit=price_unit
- for tax in taxes:
- # we compute the amount for the current tax object and append it to the result
- if tax.type=='percent':
- amount = cur_price_unit - (cur_price_unit / (1 + tax.amount))
- res.append({'id':tax.id, 'name':tax.name, 'amount':amount, 'account_collected_id':tax.account_collected_id.id, 'account_paid_id':tax.account_paid_id.id, 'base_code_id': tax.base_code_id.id, 'ref_base_code_id': tax.ref_base_code_id.id, 'sequence': tax.sequence, 'base_sign': tax.base_sign, 'tax_sign': tax.tax_sign, 'ref_base_sign': tax.ref_base_sign, 'ref_tax_sign': tax.ref_tax_sign, 'price_unit': cur_price_unit - amount, 'tax_code_id': tax.tax_code_id.id, 'ref_tax_code_id': tax.ref_tax_code_id.id,})
- elif tax.type=='fixed':
- res.append({'id':tax.id, 'name':tax.name, 'amount':tax.amount, 'account_collected_id':tax.account_collected_id.id, 'account_paid_id':tax.account_paid_id.id, 'base_code_id': tax.base_code_id.id, 'ref_base_code_id': tax.ref_base_code_id.id, 'sequence': tax.sequence, 'base_sign': tax.base_sign, 'tax_sign': tax.tax_sign, 'ref_base_sign': tax.ref_base_sign, 'ref_tax_sign': tax.ref_tax_sign, 'price_unit': 1, 'tax_code_id': tax.tax_code_id.id, 'ref_tax_code_id': tax.ref_tax_code_id.id,})
- elif tax.type=='code':
- address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None
- localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner}
- exec tax.python_compute_inv in localdict
- amount = localdict['result']
- res.append({
- 'id': tax.id,
- 'name': tax.name,
- 'amount': amount,
- 'account_collected_id': tax.account_collected_id.id,
- 'account_paid_id': tax.account_paid_id.id,
- 'base_code_id': tax.base_code_id.id,
- 'ref_base_code_id': tax.ref_base_code_id.id,
- 'sequence': tax.sequence,
- 'base_sign': tax.base_sign,
- 'tax_sign': tax.tax_sign,
- 'ref_base_sign': tax.ref_base_sign,
- 'ref_tax_sign': tax.ref_tax_sign,
- 'price_unit': cur_price_unit - amount,
- 'tax_code_id': tax.tax_code_id.id,
- 'ref_tax_code_id': tax.ref_tax_code_id.id,
- })
- amount2 = res[-1]['amount']
- if len(tax.child_ids):
- if tax.child_depend:
- del res[-1]
- amount = amount2
- else:
- amount = amount2
- for t in tax.child_ids:
- parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id)
- res.extend(parent_tax)
- if tax.include_base_amount:
- cur_price_unit-=amount
- taxes.reverse()
- return res
-
- def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None):
- """
- Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID.
- Price Unit is a VAT included price
-
- RETURN:
- [ tax ]
- tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2}
- one tax for each tax id in IDS and their childs
- """
- res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None)
- for r in res:
- r['amount'] *= quantity
- return res
-account_tax()
-
diff --git a/addons/account_tax_include/invoice_tax_incl.py b/addons/account_tax_include/invoice_tax_incl.py
index e9eb8dd..7b9648a 100644
--- a/addons/account_tax_include/invoice_tax_incl.py
+++ b/addons/account_tax_include/invoice_tax_incl.py
@@ -34,33 +34,39 @@ from osv import fields, osv
import ir
class account_invoice(osv.osv):
- def _amount_untaxed(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_untaxed(self, cr, uid, ids, name, args, context={}):
res = {}
for invoice in self.browse(cr,uid,ids):
- res[invoice.id]= reduce( lambda x, y: x+y.price_subtotal,
- invoice.invoice_line,0)
+ if invoice.price_type == 'tax_included':
+ res[invoice.id]= invoice.amount_total - invoice.amount_tax
+ else:
+ res[invoice.id] = super(account_invoice, self)._amount_untaxed(cr, uid, [invoice.id], name, args, context)[invoice.id]
return res
- def _amount_total(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_tax(self, cr, uid, ids, name, args, context={}):
res = {}
for invoice in self.browse(cr,uid,ids):
- res[invoice.id]= reduce( lambda x, y: x+y.price_subtotal_incl,
- invoice.invoice_line,0)
+ if invoice.price_type == 'tax_included':
+ res[invoice.id] = reduce( lambda x, y: x+y.amount, invoice.tax_line,0)
+ else:
+ res[invoice.id] = super(account_invoice, self)._amount_tax(cr, uid, [invoice.id], name, args, context)[invoice.id]
return res
- def _amount_tax(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_total(self, cr, uid, ids, name, args, context={}):
res = {}
for invoice in self.browse(cr,uid,ids):
- res[invoice.id]= reduce( lambda x, y: x+y.amount,
- invoice.tax_line,0)
+ if invoice.price_type == 'tax_included':
+ res[invoice.id]= reduce( lambda x, y: x+y.price_subtotal_incl, invoice.invoice_line,0)
+ else:
+ res[invoice.id] = super(account_invoice, self)._amount_total(cr, uid, [invoice.id], name, args, context)[invoice.id]
return res
_inherit = "account.invoice"
_columns = {
'price_type': fields.selection([('tax_included','Tax included'),
('tax_excluded','Tax excluded')],
- 'Price method', required=True, readonly=True,
- states={'draft':[('readonly',False)]}),
+ 'Price method', required=True, readonly=True,
+ states={'draft':[('readonly',False)]}),
'amount_untaxed': fields.function(_amount_untaxed, digits=(16,2), method=True,string='Untaxed Amount'),
'amount_tax': fields.function(_amount_tax, method=True, string='Tax'),
'amount_total': fields.function(_amount_total, method=True, string='Total'),
@@ -72,66 +78,55 @@ account_invoice()
class account_invoice_line(osv.osv):
_inherit = "account.invoice.line"
- def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
+ def _amount_line(self, cr, uid, ids, name, args, context={}):
"""
Return the subtotal excluding taxes with respect to price_type.
"""
- #cur_obj = self.pool.get('res.currency')
- cur = False
- res = {}
- tax_obj = self.pool.get('account.tax')
- for line in self.browse(cr, uid, ids):
- res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
-
- if line.product_id and line.invoice_id.price_type == 'tax_included':
- taxes = tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
- res[line.id],
- line.quantity)
- amount = 0
- for t in taxes : amount = amount + t['amount']
- cur = cur or line.invoice_id.currency_id
- res[line.id]= cur.round(cr, uid, cur, res[line.id] - amount)
- return res
-
- def _amount_line_incl(self, cr, uid, ids, prop, unknow_none,unknow_dict):
- """
- Return the subtotal including taxes with respect to price_type.
- """
res = {}
- cur = False
tax_obj = self.pool.get('account.tax')
+ res = super(account_invoice_line, self)._amount_line(cr, uid, ids, name, args, context)
+ res2 = res.copy()
for line in self.browse(cr, uid, ids):
- res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
- if line.product_id:
- prod_taxe_ids = line.product_id and [t.id for t in line.product_id.taxes_id ] or []
- prod_taxe_ids.sort()
- line_taxe_ids = [ t.id for t in line.invoice_line_tax_id if t]
- line_taxe_ids.sort()
- if prod_taxe_ids == line_taxe_ids :
- continue
- else : continue
-
- res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
- if line.invoice_id.price_type == 'tax_included':
- # remove product taxes
- taxes = tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
- res[line.id],
- line.quantity)
- amount = 0
- for t in taxes : amount = amount + t['amount']
- res[line.id]= res[line.id] - amount
- ## Add line taxes
- taxes = tax_obj.compute(cr, uid,line.invoice_line_tax_id, res[line.id], line.quantity)
- amount = 0
- for t in taxes : amount = amount + t['amount']
- cur = cur or line.invoice_id.currency_id
- res[line.id]= cur.round(cr, uid, cur, res[line.id] + amount)
+ if line.invoice_id.price_type == 'tax_included':
+ if line.product_id:
+ for tax in tax_obj.compute_inv(cr, uid,line.product_id.taxes_id, res[line.id], line.quantity):
+ res[line.id] = res[line.id] - tax['amount']
+ else:
+ for tax in tax_obj.compute_inv(cr, uid,line.invoice_line_tax_id, res[line.id], line.quantity):
+ res[line.id] = res[line.id] - tax['amount']
+ if name == 'price_subtotal_incl':
+ if line.product_id and line.invoice_id.price_type == 'tax_included':
+ prod_taxe_ids = [ t.id for t in line.product_id.taxes_id ]
+ prod_taxe_ids.sort()
+ line_taxe_ids = [ t.id for t in line.invoice_line_tax_id ]
+ line_taxe_ids.sort()
+ if line.product_id and line.invoice_id.price_type == 'tax_included' and prod_taxe_ids == line_taxe_ids:
+ res[line.id] = res2[line.id]
+ else:
+ for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, res[line.id], line.quantity):
+ res[line.id] = res[line.id] + tax['amount']
+ res[line.id]= round(res[line.id], 2)
return res
+ def _price_unit_default(self, cr, uid, context={}):
+ if 'check_total' in context:
+ t = context['check_total']
+ if context.get('price_type', False) == 'tax_included':
+ for l in context.get('invoice_line', {}):
+ if len(l) >= 3 and l[2]:
+ p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
+ t = t - (p * l[2].get('quantity'))
+ return t
+ return super(account_invoice_line, self)._price_unit_default(cr, uid, context)
+ return 0
_columns = {
- 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o vat'),
- 'price_subtotal_incl': fields.function(_amount_line_incl, method=True, string='Subtotal'),
+ 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o tax'),
+ 'price_subtotal_incl': fields.function(_amount_line, method=True, string='Subtotal'),
+ }
+
+ _defaults = {
+ 'price_unit': _price_unit_default,
}
#
@@ -148,79 +143,35 @@ class account_invoice_line(osv.osv):
cur_obj = self.pool.get('res.currency')
ait_obj = self.pool.get('account.invoice.tax')
cur = inv.currency_id
- for line in inv.invoice_line:
- price_unit = line.price_unit
- if line.product_id:
- prod_taxe_ids = [ t.id for t in line.product_id.taxes_id ]
- prod_taxe_ids.sort()
- line_taxe_ids = [ t.id for t in line.invoice_line_tax_id]
- line_taxe_ids.sort()
- if line.product_id and prod_taxe_ids != line_taxe_ids :
- price_unit= reduce( lambda x, y: x-y['amount'],
- tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
- line.price_unit * (1-(line.discount or 0.0)/100.0), line.quantity),
- price_unit)
- taxes =tax_obj.compute(cr, uid, line.invoice_line_tax_id,
- (price_unit *(1.0-(line['discount'] or 0.0)/100.0)),
- line.quantity, inv.address_invoice_id.id)
- else:
- taxes= tax_obj.compute_inv(cr, uid, line.invoice_line_tax_id,
- (line.price_unit *(1.0-(line['discount'] or 0.0)/100.0)),
- line.quantity, inv.address_invoice_id.id)
+ for line in inv.invoice_line:
res.append( {
- 'type':'src',
- 'name':line.name,
- 'price_unit':price_unit,
- 'quantity':line.quantity,
- 'price':line.quantity*price_unit * (1.0- (line.discount or 0.0)/100.0),
+ 'type':'src',
+ 'name':line.name,
+ 'price_unit':line.price_unit,
+ 'quantity':line.quantity,
+ 'price':line.price_subtotal,
'account_id':line.account_id.id,
'product_id': line.product_id.id,
'uos_id':line.uos_id.id,
'account_analytic_id':line.account_analytic_id.id,
})
- for tax in taxes:
- val={}
- val['invoice_id'] = inv.id
- val['name'] = tax['name']
- val['amount'] = cur_obj.round(cr, uid, cur, tax['amount'])
- val['manual'] = False
- val['sequence'] = tax['sequence']
- val['base'] = tax['price_unit'] * line['quantity']
-
- res[-1]['price']-=tax['amount']
-
- #
- # Setting the tax account and amount for the line
- #
- if inv.type in ('out_invoice','in_invoice'):
- val['base_code_id'] = tax['base_code_id']
- val['tax_code_id'] = tax['tax_code_id']
- val['base_amount'] = val['base'] * tax['base_sign']
- val['tax_amount'] = val['amount'] * tax['tax_sign']
- val['account_id'] = tax['account_collected_id'] or line.account_id.id
+ for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit *(1.0-(line['discount'] or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id):
+ if inv.type in ('out_invoice', 'in_invoice'):
+ res[-1]['tax_code_id'] = tax['base_code_id']
+ res[-1]['tax_amount'] = tax['price_unit'] * line['quantity'] * tax['base_sign']
else:
- val['base_code_id'] = tax['ref_base_code_id']
- val['tax_code_id'] = tax['ref_tax_code_id']
- val['base_amount'] = val['base'] * tax['ref_base_sign']
- val['tax_amount'] = val['amount'] * tax['ref_tax_sign']
- val['account_id'] = tax['account_paid_id'] or line.account_id.id
+ res[-1]['ta_code_id'] = tax['ref_base_code_id']
+ res[-1]['tax_amount'] = tax['price_unit'] * line['quantity'] * tax['ref_base_sign']
+ return res
- res[-1]['tax_code_id'] = val['base_code_id']
- res[-1]['tax_amount'] = val['base_amount']
+ def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}):
+ if context.get('price_type', False) == 'tax_included':
+ return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
+ else:
+ return super(account_invoice_line, self).product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context)
- key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
- if not key in tax_grouped:
- tax_grouped[key] = val
- else:
- tax_grouped[key]['amount'] += val['amount']
- tax_grouped[key]['base'] += val['base']
- tax_grouped[key]['base_amount'] += val['base_amount']
- tax_grouped[key]['tax_amount'] += val['tax_amount']
- res[-1]['price']=cur_obj.round(cr, uid, cur, res[-1]['price'])
- # delete automatic tax lines for this invoice
- cr.execute("DELETE FROM account_invoice_tax WHERE NOT manual AND invoice_id=%d", (invoice_id,))
- for t in tax_grouped.values():
- ait_obj.create(cr, uid, t)
- return res
+ def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, price_type='tax_excluded', context={}):
+ context.update({'price_type': price_type})
+ return super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, price_unit, address_invoice_id, context=context)
account_invoice_line()
diff --git a/addons/account_tax_include/invoice_tax_incl.xml b/addons/account_tax_include/invoice_tax_incl.xml
index fefed46..43792dc 100644
--- a/addons/account_tax_include/invoice_tax_incl.xml
+++ b/addons/account_tax_include/invoice_tax_incl.xml
@@ -21,12 +21,48 @@
account.invoice
-
+
+
+ account.invoice.supplier.tax_include
+ form
+ account.invoice
+
+
+
+
+
+
+
+
+
+ account.invoice.supplier.tax_include2
+ form
+ account.invoice
+
+
+
+
+
+
+
+
+
+ account.invoice.supplier.tax_include3
+ form
+ account.invoice
+
+
+
+
+
+
+
+
account.invoice.line.tree
account.invoice.line
--
1.7.10.4