account_tax_include : Now the module manage correctly the case when the taxes defined...
[odoo/odoo.git] / addons / account_tax_include / invoice_tax_incl.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 #
6 # $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
7 #
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
14 #
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28 #
29 ##############################################################################
30
31 import time
32 import netsvc
33 from osv import fields, osv
34 import ir
35
36 class account_invoice(osv.osv):
37         def _amount_untaxed(self, cr, uid, ids, prop, unknow_none,unknow_dict):
38                 res = {}
39                 for invoice in self.browse(cr,uid,ids):
40                         res[invoice.id]= reduce( lambda x, y: x+y.price_subtotal,
41                                                                         invoice.invoice_line,0)
42                 return res
43
44         def _amount_total(self, cr, uid, ids, prop, unknow_none,unknow_dict):
45                 res = {}
46                 for invoice in self.browse(cr,uid,ids):
47                         res[invoice.id]= reduce( lambda x, y: x+y.price_subtotal_incl,
48                                                                         invoice.invoice_line,0)
49                 return res
50
51         def _amount_tax(self, cr, uid, ids, prop, unknow_none,unknow_dict):
52                 res = {}
53                 for invoice in self.browse(cr,uid,ids):
54                         res[invoice.id]= reduce( lambda x, y: x+y.amount,
55                                                                         invoice.tax_line,0)
56                 return res
57
58         _inherit = "account.invoice"
59         _columns = {
60                 'price_type': fields.selection([('tax_included','Tax included'),
61                                                                                 ('tax_excluded','Tax excluded')],
62                                                                            'Price method', required=True, readonly=True,
63                                                                            states={'draft':[('readonly',False)]}),
64                 'amount_untaxed': fields.function(_amount_untaxed, digits=(16,2), method=True,string='Untaxed Amount'),
65                 'amount_tax': fields.function(_amount_tax, method=True, string='Tax'),
66                 'amount_total': fields.function(_amount_total, method=True, string='Total'),
67         }
68         _defaults = {
69                 'price_type': lambda *a: 'tax_excluded',
70         }
71 account_invoice()
72
73 class account_invoice_line(osv.osv):
74         _inherit = "account.invoice.line"
75         def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
76                 """
77                 Return the subtotal excluding taxes with respect to price_type.
78                 """
79                 #cur_obj = self.pool.get('res.currency')
80                 cur = False
81                 res = {}
82                 tax_obj = self.pool.get('account.tax')
83                 for line in self.browse(cr, uid, ids):
84                         res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
85                 
86                         if line.product_id and line.invoice_id.price_type == 'tax_included':
87                                 taxes = tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
88                                                                                         res[line.id],
89                                                                                         line.quantity)
90                                 amount = 0
91                                 for t in taxes : amount = amount + t['amount']
92                                 cur = cur or line.invoice_id.currency_id
93                                 res[line.id]= cur.round(cr, uid, cur, res[line.id] - amount) 
94                 return res
95
96         def _amount_line_incl(self, cr, uid, ids, prop, unknow_none,unknow_dict):
97                 """
98                 Return the subtotal including taxes with respect to price_type.
99                 """
100                 res = {}
101                 cur = False
102                 tax_obj = self.pool.get('account.tax')
103                 for line in self.browse(cr, uid, ids):
104                         res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
105                         if line.product_id:
106                                 prod_taxe_ids = line.product_id and [t.id for t in line.product_id.taxes_id ] or []
107                                 prod_taxe_ids.sort()
108                                 line_taxe_ids = [ t.id for t in line.invoice_line_tax_id if t]
109                                 line_taxe_ids.sort()
110                                 if prod_taxe_ids == line_taxe_ids :
111                                         continue
112                         else : continue
113                         
114                         res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
115                         if line.invoice_id.price_type == 'tax_included':                        
116                                 # remove product taxes
117                                 taxes = tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
118                                                                                         res[line.id],
119                                                                                         line.quantity)
120                                 amount = 0
121                                 for t in taxes : amount = amount + t['amount']
122                                 res[line.id]= res[line.id] - amount
123                         ## Add line taxes
124                         taxes = tax_obj.compute(cr, uid,line.invoice_line_tax_id, res[line.id], line.quantity)
125                         amount = 0
126                         for t in taxes : amount = amount + t['amount']
127                         cur = cur or line.invoice_id.currency_id                                        
128                         res[line.id]= cur.round(cr, uid, cur, res[line.id] + amount) 
129                 return res
130
131
132         _columns = {
133                 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o vat'),
134                 'price_subtotal_incl': fields.function(_amount_line_incl, method=True, string='Subtotal'),
135         }
136
137         #
138         # Compute a tax amount for each kind of tax :
139         #
140         def move_line_get(self, cr, uid, invoice_id, context={}):
141                 inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
142                 if inv.price_type=='tax_excluded':
143                         return super(account_invoice_line,self).move_line_get(cr, uid, invoice_id)
144
145                 res = []
146                 tax_grouped = {}
147                 tax_obj = self.pool.get('account.tax')
148                 cur_obj = self.pool.get('res.currency')
149                 ait_obj = self.pool.get('account.invoice.tax')
150                 cur = inv.currency_id
151
152                 for line in inv.invoice_line:
153                         price_unit = line.price_unit
154                         if line.product_id:
155                                 prod_taxe_ids = [ t.id for t in line.product_id.taxes_id ]
156                                 prod_taxe_ids.sort()
157                                 line_taxe_ids = [ t.id for t in line.invoice_line_tax_id]
158                                 line_taxe_ids.sort()
159                         if line.product_id and prod_taxe_ids != line_taxe_ids :
160                                 price_unit= reduce( lambda x, y: x-y['amount'],
161                                                                         tax_obj.compute_inv(cr, uid,line.product_id.taxes_id,
162                                                                                                                 line.price_unit * (1-(line.discount or 0.0)/100.0), line.quantity),
163                                                                         price_unit)
164                                 taxes =tax_obj.compute(cr, uid, line.invoice_line_tax_id,
165                                                                            (price_unit *(1.0-(line['discount'] or 0.0)/100.0)),
166                                                                            line.quantity, inv.address_invoice_id.id)
167                         else:
168                                 taxes= tax_obj.compute_inv(cr, uid, line.invoice_line_tax_id,
169                                                                                    (line.price_unit *(1.0-(line['discount'] or 0.0)/100.0)),
170                                                                                    line.quantity, inv.address_invoice_id.id)
171
172                         res.append( {
173                                 'type':'src', 
174                                 'name':line.name, 
175                                 'price_unit':price_unit, 
176                                 'quantity':line.quantity, 
177                                 'price':line.quantity*price_unit * (1.0- (line.discount or 0.0)/100.0),
178                                 'account_id':line.account_id.id,
179                                 'product_id': line.product_id.id,
180                                 'uos_id':line.uos_id.id,
181                                 'account_analytic_id':line.account_analytic_id.id,
182                         })
183                         for tax in taxes:
184                                 val={}
185                                 val['invoice_id'] = inv.id
186                                 val['name'] = tax['name']
187                                 val['amount'] = cur_obj.round(cr, uid, cur, tax['amount'])
188                                 val['manual'] = False
189                                 val['sequence'] = tax['sequence']
190                                 val['base'] = tax['price_unit'] * line['quantity']
191
192                                 res[-1]['price']-=tax['amount']
193
194                                 #
195                                 # Setting the tax account and amount for the line
196                                 #
197                                 if inv.type in ('out_invoice','in_invoice'):
198                                         val['base_code_id'] = tax['base_code_id']
199                                         val['tax_code_id'] = tax['tax_code_id']
200                                         val['base_amount'] = val['base'] * tax['base_sign']
201                                         val['tax_amount'] = val['amount'] * tax['tax_sign']
202                                         val['account_id'] = tax['account_collected_id'] or line.account_id.id
203                                 else:
204                                         val['base_code_id'] = tax['ref_base_code_id']
205                                         val['tax_code_id'] = tax['ref_tax_code_id']
206                                         val['base_amount'] = val['base'] * tax['ref_base_sign']
207                                         val['tax_amount'] = val['amount'] * tax['ref_tax_sign']
208                                         val['account_id'] = tax['account_paid_id'] or line.account_id.id
209
210                                 res[-1]['tax_code_id'] = val['base_code_id']
211                                 res[-1]['tax_amount'] = val['base_amount']
212
213                                 key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
214                                 if not key in tax_grouped:
215                                         tax_grouped[key] = val
216                                 else:
217                                         tax_grouped[key]['amount'] += val['amount']
218                                         tax_grouped[key]['base'] += val['base']
219                                         tax_grouped[key]['base_amount'] += val['base_amount']
220                                         tax_grouped[key]['tax_amount'] += val['tax_amount']
221                         res[-1]['price']=cur_obj.round(cr, uid, cur, res[-1]['price'])
222                 # delete automatic tax lines for this invoice
223                 cr.execute("DELETE FROM account_invoice_tax WHERE NOT manual AND invoice_id=%d", (invoice_id,))
224                 for t in tax_grouped.values():
225                         ait_obj.create(cr, uid, t)
226                 return res
227 account_invoice_line()