1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
25 from osv import fields, osv
29 from mx.DateTime import RelativeDateTime
30 from tools import config
31 from tools.translate import _
33 class fiscalyear_seq(osv.osv):
34 _name = "fiscalyear.seq"
35 _description = "Maintains Invoice sequences with Fiscal Year"
36 _rec_name = 'fiscalyear_id'
38 'journal_id': fields.many2one('account.journal', 'Journal'),
39 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year',required=True),
40 'sequence_id':fields.many2one('ir.sequence', 'Sequence',required=True),
45 class account_invoice(osv.osv):
46 def _amount_all(self, cr, uid, ids, name, args, context=None):
48 for invoice in self.browse(cr,uid,ids, context=context):
50 'amount_untaxed': 0.0,
54 for line in invoice.invoice_line:
55 res[invoice.id]['amount_untaxed'] += line.price_subtotal
56 for line in invoice.tax_line:
57 res[invoice.id]['amount_tax'] += line.amount
58 res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed']
61 def _get_journal(self, cr, uid, context):
64 type_inv = context.get('type', 'out_invoice')
65 type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
66 refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True}
67 journal_obj = self.pool.get('account.journal')
68 res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')), ('refund_journal', '=', refund_journal.get(type_inv, False))], limit=1)
74 def _get_currency(self, cr, uid, context):
75 user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
77 return user.company_id.currency_id.id
79 return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
81 def _get_journal_analytic(self, cr, uid, type_inv, context=None):
82 type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
83 tt = type2journal.get(type_inv, 'sale')
84 result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
86 raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s' !") % (tt,))
89 def _get_type(self, cr, uid, context=None):
92 type = context.get('type', 'out_invoice')
95 def _reconciled(self, cr, uid, ids, name, args, context):
98 res[id] = self.test_paid(cr, uid, [id])
101 def _get_reference_type(self, cr, uid, context=None):
102 return [('none', _('Free Reference'))]
104 def _amount_residual(self, cr, uid, ids, name, args, context=None):
106 data_inv = self.browse(cr, uid, ids)
107 cur_obj = self.pool.get('res.currency')
110 context.update({'date':inv.date_invoice})
111 context_unreconciled=context.copy()
112 for lines in inv.move_lines:
113 debit_tmp = lines.debit
114 credit_tmp = lines.credit
115 # If currency conversion needed
116 if inv.company_id.currency_id.id <> inv.currency_id.id:
117 # If invoice paid, compute currency amount according to invoice date
118 # otherwise, take the line date
119 if not inv.reconciled:
120 context.update({'date':lines.date})
121 context_unreconciled.update({'date':lines.date})
122 # If amount currency setted, compute for debit and credit in company currency
123 if lines.amount_currency < 0:
124 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))
125 elif lines.amount_currency > 0:
126 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))
127 # Then, recomput into invoice currency to avoid rounding trouble !
128 debit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, debit_tmp, round=False,context=context)
129 credit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, credit_tmp, round=False,context=context)
134 if not inv.amount_total:
136 elif inv.type in ('out_invoice','in_refund'):
137 amount = credit-debit
138 result = inv.amount_total - amount
140 amount = debit-credit
141 result = inv.amount_total - amount
142 # Use is_zero function to avoid rounding trouble => should be fixed into ORM
143 res[inv.id] = not self.pool.get('res.currency').is_zero(cr, uid, inv.company_id.currency_id,result) and result or 0.0
147 def _get_lines(self, cr, uid, ids, name, arg, context=None):
150 move_lines = self.move_line_id_payment_get(cr,uid,[id])
155 data_lines = self.pool.get('account.move.line').browse(cr,uid,move_lines)
156 partial_ids = []# Keeps the track of ids where partial payments are done with payment terms
157 for line in data_lines:
159 if line.reconcile_id:
160 ids_line = line.reconcile_id.line_id
161 elif line.reconcile_partial_id:
162 ids_line = line.reconcile_partial_id.line_partial_ids
163 l = map(lambda x: x.id, ids_line)
164 partial_ids.append(line.id)
165 res[id] =[x for x in l if x <> line.id and x not in partial_ids]
168 def _get_invoice_line(self, cr, uid, ids, context=None):
170 for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
171 result[line.invoice_id.id] = True
174 def _get_invoice_tax(self, cr, uid, ids, context=None):
176 for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
177 result[tax.invoice_id.id] = True
180 def _compute_lines(self, cr, uid, ids, name, args, context=None):
182 for invoice in self.browse(cr, uid, ids, context):
183 moves = self.move_line_id_payment_get(cr, uid, [invoice.id])
186 for m in self.pool.get('account.move.line').browse(cr, uid, moves, context):
187 temp_lines = []#Added temp list to avoid duplicate records
189 temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
190 elif m.reconcile_partial_id:
191 temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
192 lines += [x for x in temp_lines if x not in lines]
195 lines = filter(lambda x: x not in src, lines)
196 result[invoice.id] = lines
199 def _get_invoice_from_line(self, cr, uid, ids, context={}):
201 for line in self.pool.get('account.move.line').browse(cr, uid, ids):
202 if line.reconcile_partial_id:
203 for line2 in line.reconcile_partial_id.line_partial_ids:
204 move[line2.move_id.id] = True
205 if line.reconcile_id:
206 for line2 in line.reconcile_id.line_id:
207 move[line2.move_id.id] = True
210 invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
213 def _get_invoice_from_reconcile(self, cr, uid, ids, context={}):
215 for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
216 for line in r.line_partial_ids:
217 move[line.move_id.id] = True
218 for line in r.line_id:
219 move[line.move_id.id] = True
223 invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
226 _name = "account.invoice"
227 _description = 'Invoice'
230 'name': fields.char('Description', size=64, select=True,readonly=True, states={'draft':[('readonly',False)]}),
231 'origin': fields.char('Origin', size=64, help="Reference of the document that produced this invoice."),
232 'type': fields.selection([
233 ('out_invoice','Customer Invoice'),
234 ('in_invoice','Supplier Invoice'),
235 ('out_refund','Customer Refund'),
236 ('in_refund','Supplier Refund'),
237 ],'Type', readonly=True, select=True),
239 'number': fields.char('Invoice Number', size=32, readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."),
240 'reference': fields.char('Invoice Reference', size=64, help="The partner reference of this invoice."),
241 'reference_type': fields.selection(_get_reference_type, 'Reference Type',
243 'comment': fields.text('Additional Information'),
245 'state': fields.selection([
247 ('proforma','Pro-forma'),
248 ('proforma2','Pro-forma'),
251 ('cancel','Cancelled')
252 ],'State', select=True, readonly=True),
254 'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
255 'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]},
256 help="If you use payment terms, the due date will be computed automatically at the generation "\
257 "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."),
258 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
259 'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
260 'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
261 'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]},
262 help="If you use payment terms, the due date will be computed automatically at the generation "\
263 "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\
264 "The payment term may compute several due dates, for example 50% now, 50% in one month."),
265 '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)]}),
267 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
268 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
269 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
271 'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True, help="Link to the automatically generated account moves."),
272 'amount_untaxed': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])),string='Untaxed',
274 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
275 'account.invoice.tax': (_get_invoice_tax, None, 20),
276 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
279 'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Tax',
281 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
282 'account.invoice.tax': (_get_invoice_tax, None, 20),
283 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
286 'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
288 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
289 'account.invoice.tax': (_get_invoice_tax, None, 20),
290 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
293 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
294 'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}),
295 'company_id': fields.many2one('res.company', 'Company', required=True),
296 'check_total': fields.float('Total', digits=(16, int(config['price_accuracy'])), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
297 'reconciled': fields.function(_reconciled, method=True, string='Paid/Reconciled', type='boolean',
299 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
300 'account.move.line': (_get_invoice_from_line, None, 50),
301 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
302 }, help="The account moves of the invoice have been reconciled with account moves of the payment(s)."),
303 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',
304 help='The bank account to pay to or to be paid from'),
305 'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Move Lines'),
306 'residual': fields.function(_amount_residual, method=True, digits=(16, int(config['price_accuracy'])),string='Residual',
308 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
309 'account.invoice.tax': (_get_invoice_tax, None, 50),
310 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
311 'account.move.line': (_get_invoice_from_line, None, 50),
312 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
314 help="Remaining amount due."),
315 'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'),
316 'move_name': fields.char('Account Move', size=64),
317 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position')
321 #'date_invoice': lambda *a: time.strftime('%Y-%m-%d'),
322 'state': lambda *a: 'draft',
323 'journal_id': _get_journal,
324 'currency_id': _get_currency,
325 'company_id': lambda self, cr, uid, context: \
326 self.pool.get('res.users').browse(cr, uid, uid,
327 context=context).company_id.id,
328 'reference_type': lambda *a: 'none',
329 'check_total': lambda *a: 0.0,
332 def unlink(self, cr, uid, ids, context=None):
333 invoices = self.read(cr, uid, ids, ['state'])
336 if t['state'] in ('draft', 'cancel'):
337 unlink_ids.append(t['id'])
339 raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
340 osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
343 # def get_invoice_address(self, cr, uid, ids):
344 # res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
347 def onchange_partner_id(self, cr, uid, ids, type, partner_id,
348 date_invoice=False, payment_term=False, partner_bank_id=False):
349 invoice_addr_id = False
350 contact_addr_id = False
351 partner_payment_term = False
354 fiscal_position = False
356 opt = [('uid', str(uid))]
359 opt.insert(0, ('id', partner_id))
360 res = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['contact', 'invoice'])
361 contact_addr_id = res['contact']
362 invoice_addr_id = res['invoice']
363 p = self.pool.get('res.partner').browse(cr, uid, partner_id)
364 if type in ('out_invoice', 'out_refund'):
365 acc_id = p.property_account_receivable.id
367 acc_id = p.property_account_payable.id
368 fiscal_position = p.property_account_position and p.property_account_position.id or False
369 partner_payment_term = p.property_payment_term and p.property_payment_term.id or False
371 bank_id = p.bank_ids[0].id
374 'address_contact_id': contact_addr_id,
375 'address_invoice_id': invoice_addr_id,
376 'account_id': acc_id,
377 'payment_term': partner_payment_term,
378 'fiscal_position': fiscal_position
382 if type in ('in_invoice', 'in_refund'):
383 result['value']['partner_bank'] = bank_id
385 if partner_bank_id != bank_id:
386 to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
387 result['value'].update(to_update['value'])
390 def onchange_currency_id(self, cr, uid, ids, curr_id):
393 def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice):
394 if not payment_term_id:
397 pt_obj= self.pool.get('account.payment.term')
398 if not date_invoice :
399 date_invoice = time.strftime('%Y-%m-%d')
401 pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
404 pterm_list = [line[0] for line in pterm_list]
406 res= {'value':{'date_due': pterm_list[-1]}}
408 raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
412 def onchange_invoice_line(self, cr, uid, ids, lines):
415 def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
418 # go from canceled state to draft state
419 def action_cancel_draft(self, cr, uid, ids, *args):
420 self.write(cr, uid, ids, {'state':'draft'})
421 wf_service = netsvc.LocalService("workflow")
423 wf_service.trg_create(uid, 'account.invoice', inv_id, cr)
429 # return the ids of the move lines which has the same account than the invoice
431 def move_line_id_payment_get(self, cr, uid, ids, *args):
433 if not ids: return res
436 from account_move_line l \
437 left join account_invoice i on (i.move_id=l.move_id) \
438 where i.id in ('+','.join(map(str,ids))+') and l.account_id=i.account_id')
439 res = map(lambda x: x[0], cr.fetchall())
442 def copy(self, cr, uid, id, default=None, context=None):
445 default = default.copy()
446 default.update({'state':'draft', 'number':False, 'move_id':False, 'move_name':False,})
447 if 'date_invoice' not in default:
448 default['date_invoice'] = False
449 if 'date_due' not in default:
450 default['date_due'] = False
451 return super(account_invoice, self).copy(cr, uid, id, default, context)
453 def test_paid(self, cr, uid, ids, *args):
454 res = self.move_line_id_payment_get(cr, uid, ids)
459 cr.execute('select reconcile_id from account_move_line where id=%s', (id,))
460 ok = ok and bool(cr.fetchone()[0])
463 def button_reset_taxes(self, cr, uid, ids, context=None):
466 ait_obj = self.pool.get('account.invoice.tax')
468 cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
469 partner = self.browse(cr, uid, id,context=context).partner_id
471 context.update({'lang': partner.lang})
472 for taxe in ait_obj.compute(cr, uid, id, context=context).values():
473 ait_obj.create(cr, uid, taxe)
474 # Update the stored value (fields.function), so we write to trigger recompute
475 self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=context)
476 # self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
479 def button_compute(self, cr, uid, ids, context=None, set_total=False):
480 self.button_reset_taxes(cr, uid, ids, context)
481 for inv in self.browse(cr, uid, ids):
483 self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
486 def _convert_ref(self, cr, uid, ref):
487 return (ref or '').replace('/','')
489 def _get_analytic_lines(self, cr, uid, id):
490 inv = self.browse(cr, uid, [id])[0]
491 cur_obj = self.pool.get('res.currency')
493 company_currency = inv.company_id.currency_id.id
494 if inv.type in ('out_invoice', 'in_refund'):
499 iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
501 if il['account_analytic_id']:
502 if inv.type in ('in_invoice', 'in_refund'):
505 ref = self._convert_ref(cr, uid, inv.number)
506 il['analytic_lines'] = [(0,0, {
508 'date': inv['date_invoice'],
509 'account_id': il['account_analytic_id'],
510 'unit_amount': il['quantity'],
511 'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign,
512 'product_id': il['product_id'],
513 'product_uom_id': il['uos_id'],
514 'general_account_id': il['account_id'],
515 'journal_id': self._get_journal_analytic(cr, uid, inv.type),
520 def action_date_assign(self, cr, uid, ids, *args):
521 for inv in self.browse(cr, uid, ids):
522 res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
523 if res and res['value']:
524 self.write(cr, uid, [inv.id], res['value'])
527 def action_move_create(self, cr, uid, ids, *args):
528 ait_obj = self.pool.get('account.invoice.tax')
529 cur_obj = self.pool.get('res.currency')
531 for inv in self.browse(cr, uid, ids):
535 if not inv.date_invoice:
536 self.write(cr, uid, [inv.id], {'date_invoice':time.strftime('%Y-%m-%d')})
537 company_currency = inv.company_id.currency_id.id
538 # create the analytical lines
539 line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
540 # one move line per invoice line
541 iml = self._get_analytic_lines(cr, uid, inv.id)
542 # check if taxes are all computed
544 context.update({'lang': inv.partner_id.lang})
545 compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
547 for tax in compute_taxes.values():
548 ait_obj.create(cr, uid, tax)
551 for tax in inv.tax_line:
554 key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
556 if not key in compute_taxes:
557 raise osv.except_osv(_('Warning !'), _('Global taxes defined, but are not in invoice lines !'))
558 base = compute_taxes[key]['base']
559 if abs(base - tax.base) > inv.company_id.currency_id.rounding:
560 raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base'))
561 for key in compute_taxes:
562 if not key in tax_key:
563 raise osv.except_osv(_('Warning !'), _('Taxes missing !'))
565 if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
566 raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
568 # one move line per tax line
569 iml += ait_obj.move_line_get(cr, uid, inv.id)
571 if inv.type in ('in_invoice', 'in_refund'):
574 ref = self._convert_ref(cr, uid, inv.number)
576 diff_currency_p = inv.currency_id.id <> company_currency
577 # create one move line for the total and possibly adjust the other lines amount
581 if inv.currency_id.id != company_currency:
582 i['currency_id'] = inv.currency_id.id
583 i['amount_currency'] = i['price']
584 i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
585 company_currency, i['price'],
586 context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')})
588 i['amount_currency'] = False
589 i['currency_id'] = False
591 if inv.type in ('out_invoice','in_refund'):
593 total_currency += i['amount_currency'] or i['price']
594 i['price'] = - i['price']
597 total_currency -= i['amount_currency'] or i['price']
598 acc_id = inv.account_id.id
600 name = inv['name'] or '/'
603 totlines = self.pool.get('account.payment.term').compute(cr,
604 uid, inv.payment_term.id, total, inv.date_invoice or False)
606 res_amount_currency = total_currency
609 if inv.currency_id.id != company_currency:
610 amount_currency = cur_obj.compute(cr, uid,
611 company_currency, inv.currency_id.id, t[1])
613 amount_currency = False
615 # last line add the diff
616 res_amount_currency -= amount_currency or 0
618 if i == len(totlines):
619 amount_currency += res_amount_currency
625 'account_id': acc_id,
626 'date_maturity': t[0],
627 'amount_currency': diff_currency_p \
628 and amount_currency or False,
629 'currency_id': diff_currency_p \
630 and inv.currency_id.id or False,
638 'account_id': acc_id,
639 'date_maturity' : inv.date_due or False,
640 'amount_currency': diff_currency_p \
641 and total_currency or False,
642 'currency_id': diff_currency_p \
643 and inv.currency_id.id or False,
647 date = inv.date_invoice or time.strftime('%Y-%m-%d')
648 part = inv.partner_id.id
650 line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml)
652 if inv.journal_id.group_invoice_lines:
655 tmp = str(l['account_id'])
656 tmp += '-'+str(l.get('tax_code_id',"False"))
657 tmp += '-'+str(l.get('product_id',"False"))
658 tmp += '-'+str(l.get('analytic_account_id',"False"))
659 tmp += '-'+str(l.get('date_maturity',"False"))
662 am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
663 line2[tmp]['debit'] = (am > 0) and am or 0.0
664 line2[tmp]['credit'] = (am < 0) and -am or 0.0
665 line2[tmp]['tax_amount'] += l['tax_amount']
666 line2[tmp]['analytic_lines'] += l['analytic_lines']
670 for key, val in line2.items():
671 line.append((0,0,val))
673 journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']})
674 journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
675 if journal.centralisation:
676 raise osv.except_osv(_('UserError'),
677 _('Cannot create invoice move on centralised journal'))
678 move = {'ref': inv.number, 'line_id': line, 'journal_id': journal_id, 'date': date}
679 period_id=inv.period_id and inv.period_id.id or False
681 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'))])
683 period_id=period_ids[0]
685 move['period_id'] = period_id
687 i[2]['period_id'] = period_id
689 move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
690 new_move_name = self.pool.get('account.move').browse(cr, uid, move_id).name
691 # make the invoice point to that move
692 self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name})
693 self.pool.get('account.move').post(cr, uid, [move_id])
694 self._log_event(cr, uid, ids)
697 def line_get_convert(self, cr, uid, x, part, date, context=None):
699 'date_maturity': x.get('date_maturity', False),
701 'name':x['name'][:64],
703 'debit':x['price']>0 and x['price'],
704 'credit':x['price']<0 and -x['price'],
705 'account_id':x['account_id'],
706 'analytic_lines':x.get('analytic_lines', []),
707 'amount_currency':x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
708 'currency_id':x.get('currency_id', False),
709 'tax_code_id': x.get('tax_code_id', False),
710 'tax_amount': x.get('tax_amount', False),
711 'ref':x.get('ref',False),
712 'quantity':x.get('quantity',1.00),
713 'product_id':x.get('product_id', False),
714 'product_uom_id':x.get('uos_id',False),
715 'analytic_account_id':x.get('account_analytic_id',False),
718 def action_number(self, cr, uid, ids, *args):
719 cr.execute('SELECT id, type, number, move_id, reference ' \
720 'FROM account_invoice ' \
721 'WHERE id IN ('+','.join(map(str,ids))+')')
722 obj_inv = self.browse(cr, uid, ids)[0]
723 for (id, invtype, number, move_id, reference) in cr.fetchall():
725 if obj_inv.journal_id.invoice_sequence_id:
726 sid = obj_inv.journal_id.invoice_sequence_id.id
727 number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', {'fiscalyear_id': obj_inv.period_id.fiscalyear_id.id})
729 number = self.pool.get('ir.sequence').get(cr, uid,
730 'account.invoice.' + invtype)
731 if invtype in ('in_invoice', 'in_refund'):
734 ref = self._convert_ref(cr, uid, number)
735 cr.execute('UPDATE account_invoice SET number=%s ' \
736 'WHERE id=%s', (number, id))
737 cr.execute('UPDATE account_move SET ref=%s ' \
738 'WHERE id=%s AND (ref is null OR ref = \'\')',
740 cr.execute('UPDATE account_move_line SET ref=%s ' \
741 'WHERE move_id=%s AND (ref is null OR ref = \'\')',
743 cr.execute('UPDATE account_analytic_line SET ref=%s ' \
744 'FROM account_move_line ' \
745 'WHERE account_move_line.move_id = %s ' \
746 'AND account_analytic_line.move_id = account_move_line.id',
750 def action_cancel(self, cr, uid, ids, *args):
751 account_move_obj = self.pool.get('account.move')
752 invoices = self.read(cr, uid, ids, ['move_id'])
755 account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
756 # delete the move this invoice was pointing to
757 # Note that the corresponding move_lines and move_reconciles
758 # will be automatically deleted too
759 account_move_obj.unlink(cr, uid, [i['move_id'][0]])
760 self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
761 self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
766 def list_distinct_taxes(self, cr, uid, ids):
767 invoices = self.browse(cr, uid, ids)
770 for tax in inv.tax_line:
771 if not tax['name'] in taxes:
772 taxes[tax['name']] = {'name': tax['name']}
773 return taxes.values()
775 def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'):
776 invs = self.read(cr, uid, ids, ['type','partner_id','amount_untaxed'])
778 part=inv['partner_id'] and inv['partner_id'][0]
780 cr.execute('select sum(quantity*price_unit) from account_invoice_line where invoice_id=%s', (inv['id'],))
781 total = inv['amount_untaxed']
782 if inv['type'] in ('in_invoice','in_refund'):
783 partnertype='supplier'
784 eventtype = 'purchase'
787 partnertype = 'customer'
790 if self.pool.get('res.partner.event.type').check(cr, uid, 'invoice_open'):
791 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})
794 def name_get(self, cr, uid, ids, context=None):
798 'out_invoice': 'CI: ',
799 'in_invoice': 'SI: ',
800 'out_refund': 'OR: ',
803 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')]
805 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
812 ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
814 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
815 return self.name_get(cr, user, ids, context)
817 def _refund_cleanup_lines(self, cr, uid, lines):
820 del line['invoice_id']
821 if 'account_id' in line:
822 line['account_id'] = line.get('account_id', False) and line['account_id'][0]
823 if 'product_id' in line:
824 line['product_id'] = line.get('product_id', False) and line['product_id'][0]
826 line['uos_id'] = line.get('uos_id', False) and line['uos_id'][0]
827 if 'invoice_line_tax_id' in line:
828 line['invoice_line_tax_id'] = [(6,0, line.get('invoice_line_tax_id', [])) ]
829 if 'account_analytic_id' in line:
830 line['account_analytic_id'] = line.get('account_analytic_id', False) and line['account_analytic_id'][0]
831 if 'tax_code_id' in line :
832 if isinstance(line['tax_code_id'],tuple) and len(line['tax_code_id']) >0 :
833 line['tax_code_id'] = line['tax_code_id'][0]
834 if 'base_code_id' in line :
835 if isinstance(line['base_code_id'],tuple) and len(line['base_code_id']) >0 :
836 line['base_code_id'] = line['base_code_id'][0]
837 return map(lambda x: (0,0,x), lines)
839 def refund(self, cr, uid, ids, date=None, period_id=None, description=None):
840 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'])
843 for invoice in invoices:
847 'out_invoice': 'out_refund', # Customer Invoice
848 'in_invoice': 'in_refund', # Supplier Invoice
849 'out_refund': 'out_invoice', # Customer Refund
850 'in_refund': 'in_invoice', # Supplier Refund
854 invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
855 invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines)
857 tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line'])
858 tax_lines = filter(lambda l: l['manual'], tax_lines)
859 tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
861 date = time.strftime('%Y-%m-%d')
863 'type': type_dict[invoice['type']],
864 'date_invoice': date,
867 'invoice_line': invoice_lines,
868 'tax_line': tax_lines
872 'period_id': period_id,
878 # take the id part of the tuple returned for many2one fields
879 for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
880 'account_id', 'currency_id', 'payment_term', 'journal_id'):
881 invoice[field] = invoice[field] and invoice[field][0]
882 # create the new invoice
883 new_ids.append(self.create(cr, uid, invoice))
886 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=''):
889 #TODO check if we can use different period for payment and the writeoff line
890 assert len(ids)==1, "Can only pay one invoice at a time"
891 invoice = self.browse(cr, uid, ids[0])
892 src_account_id = invoice.account_id.id
893 # Take the seq as name for move
894 types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
895 direction = types[invoice.type]
896 #take the choosen date
897 if 'date_p' in context and context['date_p']:
898 date=context['date_p']
900 date=time.strftime('%Y-%m-%d')
902 # Take the amount in currency and the currency of the payment
903 if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
904 amount_currency = context['amount_currency']
905 currency_id = context['currency_id']
907 amount_currency = False
910 if invoice.type in ('in_invoice', 'in_refund'):
911 ref = invoice.reference
913 ref = self._convert_ref(cr, uid, invoice.number)
914 # Pay attention to the sign for both debit/credit AND amount_currency
916 'debit': direction * pay_amount>0 and direction * pay_amount,
917 'credit': direction * pay_amount<0 and - direction * pay_amount,
918 'account_id': src_account_id,
919 'partner_id': invoice.partner_id.id,
922 'currency_id':currency_id,
923 'amount_currency':amount_currency and direction * amount_currency or 0.0,
926 'debit': direction * pay_amount<0 and - direction * pay_amount,
927 'credit': direction * pay_amount>0 and direction * pay_amount,
928 'account_id': pay_account_id,
929 'partner_id': invoice.partner_id.id,
932 'currency_id':currency_id,
933 'amount_currency':amount_currency and - direction * amount_currency or 0.0,
937 name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
941 lines = [(0, 0, l1), (0, 0, l2)]
942 move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
943 move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
947 line = self.pool.get('account.move.line')
948 cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')')
949 lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
950 for l in lines+invoice.payment_ids:
951 if l.account_id.id==src_account_id:
952 line_ids.append(l.id)
953 total += (l.debit or 0.0) - (l.credit or 0.0)
954 if (not round(total,int(config['price_accuracy']))) or writeoff_acc_id:
955 self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
957 self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
959 # Update the stored value (fields.function), so we write to trigger recompute
960 self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
964 class account_invoice_line(osv.osv):
965 def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
967 cur_obj=self.pool.get('res.currency')
968 for line in self.browse(cr, uid, ids):
970 res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
971 cur = line.invoice_id.currency_id
972 res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
974 res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),int(config['price_accuracy']))
978 def _price_unit_default(self, cr, uid, context=None):
981 if 'check_total' in context:
982 t = context['check_total']
983 for l in context.get('invoice_line', {}):
984 if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
985 tax_obj = self.pool.get('account.tax')
986 p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
987 t = t - (p * l[2].get('quantity'))
988 taxes = l[2].get('invoice_line_tax_id')
989 if len(taxes[0]) >= 3 and taxes[0][2]:
990 taxes=tax_obj.browse(cr, uid, taxes[0][2])
991 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)):
992 t = t - tax['amount']
996 _name = "account.invoice.line"
997 _description = "Invoice line"
999 'name': fields.char('Description', size=256, required=True),
1000 'origin': fields.char('Origin', size=256, help="Reference of the document that produced this invoice."),
1001 'invoice_id': fields.many2one('account.invoice', 'Invoice Ref', ondelete='cascade', select=True),
1002 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
1003 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
1004 '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."),
1005 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
1006 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True, type="float", digits=(16, int(config['price_accuracy']))),
1007 'quantity': fields.float('Quantity', required=True),
1008 'discount': fields.float('Discount (%)', digits=(16, int(config['price_accuracy']))),
1009 'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
1010 'note': fields.text('Notes'),
1011 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
1014 'quantity': lambda *a: 1,
1015 'discount': lambda *a: 0.0,
1016 'price_unit': _price_unit_default,
1019 def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=None):
1020 tax_obj = self.pool.get('account.tax')
1022 taxes = tax_obj.browse(cr, uid, tax_id)
1023 for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id):
1024 price_unit = price_unit - tax['amount']
1025 return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
1027 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, context=None):
1031 raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
1033 if type in ('in_invoice', 'in_refund'):
1034 return {'domain':{'product_uom':[]}}
1036 return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
1037 part = self.pool.get('res.partner').browse(cr, uid, partner_id)
1038 fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1041 context.update({'lang': lang})
1043 res = self.pool.get('product.product').browse(cr, uid, product, context=context)
1045 if type in ('out_invoice','out_refund'):
1046 a = res.product_tmpl_id.property_account_income.id
1048 a = res.categ_id.property_account_income_categ.id
1050 a = res.product_tmpl_id.property_account_expense.id
1052 a = res.categ_id.property_account_expense_categ.id
1054 a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
1056 result['account_id'] = a
1059 tax_obj = self.pool.get('account.tax')
1060 if type in ('out_invoice', 'out_refund'):
1061 taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
1062 tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1064 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)
1065 tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1066 if type in ('in_invoice', 'in_refund'):
1067 to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context)
1068 result.update(to_update)
1070 result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
1073 result['name'] = res.name
1076 result['uos_id'] = uom or res.uom_id.id or False
1077 if result['uos_id']:
1078 res2 = res.uom_id.category_id.id
1080 domain = {'uos_id':[('category_id','=',res2 )]}
1081 return {'value':result, 'domain':domain}
1083 def move_line_get(self, cr, uid, invoice_id, context=None):
1086 tax_obj = self.pool.get('account.tax')
1087 cur_obj = self.pool.get('res.currency')
1088 ait_obj = self.pool.get('account.invoice.tax')
1089 inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
1090 company_currency = inv.company_id.currency_id.id
1091 cur = inv.currency_id
1093 for line in inv.invoice_line:
1094 mres = self.move_line_get_item(cr, uid, line, context)
1098 tax_code_found= False
1099 for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id,
1100 (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)),
1101 line.quantity, inv.address_invoice_id.id, line.product_id,
1104 if inv.type in ('out_invoice', 'in_invoice'):
1105 tax_code_id = tax['base_code_id']
1106 tax_amount = line.price_subtotal * tax['base_sign']
1108 tax_code_id = tax['ref_base_code_id']
1109 tax_amount = line.price_subtotal * tax['ref_base_sign']
1114 res.append(self.move_line_get_item(cr, uid, line, context))
1115 res[-1]['price'] = 0.0
1116 res[-1]['account_analytic_id'] = False
1117 elif not tax_code_id:
1119 tax_code_found = True
1121 res[-1]['tax_code_id'] = tax_code_id
1122 res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice})
1125 def move_line_get_item(self, cr, uid, line, context=None):
1128 'name': line.name[:64],
1129 'price_unit':line.price_unit,
1130 'quantity':line.quantity,
1131 'price':line.price_subtotal,
1132 'account_id':line.account_id.id,
1133 'product_id':line.product_id.id,
1134 'uos_id':line.uos_id.id,
1135 'account_analytic_id':line.account_analytic_id.id,
1136 'taxes':line.invoice_line_tax_id,
1139 # Set the tax field according to the account and the fiscal position
1141 def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
1144 taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
1145 fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1146 res = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1147 r = {'value':{'invoice_line_tax_id': res}}
1149 account_invoice_line()
1151 class account_invoice_tax(osv.osv):
1152 _name = "account.invoice.tax"
1153 _description = "Invoice Tax"
1155 'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
1156 'name': fields.char('Tax Description', size=64, required=True),
1157 'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
1158 'base': fields.float('Base', digits=(16,int(config['price_accuracy']))),
1159 'amount': fields.float('Amount', digits=(16,int(config['price_accuracy']))),
1160 'manual': fields.boolean('Manual'),
1161 'sequence': fields.integer('Sequence'),
1163 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
1164 'base_amount': fields.float('Base Code Amount', digits=(16,int(config['price_accuracy']))),
1165 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
1166 'tax_amount': fields.float('Tax Code Amount', digits=(16,int(config['price_accuracy']))),
1169 def base_change(self, cr, uid, ids, base,currency_id=False,company_id=False,date_invoice=False):
1170 cur_obj = self.pool.get('res.currency')
1171 company_obj = self.pool.get('res.company')
1172 company_currency=False
1174 company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1175 if currency_id and company_currency:
1176 base = cur_obj.compute(cr, uid, currency_id, company_currency, base, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1177 return {'value': {'base_amount':base}}
1179 def amount_change(self, cr, uid, ids, amount,currency_id=False,company_id=False,date_invoice=False):
1180 cur_obj = self.pool.get('res.currency')
1181 company_obj = self.pool.get('res.company')
1182 company_currency=False
1184 company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1185 if currency_id and company_currency:
1186 amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1187 return {'value': {'tax_amount':amount}}
1191 'manual': lambda *a: 1,
1192 'base_amount': lambda *a: 0.0,
1193 'tax_amount': lambda *a: 0.0,
1195 def compute(self, cr, uid, invoice_id, context={}):
1197 tax_obj = self.pool.get('account.tax')
1198 cur_obj = self.pool.get('res.currency')
1199 inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context)
1200 cur = inv.currency_id
1201 company_currency = inv.company_id.currency_id.id
1203 for line in inv.invoice_line:
1204 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):
1206 val['invoice_id'] = inv.id
1207 val['name'] = tax['name']
1208 val['amount'] = tax['amount']
1209 val['manual'] = False
1210 val['sequence'] = tax['sequence']
1211 val['base'] = tax['price_unit'] * line['quantity']
1213 if inv.type in ('out_invoice','in_invoice'):
1214 val['base_code_id'] = tax['base_code_id']
1215 val['tax_code_id'] = tax['tax_code_id']
1216 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)
1217 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)
1218 val['account_id'] = tax['account_collected_id'] or line.account_id.id
1220 val['base_code_id'] = tax['ref_base_code_id']
1221 val['tax_code_id'] = tax['ref_tax_code_id']
1222 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)
1223 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)
1224 val['account_id'] = tax['account_paid_id'] or line.account_id.id
1226 key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
1227 if not key in tax_grouped:
1228 tax_grouped[key] = val
1230 tax_grouped[key]['amount'] += val['amount']
1231 tax_grouped[key]['base'] += val['base']
1232 tax_grouped[key]['base_amount'] += val['base_amount']
1233 tax_grouped[key]['tax_amount'] += val['tax_amount']
1235 for t in tax_grouped.values():
1236 t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
1237 t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
1238 t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
1241 def move_line_get(self, cr, uid, invoice_id):
1243 cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%s', (invoice_id,))
1244 for t in cr.dictfetchall():
1245 if not t['amount'] \
1246 and not t['tax_code_id'] \
1247 and not t['tax_amount']:
1252 'price_unit': t['amount'],
1254 'price': t['amount'] or 0.0,
1255 'account_id': t['account_id'],
1256 'tax_code_id': t['tax_code_id'],
1257 'tax_amount': t['tax_amount']
1260 account_invoice_tax()
1262 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: