[IMP] Set icons to state of wizards
[odoo/odoo.git] / addons / account / invoice.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import time
23 import decimal_precision as dp
24
25 import netsvc
26 from osv import fields, osv
27 import ir
28 import pooler
29 from tools import config
30 from tools.translate import _
31
32 class account_invoice(osv.osv):
33     def _amount_all(self, cr, uid, ids, name, args, context=None):
34         res = {}
35         for invoice in self.browse(cr,uid,ids, context=context):
36             res[invoice.id] = {
37                 'amount_untaxed': 0.0,
38                 'amount_tax': 0.0,
39                 'amount_total': 0.0
40             }
41             for line in invoice.invoice_line:
42                 res[invoice.id]['amount_untaxed'] += line.price_subtotal
43             for line in invoice.tax_line:
44                 res[invoice.id]['amount_tax'] += line.amount
45             res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed']
46         return res
47
48     def _get_journal(self, cr, uid, context):
49         if context is None:
50             context = {}
51         type_inv = context.get('type', 'out_invoice')
52         user = self.pool.get('res.users').browse(cr, uid, uid)
53         company_id = context.get('company_id', user.company_id.id)
54         type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
55         refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True}
56         journal_obj = self.pool.get('account.journal')
57         res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')),
58                                             ('company_id', '=', company_id),('refund_journal', '=', refund_journal.get(type_inv, False))],
59                                                 limit=1)
60         if res:
61             return res[0]
62         else:
63             return False
64
65     def _get_currency(self, cr, uid, context):
66         user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
67         if user.company_id:
68             return user.company_id.currency_id.id
69         else:
70             return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
71
72     def _get_journal_analytic(self, cr, uid, type_inv, context=None):
73         type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
74         tt = type2journal.get(type_inv, 'sale')
75         result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
76         if not result:
77             raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s' !") % (tt,))
78         return result[0]
79
80     def _get_type(self, cr, uid, context=None):
81         if context is None:
82             context = {}
83         type = context.get('type', 'out_invoice')
84         return type
85
86     def _reconciled(self, cr, uid, ids, name, args, context):
87         res = {}
88         for id in ids:
89             res[id] = self.test_paid(cr, uid, [id])
90         return res
91
92     def _get_reference_type(self, cr, uid, context=None):
93         return [('none', _('Free Reference'))]
94
95     def _amount_residual(self, cr, uid, ids, name, args, context=None):
96         res = {}
97         data_inv = self.browse(cr, uid, ids)
98         cur_obj = self.pool.get('res.currency')
99         for inv in data_inv:
100             debit = credit = 0.0
101             context.update({'date':inv.date_invoice})
102             context_unreconciled=context.copy()
103             for lines in inv.move_lines:
104                 debit_tmp = lines.debit
105                 credit_tmp = lines.credit
106                 # If currency conversion needed
107                 if inv.company_id.currency_id.id <> inv.currency_id.id:
108                     # If invoice paid, compute currency amount according to invoice date
109                     # otherwise, take the line date
110                     if not inv.reconciled:
111                         context.update({'date':lines.date})
112                     context_unreconciled.update({'date':lines.date})
113                     # If amount currency setted, compute for debit and credit in company currency
114                     if lines.amount_currency < 0:
115                         credit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False,context=context_unreconciled))
116                     elif lines.amount_currency > 0:
117                         debit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False,context=context_unreconciled))
118                     # Then, recomput into invoice currency to avoid rounding trouble !
119                     debit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, debit_tmp, round=False,context=context)
120                     credit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, credit_tmp, round=False,context=context)
121                 else:
122                     debit+=debit_tmp
123                     credit+=credit_tmp
124
125             if not inv.amount_total:
126                 result = 0.0
127             elif inv.type in ('out_invoice','in_refund'):
128                 amount = credit-debit
129                 result = inv.amount_total - amount
130             else:
131                 amount = debit-credit
132                 result = inv.amount_total - amount
133             # Use is_zero function to avoid rounding trouble => should be fixed into ORM
134             res[inv.id] = not self.pool.get('res.currency').is_zero(cr, uid, inv.company_id.currency_id,result) and result or 0.0
135
136         return res
137
138     def _get_lines(self, cr, uid, ids, name, arg, context=None):
139         res = {}
140         for id in ids:
141             move_lines = self.move_line_id_payment_get(cr,uid,[id])
142             if not move_lines:
143                 res[id] = []
144                 continue
145             res[id] = []
146             data_lines = self.pool.get('account.move.line').browse(cr,uid,move_lines)
147             partial_ids = []# Keeps the track of ids where partial payments are done with payment terms
148             for line in data_lines:
149                 ids_line = []
150                 if line.reconcile_id:
151                     ids_line = line.reconcile_id.line_id
152                 elif line.reconcile_partial_id:
153                     ids_line = line.reconcile_partial_id.line_partial_ids
154                 l = map(lambda x: x.id, ids_line)
155                 partial_ids.append(line.id)
156                 res[id] =[x for x in l if x <> line.id and x not in partial_ids]
157         return res
158
159     def _get_invoice_line(self, cr, uid, ids, context=None):
160         result = {}
161         for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
162             result[line.invoice_id.id] = True
163         return result.keys()
164
165     def _get_invoice_tax(self, cr, uid, ids, context=None):
166         result = {}
167         for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
168             result[tax.invoice_id.id] = True
169         return result.keys()
170
171     def _compute_lines(self, cr, uid, ids, name, args, context=None):
172         result = {}
173         for invoice in self.browse(cr, uid, ids, context):
174             moves = self.move_line_id_payment_get(cr, uid, [invoice.id])
175             src = []
176             lines = []
177             for m in self.pool.get('account.move.line').browse(cr, uid, moves, context):
178                 temp_lines = []#Added temp list to avoid duplicate records
179                 if m.reconcile_id:
180                     temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
181                 elif m.reconcile_partial_id:
182                     temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
183                 lines += [x for x in temp_lines if x not in lines]
184                 src.append(m.id)
185
186             lines = filter(lambda x: x not in src, lines)
187             result[invoice.id] = lines
188         return result
189
190     def _get_invoice_from_line(self, cr, uid, ids, context={}):
191         move = {}
192         for line in self.pool.get('account.move.line').browse(cr, uid, ids):
193             if line.reconcile_partial_id:
194                 for line2 in line.reconcile_partial_id.line_partial_ids:
195                     move[line2.move_id.id] = True
196             if line.reconcile_id:
197                 for line2 in line.reconcile_id.line_id:
198                     move[line2.move_id.id] = True
199         invoice_ids = []
200         if move:
201             invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
202         return invoice_ids
203
204     def _get_invoice_from_reconcile(self, cr, uid, ids, context={}):
205         move = {}
206         for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
207             for line in r.line_partial_ids:
208                 move[line.move_id.id] = True
209             for line in r.line_id:
210                 move[line.move_id.id] = True
211
212         invoice_ids = []
213         if move:
214             invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
215         return invoice_ids
216
217     _name = "account.invoice"
218     _description = 'Invoice'
219     _order = "number"
220     _columns = {
221         'name': fields.char('Description', size=64, select=True,readonly=True, states={'draft':[('readonly',False)]}),
222         'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this invoice."),
223         'type': fields.selection([
224             ('out_invoice','Customer Invoice'),
225             ('in_invoice','Supplier Invoice'),
226             ('out_refund','Customer Refund'),
227             ('in_refund','Supplier Refund'),
228             ],'Type', readonly=True, select=True, change_default=True),
229
230         'number': fields.char('Invoice Number', size=32, readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."),
231         'reference': fields.char('Invoice Reference', size=64, help="The partner reference of this invoice."),
232         'reference_type': fields.selection(_get_reference_type, 'Reference Type',
233             required=True),
234         'comment': fields.text('Additional Information', translate=True),
235
236         'state': fields.selection([
237             ('draft','Draft'),
238             ('proforma','Pro-forma'),
239             ('proforma2','Pro-forma'),
240             ('open','Open'),
241             ('paid','Done'),
242             ('cancel','Cancelled')
243             ],'State', select=True, readonly=True,
244             help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Invoice. \
245             \n* The \'Pro-forma\' when invoice is in Pro-forma state,invoice does not have an invoice number. \
246             \n* The \'Open\' state is used when user create invoice,a invoice number is generated.Its in open state till user does not pay invoice. \
247             \n* The \'Done\' state is set automatically when invoice is paid.\
248             \n* The \'Cancelled\' state is used when user cancel invoice.'),
249         'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
250         'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]},
251             help="If you use payment terms, the due date will be computed automatically at the generation "\
252                 "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."),
253         'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
254         'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
255         'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
256         'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]},
257             help="If you use payment terms, the due date will be computed automatically at the generation "\
258                 "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\
259                 "The payment term may compute several due dates, for example 50% now, 50% in one month."),
260         'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}),
261
262         'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
263         'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
264         'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
265
266         'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True, help="Links to the automatically generated Ledger Postings."),
267         'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'),string='Untaxed',
268             store={
269                 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
270                 'account.invoice.tax': (_get_invoice_tax, None, 20),
271                 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
272             },
273             multi='all'),
274         'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Tax',
275             store={
276                 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
277                 'account.invoice.tax': (_get_invoice_tax, None, 20),
278                 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
279             },
280             multi='all'),
281         'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Total',
282             store={
283                 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
284                 'account.invoice.tax': (_get_invoice_tax, None, 20),
285                 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
286             },
287             multi='all'),
288         'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
289         'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}),
290         'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True),
291         'check_total': fields.float('Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
292         'reconciled': fields.function(_reconciled, method=True, string='Paid/Reconciled', type='boolean',
293             store={
294                 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
295                 'account.move.line': (_get_invoice_from_line, None, 50),
296                 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
297             }, help="The Ledger Postings of the invoice have been reconciled with Ledger Postings of the payment(s)."),
298         'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',
299             help='The bank account to pay to or to be paid from'),
300         'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Entry Lines'),
301         'residual': fields.function(_amount_residual, method=True, digits_compute=dp.get_precision('Account'),string='Residual',
302             store={
303                 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
304                 'account.invoice.tax': (_get_invoice_tax, None, 50),
305                 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
306                 'account.move.line': (_get_invoice_from_line, None, 50),
307                 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
308             },
309             help="Remaining amount due."),
310         'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'),
311         'move_name': fields.char('Ledger Posting', size=64),
312         'user_id': fields.many2one('res.users', 'Salesman'),
313         'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position')
314     }
315     _defaults = {
316         'type': _get_type,
317         #'date_invoice': lambda *a: time.strftime('%Y-%m-%d'),
318         'state': lambda *a: 'draft',
319         'journal_id': _get_journal,
320         'currency_id': _get_currency,
321         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.invoice', context=c),
322         'reference_type': lambda *a: 'none',
323         'check_total': lambda *a: 0.0,
324         'user_id': lambda s,cr,u,c: u,
325     }
326
327     def unlink(self, cr, uid, ids, context=None):
328         invoices = self.read(cr, uid, ids, ['state'])
329         unlink_ids = []
330         for t in invoices:
331             if t['state'] in ('draft', 'cancel'):
332                 unlink_ids.append(t['id'])
333             else:
334                 raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
335         osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
336         return True
337
338 #   def get_invoice_address(self, cr, uid, ids):
339 #       res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
340 #       return [{}]
341     def onchange_partner_id(self, cr, uid, ids, type, partner_id,
342             date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
343         invoice_addr_id = False
344         contact_addr_id = False
345         partner_payment_term = False
346         acc_id = False
347         bank_id = False
348         fiscal_position = False
349
350         opt = [('uid', str(uid))]
351         if partner_id:
352
353             opt.insert(0, ('id', partner_id))
354             res = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['contact', 'invoice'])
355             contact_addr_id = res['contact']
356             invoice_addr_id = res['invoice']
357             p = self.pool.get('res.partner').browse(cr, uid, partner_id)
358             if company_id:
359                 if p.property_account_receivable.company_id.id != company_id and p.property_account_payable.company_id.id != company_id:
360                     rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
361                     pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
362                     if not rec_pro_id:
363                         rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)])
364                     if not pay_pro_id:
365                         pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)])
366                     rec_line_data = self.pool.get('ir.property').read(cr,uid,rec_pro_id,['name','value','res_id'])
367                     pay_line_data = self.pool.get('ir.property').read(cr,uid,pay_pro_id,['name','value','res_id'])
368                     rec_res_id = rec_line_data and int(rec_line_data[0]['value'].split(',')[1]) or False
369                     pay_res_id = pay_line_data and int(pay_line_data[0]['value'].split(',')[1]) or False
370                     if not rec_res_id and not pay_res_id:
371                         raise osv.except_osv(_('Configration Error !'),
372                             _('Can not find account chart for this company, Please Create account.'))
373                     rec_obj_acc=self.pool.get('account.account').browse(cr,uid,[rec_res_id])
374                     pay_obj_acc=self.pool.get('account.account').browse(cr,uid,[pay_res_id])
375                     p.property_account_receivable = rec_obj_acc[0]
376                     p.property_account_payable = pay_obj_acc[0]
377
378             if type in ('out_invoice', 'out_refund'):
379                 acc_id = p.property_account_receivable.id
380             else:
381                 acc_id = p.property_account_payable.id
382             fiscal_position = p.property_account_position and p.property_account_position.id or False
383             partner_payment_term = p.property_payment_term and p.property_payment_term.id or False
384             if p.bank_ids:
385                 bank_id = p.bank_ids[0].id
386
387         result = {'value': {
388             'address_contact_id': contact_addr_id,
389             'address_invoice_id': invoice_addr_id,
390             'account_id': acc_id,
391             'payment_term': partner_payment_term,
392             'fiscal_position': fiscal_position
393             }
394         }
395
396         if type in ('in_invoice', 'in_refund'):
397             result['value']['partner_bank'] = bank_id
398
399         if payment_term != partner_payment_term:
400             if partner_payment_term:
401                 to_update = self.onchange_payment_term_date_invoice(
402                     cr,uid,ids,partner_payment_term,date_invoice)
403                 result['value'].update(to_update['value'])
404             else:
405                 result['value']['date_due'] = False
406
407         if partner_bank_id != bank_id:
408             to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
409             result['value'].update(to_update['value'])
410         return result
411
412     def onchange_currency_id(self, cr, uid, ids, curr_id, company_id):
413         if curr_id and company_id:
414             currency = self.pool.get('res.currency').browse(cr, uid, curr_id)
415             if currency.company_id.id != company_id:
416                 raise osv.except_osv(_('Configration Error !'),
417                         _('Can not select currency that is not related to current company.\nPlease select accordingly !.'))
418         return {}
419
420     def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice):
421         if not payment_term_id:
422             return {}
423         res={}
424         pt_obj= self.pool.get('account.payment.term')
425         if not date_invoice :
426             date_invoice = time.strftime('%Y-%m-%d')
427
428         pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
429
430         if pterm_list:
431             pterm_list = [line[0] for line in pterm_list]
432             pterm_list.sort()
433             res= {'value':{'date_due': pterm_list[-1]}}
434         else:
435              raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
436
437         return res
438
439     def onchange_invoice_line(self, cr, uid, ids, lines):
440         return {}
441
442     def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
443         return {'value': {}}
444
445     def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line, currency_id):
446         val={}
447         dom={}
448         if company_id and part_id and type:
449             acc_id = False
450             partner_obj = self.pool.get('res.partner').browse(cr,uid,part_id)
451             if partner_obj.property_account_payable and partner_obj.property_account_receivable:
452                 if partner_obj.property_account_payable.company_id.id != company_id and partner_obj.property_account_receivable.company_id.id != company_id:
453                     rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
454                     pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
455                     if not rec_pro_id:
456                         rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)])
457                     if not pay_pro_id:
458                         pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)])
459                     rec_line_data = self.pool.get('ir.property').read(cr,uid,rec_pro_id,['name','value','res_id'])
460                     pay_line_data = self.pool.get('ir.property').read(cr,uid,pay_pro_id,['name','value','res_id'])
461                     rec_res_id = rec_line_data and int(rec_line_data[0]['value'].split(',')[1]) or False
462                     pay_res_id = pay_line_data and int(pay_line_data[0]['value'].split(',')[1]) or False
463                     if not rec_res_id and not rec_res_id:
464                         raise osv.except_osv(_('Configration Error !'),
465                             _('Can not find account chart for this company, Please Create account.'))
466                     if type in ('out_invoice', 'out_refund'):
467                         acc_id = rec_res_id
468                     else:
469                         acc_id = pay_res_id
470                     val= {'account_id': acc_id}
471             if ids:
472                 if company_id:
473                     inv_obj = self.browse(cr,uid,ids)
474                     for line in inv_obj[0].invoice_line:
475                         if line.account_id:
476                             if line.account_id.company_id.id != company_id:
477                                 result_id = self.pool.get('account.account').search(cr,uid,[('name','=',line.account_id.name),('company_id','=',company_id)])
478                                 if not result_id:
479                                     raise osv.except_osv(_('Configration Error !'),
480                                         _('Can not find account chart for this company in invoice line account, Please Create account.'))
481                                 r_id = self.pool.get('account.invoice.line').write(cr,uid,[line.id],{'account_id': result_id[0]})
482             else:
483                 if invoice_line:
484                     for inv_line in invoice_line:
485                         obj_l = self.pool.get('account.account').browse(cr,uid,inv_line[2]['account_id'])
486                         if obj_l.company_id.id != company_id:
487                             raise osv.except_osv(_('Configration Error !'),
488                                 _('invoice line account company is not match with invoice company.'))
489                         else:
490                             continue
491         if company_id:
492             val['journal_id']=False
493             journal_ids=self.pool.get('account.journal').search(cr,uid,[('company_id','=',company_id)])
494             dom={'journal_id':  [('id','in',journal_ids)]}
495         else:
496             journal_ids=self.pool.get('account.journal').search(cr,uid,[])
497             dom={'journal_id':  [('id','in',journal_ids)]}
498
499         if currency_id and company_id:
500             currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
501             if currency.company_id.id != company_id:
502                 val['currency_id'] = False
503             else:
504                 val['currency_id'] = currency.id
505
506         if company_id:
507             company = self.pool.get('res.company').browse(cr, uid, company_id)
508             if company.currency_id.company_id.id != company_id:
509                 val['currency_id'] = False
510             else:
511                 val['currency_id'] = company.currency_id.id
512
513         return {'value' : val, 'domain': dom }
514
515     # go from canceled state to draft state
516     def action_cancel_draft(self, cr, uid, ids, *args):
517         self.write(cr, uid, ids, {'state':'draft'})
518         wf_service = netsvc.LocalService("workflow")
519         for inv_id in ids:
520             wf_service.trg_create(uid, 'account.invoice', inv_id, cr)
521         return True
522
523     # Workflow stuff
524     #################
525
526     # return the ids of the move lines which has the same account than the invoice
527     # whose id is in ids
528     def move_line_id_payment_get(self, cr, uid, ids, *args):
529         res = []
530         if not ids: return res
531         cr.execute('select \
532                 l.id \
533             from account_move_line l \
534                 left join account_invoice i on (i.move_id=l.move_id) \
535             where i.id in ('+','.join(map(str,map(int, ids)))+') and l.account_id=i.account_id')
536         res = map(lambda x: x[0], cr.fetchall())
537         return res
538
539     def copy(self, cr, uid, id, default=None, context=None):
540         if default is None:
541             default = {}
542         default = default.copy()
543         default.update({'state':'draft', 'number':False, 'move_id':False, 'move_name':False,})
544         if 'date_invoice' not in default:
545             default['date_invoice'] = False
546         if 'date_due' not in default:
547             default['date_due'] = False
548         return super(account_invoice, self).copy(cr, uid, id, default, context)
549
550     def test_paid(self, cr, uid, ids, *args):
551         res = self.move_line_id_payment_get(cr, uid, ids)
552         if not res:
553             return False
554         ok = True
555         for id in res:
556             cr.execute('select reconcile_id from account_move_line where id=%s', (id,))
557             ok = ok and  bool(cr.fetchone()[0])
558         return ok
559
560     def button_reset_taxes(self, cr, uid, ids, context=None):
561         if not context:
562             context = {}
563         ait_obj = self.pool.get('account.invoice.tax')
564         for id in ids:
565             cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
566             partner = self.browse(cr, uid, id,context=context).partner_id
567             if partner.lang:
568                 context.update({'lang': partner.lang})
569             for taxe in ait_obj.compute(cr, uid, id, context=context).values():
570                 ait_obj.create(cr, uid, taxe)
571          # Update the stored value (fields.function), so we write to trigger recompute
572         self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=context)
573 #        self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
574         return True
575
576     def button_compute(self, cr, uid, ids, context=None, set_total=False):
577         self.button_reset_taxes(cr, uid, ids, context)
578         for inv in self.browse(cr, uid, ids):
579             if set_total:
580                 self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
581         return True
582
583     def _convert_ref(self, cr, uid, ref):
584         return (ref or '').replace('/','')
585
586     def _get_analytic_lines(self, cr, uid, id):
587         inv = self.browse(cr, uid, [id])[0]
588         cur_obj = self.pool.get('res.currency')
589
590         company_currency = inv.company_id.currency_id.id
591         if inv.type in ('out_invoice', 'in_refund'):
592             sign = 1
593         else:
594             sign = -1
595
596         iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
597         for il in iml:
598             if il['account_analytic_id']:
599                 if inv.type in ('in_invoice', 'in_refund'):
600                     ref = inv.reference
601                 else:
602                     ref = self._convert_ref(cr, uid, inv.number)
603                 il['analytic_lines'] = [(0,0, {
604                     'name': il['name'],
605                     'date': inv['date_invoice'],
606                     'account_id': il['account_analytic_id'],
607                     'unit_amount': il['quantity'],
608                     'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign,
609                     'product_id': il['product_id'],
610                     'product_uom_id': il['uos_id'],
611                     'general_account_id': il['account_id'],
612                     'journal_id': self._get_journal_analytic(cr, uid, inv.type),
613                     'ref': ref,
614                 })]
615         return iml
616
617     def action_date_assign(self, cr, uid, ids, *args):
618         for inv in self.browse(cr, uid, ids):
619             res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
620             if res and res['value']:
621                 self.write(cr, uid, [inv.id], res['value'])
622         return True
623
624     def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj):
625         if not inv.tax_line:
626             for tax in compute_taxes.values():
627                 ait_obj.create(cr, uid, tax)
628         else:
629             tax_key = []
630             for tax in inv.tax_line:
631                 if tax.manual:
632                     continue
633                 key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
634                 tax_key.append(key)
635                 if not key in compute_taxes:
636                     raise osv.except_osv(_('Warning !'), _('Global taxes defined, but are not in invoice lines !'))
637                 base = compute_taxes[key]['base']
638                 if abs(base - tax.base) > inv.company_id.currency_id.rounding:
639                     raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base'))
640             for key in compute_taxes:
641                 if not key in tax_key:
642                     raise osv.except_osv(_('Warning !'), _('Taxes missing !'))
643
644     def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines):
645         total = 0
646         total_currency = 0
647         for i in invoice_move_lines:
648             if inv.currency_id.id != company_currency:
649                 i['currency_id'] = inv.currency_id.id
650                 i['amount_currency'] = i['price']
651                 i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
652                         company_currency, i['price'],
653                         context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')})
654             else:
655                 i['amount_currency'] = False
656                 i['currency_id'] = False
657             i['ref'] = ref
658             if inv.type in ('out_invoice','in_refund'):
659                 total += i['price']
660                 total_currency += i['amount_currency'] or i['price']
661                 i['price'] = - i['price']
662             else:
663                 total -= i['price']
664                 total_currency -= i['amount_currency'] or i['price']
665         return total, total_currency, invoice_move_lines
666
667     def inv_line_characteristic_hashcode(self, invoice, invoice_line):
668         """Overridable hashcode generation for invoice lines. Lines having the same hashcode
669         will be grouped together if the journal has the 'group line' option. Of course a module
670         can add fields to invoice lines that would need to be tested too before merging lines
671         or not."""
672         return "%s-%s-%s-%s-%s"%(
673             invoice_line['account_id'],
674             invoice_line.get('tax_code_id',"False"),
675             invoice_line.get('product_id',"False"),
676             invoice_line.get('analytic_account_id',"False"),
677             invoice_line.get('date_maturity',"False"))
678
679     def group_lines(self, cr, uid, iml, line, inv):
680         """Merge account move lines (and hence analytic lines) if invoice line hashcodes are equals"""
681         if inv.journal_id.group_invoice_lines:
682             line2 = {}
683             for x, y, l in line:
684                 tmp = self.inv_line_characteristic_hashcode(inv, l)
685
686                 if tmp in line2:
687                     am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
688                     line2[tmp]['debit'] = (am > 0) and am or 0.0
689                     line2[tmp]['credit'] = (am < 0) and -am or 0.0
690                     line2[tmp]['tax_amount'] += l['tax_amount']
691                     line2[tmp]['analytic_lines'] += l['analytic_lines']
692                 else:
693                     line2[tmp] = l
694             line = []
695             for key, val in line2.items():
696                 line.append((0,0,val))
697
698         return line
699
700     def action_move_create(self, cr, uid, ids, *args):
701         """Creates invoice related analytics and financial move lines"""
702         ait_obj = self.pool.get('account.invoice.tax')
703         cur_obj = self.pool.get('res.currency')
704         context = {}
705         for inv in self.browse(cr, uid, ids):
706             if inv.move_id:
707                 continue
708
709             if not inv.date_invoice:
710                 self.write(cr, uid, [inv.id], {'date_invoice':time.strftime('%Y-%m-%d')})
711             company_currency = inv.company_id.currency_id.id
712             # create the analytical lines
713             line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
714             # one move line per invoice line
715             iml = self._get_analytic_lines(cr, uid, inv.id)
716             # check if taxes are all computed
717
718             context.update({'lang': inv.partner_id.lang})
719             compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
720             self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
721
722             if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
723                 raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
724
725             # one move line per tax line
726             iml += ait_obj.move_line_get(cr, uid, inv.id)
727
728             if inv.type in ('in_invoice', 'in_refund'):
729                 ref = inv.reference
730             else:
731                 ref = self._convert_ref(cr, uid, inv.number)
732
733             diff_currency_p = inv.currency_id.id <> company_currency
734             # create one move line for the total and possibly adjust the other lines amount
735             total = 0
736             total_currency = 0
737             total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml)
738             acc_id = inv.account_id.id
739
740             name = inv['name'] or '/'
741             totlines = False
742             if inv.payment_term:
743                 totlines = self.pool.get('account.payment.term').compute(cr,
744                         uid, inv.payment_term.id, total, inv.date_invoice or False)
745             if totlines:
746                 res_amount_currency = total_currency
747                 i = 0
748                 for t in totlines:
749                     if inv.currency_id.id != company_currency:
750                         amount_currency = cur_obj.compute(cr, uid,
751                                 company_currency, inv.currency_id.id, t[1])
752                     else:
753                         amount_currency = False
754
755                     # last line add the diff
756                     res_amount_currency -= amount_currency or 0
757                     i += 1
758                     if i == len(totlines):
759                         amount_currency += res_amount_currency
760
761                     iml.append({
762                         'type': 'dest',
763                         'name': name,
764                         'price': t[1],
765                         'account_id': acc_id,
766                         'date_maturity': t[0],
767                         'amount_currency': diff_currency_p \
768                                 and  amount_currency or False,
769                         'currency_id': diff_currency_p \
770                                 and inv.currency_id.id or False,
771                         'ref': ref,
772                     })
773             else:
774                 iml.append({
775                     'type': 'dest',
776                     'name': name,
777                     'price': total,
778                     'account_id': acc_id,
779                     'date_maturity' : inv.date_due or False,
780                     'amount_currency': diff_currency_p \
781                             and total_currency or False,
782                     'currency_id': diff_currency_p \
783                             and inv.currency_id.id or False,
784                     'ref': ref
785             })
786
787             date = inv.date_invoice or time.strftime('%Y-%m-%d')
788             part = inv.partner_id.id
789
790             line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml)
791
792             line = self.group_lines(cr, uid, iml, line, inv)
793
794             journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']})
795             journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
796             if journal.centralisation:
797                 raise osv.except_osv(_('UserError'),
798                         _('Cannot create invoice move on centralised journal'))
799             move = {'ref': inv.number, 'line_id': line, 'journal_id': journal_id, 'date': date}
800             period_id=inv.period_id and inv.period_id.id or False
801             if not period_id:
802                 period_ids= self.pool.get('account.period').search(cr,uid,[('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d'))])
803                 if len(period_ids):
804                     period_id=period_ids[0]
805             if period_id:
806                 move['period_id'] = period_id
807                 for i in line:
808                     i[2]['period_id'] = period_id
809
810             move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
811             new_move_name = self.pool.get('account.move').browse(cr, uid, move_id).name
812             # make the invoice point to that move
813             self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name})
814             self.pool.get('account.move').post(cr, uid, [move_id])
815         self._log_event(cr, uid, ids)
816         return True
817
818     def line_get_convert(self, cr, uid, x, part, date, context=None):
819         return {
820             'date_maturity': x.get('date_maturity', False),
821             'partner_id':part,
822             'name':x['name'][:64],
823             'date': date,
824             'debit':x['price']>0 and x['price'],
825             'credit':x['price']<0 and -x['price'],
826             'account_id':x['account_id'],
827             'analytic_lines':x.get('analytic_lines', []),
828             'amount_currency':x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
829             'currency_id':x.get('currency_id', False),
830             'tax_code_id': x.get('tax_code_id', False),
831             'tax_amount': x.get('tax_amount', False),
832             'ref':x.get('ref',False),
833             'quantity':x.get('quantity',1.00),
834             'product_id':x.get('product_id', False),
835             'product_uom_id':x.get('uos_id',False),
836             'analytic_account_id':x.get('account_analytic_id',False),
837         }
838
839     def action_number(self, cr, uid, ids, *args):
840         cr.execute('SELECT id, type, number, move_id, reference ' \
841                 'FROM account_invoice ' \
842                 'WHERE id IN ('+','.join(map(str, ids))+')')
843         obj_inv = self.browse(cr, uid, ids)[0]
844         for (id, invtype, number, move_id, reference) in cr.fetchall():
845             if not number:
846                 if obj_inv.journal_id.invoice_sequence_id:
847                     sid = obj_inv.journal_id.invoice_sequence_id.id
848                     number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', {'fiscalyear_id': obj_inv.period_id.fiscalyear_id.id})
849                 else:
850                     number = self.pool.get('ir.sequence').get(cr, uid,
851                             'account.invoice.' + invtype)
852                 if invtype in ('in_invoice', 'in_refund'):
853                     ref = reference
854                 else:
855                     ref = self._convert_ref(cr, uid, number)
856                 cr.execute('UPDATE account_invoice SET number=%s ' \
857                         'WHERE id=%s', (number, id))
858                 cr.execute('UPDATE account_move SET ref=%s ' \
859                         'WHERE id=%s AND (ref is null OR ref = \'\')',
860                         (ref, move_id))
861                 cr.execute('UPDATE account_move_line SET ref=%s ' \
862                         'WHERE move_id=%s AND (ref is null OR ref = \'\')',
863                         (ref, move_id))
864                 cr.execute('UPDATE account_analytic_line SET ref=%s ' \
865                         'FROM account_move_line ' \
866                         'WHERE account_move_line.move_id = %s ' \
867                             'AND account_analytic_line.move_id = account_move_line.id',
868                             (ref, move_id))
869         return True
870
871     def action_cancel(self, cr, uid, ids, *args):
872         account_move_obj = self.pool.get('account.move')
873         invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
874         for i in invoices:
875             if i['move_id']:
876                 account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
877                 # delete the move this invoice was pointing to
878                 # Note that the corresponding move_lines and move_reconciles
879                 # will be automatically deleted too
880                 account_move_obj.unlink(cr, uid, [i['move_id'][0]])
881             if i['payment_ids']:
882                 self.pool.get('account.move.line').write(cr, uid, i['payment_ids'], {'reconcile_partial_id': False})
883         self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
884         self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
885         return True
886
887     ###################
888
889     def list_distinct_taxes(self, cr, uid, ids):
890         invoices = self.browse(cr, uid, ids)
891         taxes = {}
892         for inv in invoices:
893             for tax in inv.tax_line:
894                 if not tax['name'] in taxes:
895                     taxes[tax['name']] = {'name': tax['name']}
896         return taxes.values()
897
898     def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'):
899         invs = self.read(cr, uid, ids, ['type','partner_id','amount_untaxed'])
900         for inv in invs:
901             part=inv['partner_id'] and inv['partner_id'][0]
902             pc = pr = 0.0
903             cr.execute('select sum(quantity*price_unit) from account_invoice_line where invoice_id=%s', (inv['id'],))
904             total = inv['amount_untaxed']
905             if inv['type'] in ('in_invoice','in_refund'):
906                 partnertype='supplier'
907                 eventtype = 'purchase'
908                 pc = total*factor
909             else:
910                 partnertype = 'customer'
911                 eventtype = 'sale'
912                 pr = total*factor
913             if self.pool.get('res.partner.event.type').check(cr, uid, 'invoice_open'):
914                 self.pool.get('res.partner.event').create(cr, uid, {'name':'Invoice: '+name, 'som':False, 'description':name+' '+str(inv['id']), 'document':name, 'partner_id':part, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':pc, 'type':eventtype})
915         return len(invs)
916
917     def name_get(self, cr, uid, ids, context=None):
918         if not len(ids):
919             return []
920         types = {
921                 'out_invoice': 'CI: ',
922                 'in_invoice': 'SI: ',
923                 'out_refund': 'OR: ',
924                 'in_refund': 'SR: ',
925                 }
926         return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
927
928     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
929         if not args:
930             args=[]
931         if context is None:
932             context={}
933         ids = []
934         if name:
935             ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
936         if not ids:
937             ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
938         return self.name_get(cr, user, ids, context)
939
940     def _refund_cleanup_lines(self, cr, uid, lines):
941         for line in lines:
942             del line['id']
943             del line['invoice_id']
944             if 'account_id' in line:
945                 line['account_id'] = line.get('account_id', False) and line['account_id'][0]
946             if 'product_id' in line:
947                 line['product_id'] = line.get('product_id', False) and line['product_id'][0]
948             if 'uos_id' in line:
949                 line['uos_id'] = line.get('uos_id', False) and line['uos_id'][0]
950             if 'invoice_line_tax_id' in line:
951                 line['invoice_line_tax_id'] = [(6,0, line.get('invoice_line_tax_id', [])) ]
952             if 'account_analytic_id' in line:
953                 line['account_analytic_id'] = line.get('account_analytic_id', False) and line['account_analytic_id'][0]
954             if 'tax_code_id' in line :
955                 if isinstance(line['tax_code_id'],tuple)  and len(line['tax_code_id']) >0 :
956                     line['tax_code_id'] = line['tax_code_id'][0]
957             if 'base_code_id' in line :
958                 if isinstance(line['base_code_id'],tuple)  and len(line['base_code_id']) >0 :
959                     line['base_code_id'] = line['base_code_id'][0]
960         return map(lambda x: (0,0,x), lines)
961
962     def refund(self, cr, uid, ids, date=None, period_id=None, description=None):
963         invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'address_contact_id', 'address_invoice_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id'])
964
965         new_ids = []
966         for invoice in invoices:
967             del invoice['id']
968
969             type_dict = {
970                 'out_invoice': 'out_refund', # Customer Invoice
971                 'in_invoice': 'in_refund',   # Supplier Invoice
972                 'out_refund': 'out_invoice', # Customer Refund
973                 'in_refund': 'in_invoice',   # Supplier Refund
974             }
975
976
977             invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
978             invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines)
979
980             tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line'])
981             tax_lines = filter(lambda l: l['manual'], tax_lines)
982             tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
983             if not date :
984                 date = time.strftime('%Y-%m-%d')
985             invoice.update({
986                 'type': type_dict[invoice['type']],
987                 'date_invoice': date,
988                 'state': 'draft',
989                 'number': False,
990                 'invoice_line': invoice_lines,
991                 'tax_line': tax_lines
992             })
993             if period_id :
994                 invoice.update({
995                     'period_id': period_id,
996                 })
997             if description :
998                 invoice.update({
999                     'name': description,
1000                 })
1001             # take the id part of the tuple returned for many2one fields
1002             for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
1003                     'account_id', 'currency_id', 'payment_term', 'journal_id'):
1004                 invoice[field] = invoice[field] and invoice[field][0]
1005             # create the new invoice
1006             new_ids.append(self.create(cr, uid, invoice))
1007         return new_ids
1008
1009     def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
1010         if context is None:
1011             context = {}
1012         #TODO check if we can use different period for payment and the writeoff line
1013         assert len(ids)==1, "Can only pay one invoice at a time"
1014         invoice = self.browse(cr, uid, ids[0])
1015         src_account_id = invoice.account_id.id
1016         # Take the seq as name for move
1017         types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
1018         direction = types[invoice.type]
1019         #take the choosen date
1020         if 'date_p' in context and context['date_p']:
1021             date=context['date_p']
1022         else:
1023             date=time.strftime('%Y-%m-%d')
1024
1025         # Take the amount in currency and the currency of the payment
1026         if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
1027             amount_currency = context['amount_currency']
1028             currency_id = context['currency_id']
1029         else:
1030             amount_currency = False
1031             currency_id = False
1032         if invoice.type in ('in_invoice', 'in_refund'):
1033             ref = invoice.reference
1034         else:
1035             ref = self._convert_ref(cr, uid, invoice.number)
1036         # Pay attention to the sign for both debit/credit AND amount_currency
1037         l1 = {
1038             'debit': direction * pay_amount>0 and direction * pay_amount,
1039             'credit': direction * pay_amount<0 and - direction * pay_amount,
1040             'account_id': src_account_id,
1041             'partner_id': invoice.partner_id.id,
1042             'ref':ref,
1043             'date': date,
1044             'currency_id':currency_id,
1045             'amount_currency':amount_currency and direction * amount_currency or 0.0,
1046             'company_id': invoice.company_id.id,
1047         }
1048         l2 = {
1049             'debit': direction * pay_amount<0 and - direction * pay_amount,
1050             'credit': direction * pay_amount>0 and direction * pay_amount,
1051             'account_id': pay_account_id,
1052             'partner_id': invoice.partner_id.id,
1053             'ref':ref,
1054             'date': date,
1055             'currency_id':currency_id,
1056             'amount_currency':amount_currency and - direction * amount_currency or 0.0,
1057             'company_id': invoice.company_id.id,
1058         }
1059
1060         if not name:
1061             name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
1062         l1['name'] = name
1063         l2['name'] = name
1064
1065         lines = [(0, 0, l1), (0, 0, l2)]
1066         move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
1067         move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
1068
1069         line_ids = []
1070         total = 0.0
1071         line = self.pool.get('account.move.line')
1072         move_ids = [move_id,]
1073         if invoice.move_id:
1074             move_ids.append(invoice.move_id.id)
1075         cr.execute('SELECT id FROM account_move_line WHERE move_id = ANY(%s)',(move_ids,))
1076         lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
1077         for l in lines+invoice.payment_ids:
1078             if l.account_id.id==src_account_id:
1079                 line_ids.append(l.id)
1080                 total += (l.debit or 0.0) - (l.credit or 0.0)
1081         if (not round(total,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))) or writeoff_acc_id:
1082             self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
1083         else:
1084             self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
1085
1086         # Update the stored value (fields.function), so we write to trigger recompute
1087         self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
1088         return True
1089 account_invoice()
1090
1091 class account_invoice_line(osv.osv):
1092     def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
1093         res = {}
1094         cur_obj=self.pool.get('res.currency')
1095         for line in self.browse(cr, uid, ids):
1096             if line.invoice_id:
1097                 res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
1098                 cur = line.invoice_id.currency_id
1099                 res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
1100             else:
1101                 res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))
1102         return res
1103
1104
1105     def _price_unit_default(self, cr, uid, context=None):
1106         if context is None:
1107             context = {}
1108         if 'check_total' in context:
1109             t = context['check_total']
1110             for l in context.get('invoice_line', {}):
1111                 if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
1112                     tax_obj = self.pool.get('account.tax')
1113                     p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
1114                     t = t - (p * l[2].get('quantity'))
1115                     taxes = l[2].get('invoice_line_tax_id')
1116                     if len(taxes[0]) >= 3 and taxes[0][2]:
1117                         taxes=tax_obj.browse(cr, uid, taxes[0][2])
1118                         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)):
1119                             t = t - tax['amount']
1120             return t
1121         return 0
1122
1123     _name = "account.invoice.line"
1124     _description = "Invoice line"
1125     _columns = {
1126         'name': fields.char('Description', size=256, required=True),
1127         'origin': fields.char('Origin', size=256, help="Reference of the document that produced this invoice."),
1128         'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True),
1129         'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
1130         'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
1131         'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
1132         'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Account')),
1133         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True, type="float", digits_compute= dp.get_precision('Account')),
1134         'quantity': fields.float('Quantity', required=True),
1135         'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Account')),
1136         'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
1137         'note': fields.text('Notes', translate=True),
1138         'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
1139         'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
1140         'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True)
1141     }
1142     _defaults = {
1143         'quantity': lambda *a: 1,
1144         'discount': lambda *a: 0.0,
1145         'price_unit': _price_unit_default,
1146     }
1147
1148     def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=None):
1149         tax_obj = self.pool.get('account.tax')
1150         if price_unit:
1151             taxes = tax_obj.browse(cr, uid, tax_id)
1152             for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id):
1153                 price_unit = price_unit - tax['amount']
1154         return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
1155
1156     def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
1157         if context is None:
1158             context = {}
1159         company_id = context.get('company_id',False)
1160         if not partner_id:
1161             raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
1162         if not product:
1163             if type in ('in_invoice', 'in_refund'):
1164                 return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}}
1165             else:
1166                 return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}}
1167         part = self.pool.get('res.partner').browse(cr, uid, partner_id)
1168         fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1169
1170         lang=part.lang
1171         context.update({'lang': lang})
1172         result = {}
1173         res = self.pool.get('product.product').browse(cr, uid, product, context=context)
1174
1175         if company_id:
1176             in_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_income'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
1177             if not in_pro_id:
1178                 in_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_income_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
1179             exp_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_expense'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
1180             if not exp_pro_id:
1181                 exp_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_expense_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
1182
1183             if not in_pro_id:
1184                 in_acc = res.product_tmpl_id.property_account_income
1185                 in_acc_cate = res.categ_id.property_account_income_categ
1186                 if in_acc:
1187                     app_acc_in = in_acc
1188                 else:
1189                     app_acc_in = in_acc_cate
1190             else:
1191                 app_acc_in = self.pool.get('account.account').browse(cr,uid,in_pro_id)[0]
1192             if not exp_pro_id:
1193                 ex_acc = res.product_tmpl_id.property_account_expense
1194                 ex_acc_cate = res.categ_id.property_account_expense_categ
1195                 if ex_acc:
1196                     app_acc_exp = ex_acc
1197                 else:
1198                     app_acc_exp = ex_acc_cate
1199             else:
1200                 app_acc_exp = self.pool.get('account.account').browse(cr,uid,exp_pro_id)[0]
1201             if not in_pro_id and not exp_pro_id:
1202                 in_acc = res.product_tmpl_id.property_account_income
1203                 in_acc_cate = res.categ_id.property_account_income_categ
1204                 ex_acc = res.product_tmpl_id.property_account_expense
1205                 ex_acc_cate = res.categ_id.property_account_expense_categ
1206                 if in_acc or ex_acc:
1207                     app_acc_in = in_acc
1208                     app_acc_exp = ex_acc
1209                 else:
1210                     app_acc_in = in_acc_cate
1211                     app_acc_exp = ex_acc_cate
1212 #            else:
1213 #                app_acc_in = self.pool.get('account.account').browse(cr,uid,in_pro_id)[0]
1214 #                app_acc_exp = self.pool.get('account.account').browse(cr,uid,exp_pro_id)[0]
1215             if app_acc_in.company_id.id != company_id and app_acc_exp.company_id.id != company_id:
1216                 in_res_id=self.pool.get('account.account').search(cr,uid,[('name','=',app_acc_in.name),('company_id','=',company_id)])
1217                 exp_res_id=self.pool.get('account.account').search(cr,uid,[('name','=',app_acc_exp.name),('company_id','=',company_id)])
1218                 if not in_res_id and not exp_res_id:
1219                     raise osv.except_osv(_('Configration Error !'),
1220                         _('Can not find account chart for this company, Please Create account.'))
1221                 in_obj_acc=self.pool.get('account.account').browse(cr,uid,in_res_id)
1222                 exp_obj_acc=self.pool.get('account.account').browse(cr,uid,exp_res_id)
1223                 if in_acc or ex_acc:
1224                     res.product_tmpl_id.property_account_income = in_obj_acc[0]
1225                     res.product_tmpl_id.property_account_expense = exp_obj_acc[0]
1226                 else:
1227                     res.categ_id.property_account_income_categ = in_obj_acc[0]
1228                     res.categ_id.property_account_expense_categ = exp_obj_acc[0]
1229
1230         if type in ('out_invoice','out_refund'):
1231             a =  res.product_tmpl_id.property_account_income.id
1232             if not a:
1233                 a = res.categ_id.property_account_income_categ.id
1234         else:
1235             a =  res.product_tmpl_id.property_account_expense.id
1236             if not a:
1237                 a = res.categ_id.property_account_expense_categ.id
1238
1239         a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
1240         if a:
1241             result['account_id'] = a
1242
1243         taxep=None
1244         tax_obj = self.pool.get('account.tax')
1245         if type in ('out_invoice', 'out_refund'):
1246             taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
1247             tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1248         else:
1249             taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
1250             tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1251         if type in ('in_invoice', 'in_refund'):
1252             to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit or res.standard_price, qty, address_invoice_id, product, partner_id, context=context)
1253             result.update(to_update)
1254         else:
1255             result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
1256
1257         if not name:
1258             result['name'] = res.partner_ref
1259
1260         domain = {}
1261         result['uos_id'] = uom or res.uom_id.id or False
1262         if result['uos_id']:
1263             res2 = res.uom_id.category_id.id
1264             if res2 :
1265                 domain = {'uos_id':[('category_id','=',res2 )]}
1266
1267         prod_pool=self.pool.get('product.product')
1268         result['categ_id'] = res.categ_id.id
1269         res_final = {'value':result, 'domain':domain}
1270
1271         if not company_id and not currency_id:
1272             return res_final
1273
1274         company = self.pool.get('res.company').browse(cr, uid, company_id)
1275         currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
1276
1277         if not currency.company_id.id == company.id:
1278             raise osv.except_osv(_('Configration Error !'),
1279                         _('Can not select currency that is not related to any company.\nPlease select accordingly !.'))
1280
1281         if company.currency_id.id != currency.id:
1282             new_price = res_final['value']['price_unit'] * currency.rate
1283             res_final['value']['price_unit'] = new_price
1284         return res_final
1285
1286     def move_line_get(self, cr, uid, invoice_id, context=None):
1287         res = []
1288         tax_grouped = {}
1289         tax_obj = self.pool.get('account.tax')
1290         cur_obj = self.pool.get('res.currency')
1291         ait_obj = self.pool.get('account.invoice.tax')
1292         inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
1293         company_currency = inv.company_id.currency_id.id
1294         cur = inv.currency_id
1295
1296         for line in inv.invoice_line:
1297             mres = self.move_line_get_item(cr, uid, line, context)
1298             if not mres:
1299                 continue
1300             res.append(mres)
1301             tax_code_found= False
1302             for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id,
1303                     (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)),
1304                     line.quantity, inv.address_invoice_id.id, line.product_id,
1305                     inv.partner_id):
1306
1307                 if inv.type in ('out_invoice', 'in_invoice'):
1308                     tax_code_id = tax['base_code_id']
1309                     tax_amount = line.price_subtotal * tax['base_sign']
1310                 else:
1311                     tax_code_id = tax['ref_base_code_id']
1312                     tax_amount = line.price_subtotal * tax['ref_base_sign']
1313
1314                 if tax_code_found:
1315                     if not tax_code_id:
1316                         continue
1317                     res.append(self.move_line_get_item(cr, uid, line, context))
1318                     res[-1]['price'] = 0.0
1319                     res[-1]['account_analytic_id'] = False
1320                 elif not tax_code_id:
1321                     continue
1322                 tax_code_found = True
1323
1324                 res[-1]['tax_code_id'] = tax_code_id
1325                 res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice})
1326         return res
1327
1328     def move_line_get_item(self, cr, uid, line, context=None):
1329         return {
1330             'type':'src',
1331             'name': line.name[:64],
1332             'price_unit':line.price_unit,
1333             'quantity':line.quantity,
1334             'price':line.price_subtotal,
1335             'account_id':line.account_id.id,
1336             'product_id':line.product_id.id,
1337             'uos_id':line.uos_id.id,
1338             'account_analytic_id':line.account_analytic_id.id,
1339             'taxes':line.invoice_line_tax_id,
1340         }
1341     #
1342     # Set the tax field according to the account and the fiscal position
1343     #
1344     def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
1345         if not account_id:
1346             return {}
1347         taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
1348         fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1349         res = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1350         r = {'value':{'invoice_line_tax_id': res}}
1351         return r
1352 account_invoice_line()
1353
1354 class account_invoice_tax(osv.osv):
1355     _name = "account.invoice.tax"
1356     _description = "Invoice Tax"
1357     _columns = {
1358         'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
1359         'name': fields.char('Tax Description', size=64, required=True),
1360         'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
1361         'base': fields.float('Base', digits_compute=dp.get_precision('Account')),
1362         'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')),
1363         'manual': fields.boolean('Manual'),
1364         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."),
1365
1366         'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
1367         'base_amount': fields.float('Base Code Amount', digits_compute=dp.get_precision('Account')),
1368         'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
1369         'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Account')),
1370         'company_id': fields.related('account_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
1371     }
1372
1373     def base_change(self, cr, uid, ids, base,currency_id=False,company_id=False,date_invoice=False):
1374         cur_obj = self.pool.get('res.currency')
1375         company_obj = self.pool.get('res.company')
1376         company_currency=False
1377         if company_id:
1378             company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1379         if currency_id and company_currency:
1380             base = cur_obj.compute(cr, uid, currency_id, company_currency, base, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1381         return {'value': {'base_amount':base}}
1382
1383     def amount_change(self, cr, uid, ids, amount,currency_id=False,company_id=False,date_invoice=False):
1384         cur_obj = self.pool.get('res.currency')
1385         company_obj = self.pool.get('res.company')
1386         company_currency=False
1387         if company_id:
1388             company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1389         if currency_id and company_currency:
1390             amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1391         return {'value': {'tax_amount':amount}}
1392
1393     _order = 'sequence'
1394     _defaults = {
1395         'manual': lambda *a: 1,
1396         'base_amount': lambda *a: 0.0,
1397         'tax_amount': lambda *a: 0.0,
1398     }
1399     def compute(self, cr, uid, invoice_id, context={}):
1400         tax_grouped = {}
1401         tax_obj = self.pool.get('account.tax')
1402         cur_obj = self.pool.get('res.currency')
1403         inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context)
1404         cur = inv.currency_id
1405         company_currency = inv.company_id.currency_id.id
1406
1407         for line in inv.invoice_line:
1408             for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id):
1409                 val={}
1410                 val['invoice_id'] = inv.id
1411                 val['name'] = tax['name']
1412                 val['amount'] = tax['amount']
1413                 val['manual'] = False
1414                 val['sequence'] = tax['sequence']
1415                 val['base'] = tax['price_unit'] * line['quantity']
1416
1417                 if inv.type in ('out_invoice','in_invoice'):
1418                     val['base_code_id'] = tax['base_code_id']
1419                     val['tax_code_id'] = tax['tax_code_id']
1420                     val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1421                     val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1422                     val['account_id'] = tax['account_collected_id'] or line.account_id.id
1423                 else:
1424                     val['base_code_id'] = tax['ref_base_code_id']
1425                     val['tax_code_id'] = tax['ref_tax_code_id']
1426                     val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1427                     val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1428                     val['account_id'] = tax['account_paid_id'] or line.account_id.id
1429
1430                 key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
1431                 if not key in tax_grouped:
1432                     tax_grouped[key] = val
1433                 else:
1434                     tax_grouped[key]['amount'] += val['amount']
1435                     tax_grouped[key]['base'] += val['base']
1436                     tax_grouped[key]['base_amount'] += val['base_amount']
1437                     tax_grouped[key]['tax_amount'] += val['tax_amount']
1438
1439         for t in tax_grouped.values():
1440             t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
1441             t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
1442             t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
1443         return tax_grouped
1444
1445     def move_line_get(self, cr, uid, invoice_id):
1446         res = []
1447         cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%s', (invoice_id,))
1448         for t in cr.dictfetchall():
1449             if not t['amount'] \
1450                     and not t['tax_code_id'] \
1451                     and not t['tax_amount']:
1452                 continue
1453             res.append({
1454                 'type':'tax',
1455                 'name':t['name'],
1456                 'price_unit': t['amount'],
1457                 'quantity': 1,
1458                 'price': t['amount'] or 0.0,
1459                 'account_id': t['account_id'],
1460                 'tax_code_id': t['tax_code_id'],
1461                 'tax_amount': t['tax_amount']
1462             })
1463         return res
1464 account_invoice_tax()
1465
1466 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: