[FIX] account: residual amount on invoice wrongly computed when payment term creating...
[odoo/odoo.git] / addons / account / invoice.py
index 64a1c31..696e75d 100644 (file)
 ##############################################################################
 
 import time
+from operator import itemgetter
+
 import netsvc
 from osv import fields, osv
-import ir
+from osv.orm import except_orm
 import pooler
-import mx.DateTime
-from mx.DateTime import RelativeDateTime
 from tools import config
 from tools.translate import _
 
+class fiscalyear_seq(osv.osv):
+    _name = "fiscalyear.seq"
+    _description = "Maintains Invoice sequences with Fiscal Year"
+    _rec_name = 'fiscalyear_id'
+    _columns = {
+        'journal_id': fields.many2one('account.journal', 'Journal'),
+        'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year',required=True),
+        'sequence_id':fields.many2one('ir.sequence', 'Sequence',required=True),
+    }
+
+fiscalyear_seq()
+
 class account_invoice(osv.osv):
     def _amount_all(self, cr, uid, ids, name, args, context=None):
         res = {}
@@ -51,8 +63,9 @@ class account_invoice(osv.osv):
             context = {}
         type_inv = context.get('type', 'out_invoice')
         type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
+        refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True}
         journal_obj = self.pool.get('account.journal')
-        res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale'))], limit=1)
+        res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')), ('refund_journal', '=', refund_journal.get(type_inv, False))], limit=1)
         if res:
             return res[0]
         else:
@@ -70,7 +83,7 @@ class account_invoice(osv.osv):
         tt = type2journal.get(type_inv, 'sale')
         result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
         if not result:
-            raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal of type '%s' !") % (tt,))
+            raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s' !") % (tt,))
         return result[0]
 
     def _get_type(self, cr, uid, context=None):
@@ -89,16 +102,34 @@ class account_invoice(osv.osv):
         return [('none', _('Free Reference'))]
 
     def _amount_residual(self, cr, uid, ids, name, args, context=None):
+        if context is None:
+            context = {}
         res = {}
         data_inv = self.browse(cr, uid, ids)
+        cur_obj = self.pool.get('res.currency')
         for inv in data_inv:
-            paid_amt = 0.0
-            to_pay = inv.amount_total
-            for lines in inv.move_lines:
-                paid_amt = paid_amt + lines.credit
-            res[inv.id] = to_pay - paid_amt
+            if inv.reconciled: 
+                res[inv.id] = 0.0
+                continue
+            inv_total = inv.amount_total
+            context_unreconciled = context.copy()
+            for lines in inv.payment_ids:
+                if lines.amount_currency and lines.currency_id.id == inv.currency_id.id:
+                   if inv.type in ('out_invoice','in_refund'):
+                        inv_total += lines.amount_currency
+                   else:
+                        inv_total -= lines.amount_currency
+                else:
+                   context_unreconciled.update({'date': lines.date})
+                   amount_in_invoice_currency = cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id,abs(lines.debit-lines.credit),round=False,context=context_unreconciled)
+                   inv_total -= amount_in_invoice_currency
+
+            result = inv_total 
+            res[inv.id] =  self.pool.get('res.currency').round(cr, uid, inv.currency_id, result)
         return res
 
+    #This function is called by the fields.function move_lines, which is probably unused now.
+    #This function is also wrongly computed: you should use the one on the field payment_ids instead
     def _get_lines(self, cr, uid, ids, name, arg, context=None):
         res = {}
         for id in ids:
@@ -106,7 +137,9 @@ class account_invoice(osv.osv):
             if not move_lines:
                 res[id] = []
                 continue
+            res[id] = []
             data_lines = self.pool.get('account.move.line').browse(cr,uid,move_lines)
+            partial_ids = []# Keeps the track of ids where partial payments are done with payment terms
             for line in data_lines:
                 ids_line = []
                 if line.reconcile_id:
@@ -114,7 +147,8 @@ class account_invoice(osv.osv):
                 elif line.reconcile_partial_id:
                     ids_line = line.reconcile_partial_id.line_partial_ids
                 l = map(lambda x: x.id, ids_line)
-                res[id]=[x for x in l if x <> line.id]
+                partial_ids.append(line.id)
+                res[id] =[x for x in l if x <> line.id and x not in partial_ids]
         return res
 
     def _get_invoice_line(self, cr, uid, ids, context=None):
@@ -136,15 +170,44 @@ class account_invoice(osv.osv):
             src = []
             lines = []
             for m in self.pool.get('account.move.line').browse(cr, uid, moves, context):
+                temp_lines = []#Added temp list to avoid duplicate records
                 if m.reconcile_id:
-                    lines += map(lambda x: x.id, m.reconcile_id.line_id)
+                    temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
                 elif m.reconcile_partial_id:
-                    lines += map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
+                    temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
+                lines += [x for x in temp_lines if x not in lines]
                 src.append(m.id)
             lines = filter(lambda x: x not in src, lines)
             result[invoice.id] = lines
         return result
 
+    def _get_invoice_from_line(self, cr, uid, ids, context={}):
+        move = {}
+        for line in self.pool.get('account.move.line').browse(cr, uid, ids):
+            if line.reconcile_partial_id:
+                for line2 in line.reconcile_partial_id.line_partial_ids:
+                    move[line2.move_id.id] = True
+            if line.reconcile_id:
+                for line2 in line.reconcile_id.line_id:
+                    move[line2.move_id.id] = True
+        invoice_ids = []
+        if move:
+            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
+        return invoice_ids
+
+    def _get_invoice_from_reconcile(self, cr, uid, ids, context={}):
+        move = {}
+        for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
+            for line in r.line_partial_ids:
+                move[line.move_id.id] = True
+            for line in r.line_id:
+                move[line.move_id.id] = True
+        
+        invoice_ids = []
+        if move:
+            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
+        return invoice_ids
+
     _name = "account.invoice"
     _description = 'Invoice'
     _order = "number"
@@ -158,11 +221,11 @@ class account_invoice(osv.osv):
             ('in_refund','Supplier Refund'),
             ],'Type', readonly=True, select=True),
 
-        'number': fields.char('Invoice Number', size=32, readonly=True, help="Uniq number of the invoice, computed automatically when the invoice is created."),
+        'number': fields.char('Invoice Number', size=32, readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."),
         'reference': fields.char('Invoice Reference', size=64, help="The partner reference of this invoice."),
         'reference_type': fields.selection(_get_reference_type, 'Reference Type',
             required=True),
-        'comment': fields.text('Additionnal Information'),
+        'comment': fields.text('Additional Information'),
 
         'state': fields.selection([
             ('draft','Draft'),
@@ -170,64 +233,74 @@ class account_invoice(osv.osv):
             ('proforma2','Pro-forma'),
             ('open','Open'),
             ('paid','Done'),
-            ('cancel','Canceled')
+            ('cancel','Cancelled')
         ],'State', select=True, readonly=True),
 
-        'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}),
-        'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]}),
-
+        'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
+        'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]},
+            help="If you use payment terms, the due date will be computed automatically at the generation "\
+                "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."),
         'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
         'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
         'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
-
-        'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]} ),
-
-        'period_id': fields.many2one('account.period', 'Force Period', help="Keep empty to use the period of the validation date."),
+        'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]},
+            help="If you use payment terms, the due date will be computed automatically at the generation "\
+                "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."),
+        '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)]}),
 
         'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
         'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
         'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
 
         'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True, help="Link to the automatically generated account moves."),
-        'amount_untaxed': fields.function(_amount_all, method=True, digits=(16,2),string='Untaxed',
+        'amount_untaxed': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])),string='Untaxed',
             store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
                 'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, None, 20),
+                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
             },
             multi='all'),
-        'amount_tax': fields.function(_amount_all, method=True, digits=(16,2), string='Tax',
+        'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Tax',
             store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
                 'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, None, 20),
+                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
             },
             multi='all'),
-        'amount_total': fields.function(_amount_all, method=True, digits=(16,2), string='Total',
+        'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
             store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
                 'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, None, 20),
+                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
             },
             multi='all'),
         'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
         'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}),
         'company_id': fields.many2one('res.company', 'Company', required=True),
-        'check_total': fields.float('Total', digits=(16,2), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
+        'check_total': fields.float('Total', digits=(16, int(config['price_accuracy'])), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
         'reconciled': fields.function(_reconciled, method=True, string='Paid/Reconciled', type='boolean',
-            store=True, help="The account moves of the invoice have been reconciled with account moves of the payment(s)."),
+            store={
+                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
+                'account.move.line': (_get_invoice_from_line, None, 50),
+                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
+            }, help="The account moves of the invoice have been reconciled with account moves of the payment(s)."),
         'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',
             help='The bank account to pay to or to be paid from'),
+        #this field is probably unused, and wrongly computed. Use payment_ids instead.
         'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Move Lines'),
-        'residual': fields.function(_amount_residual, method=True, digits=(16,2),string='Residual',
+        'residual': fields.function(_amount_residual, method=True, digits=(16, int(config['price_accuracy'])),string='Residual',
             store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50),
+                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
                 'account.invoice.tax': (_get_invoice_tax, None, 50),
-                'account.invoice.line': (_get_invoice_line, None, 50),
+                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
+                'account.move.line': (_get_invoice_from_line, None, 50),
+                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
             },
             help="Remaining amount due."),
         'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'),
         'move_name': fields.char('Account Move', size=64),
+        'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position')
     }
     _defaults = {
         'type': _get_type,
@@ -239,17 +312,29 @@ class account_invoice(osv.osv):
                 self.pool.get('res.users').browse(cr, uid, uid,
                     context=context).company_id.id,
         'reference_type': lambda *a: 'none',
+        'check_total': lambda *a: 0.0,
     }
-
-    def unlink(self, cr, uid, ids):
+    
+    def create(self, cr, uid, vals, context={}):
+        try:
+            res = super(account_invoice, self).create(cr, uid, vals, context)
+            return res
+        except Exception,e:
+            if '"journal_id" viol' in e.args[0]:
+                raise except_orm(_('Configuration Error!'),
+                     _('There is no Accounting Journal of type Sale/Purchase defined!'))
+            else:
+                raise except_orm(_('UnknownError'), str(e))
+            
+    def unlink(self, cr, uid, ids, context=None):
         invoices = self.read(cr, uid, ids, ['state'])
         unlink_ids = []
         for t in invoices:
             if t['state'] in ('draft', 'cancel'):
                 unlink_ids.append(t['id'])
             else:
-                raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) which are already opened or paid !'))
-        osv.osv.unlink(self, cr, uid, unlink_ids)
+                raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
+        osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
         return True
 
 #   def get_invoice_address(self, cr, uid, ids):
@@ -257,12 +342,13 @@ class account_invoice(osv.osv):
 #       return [{}]
 
     def onchange_partner_id(self, cr, uid, ids, type, partner_id,
-            date_invoice=False, payment_term=False, partner_bank_id=False):
+            date_invoice=False, payment_term=False, partner_bank=False):
         invoice_addr_id = False
         contact_addr_id = False
         partner_payment_term = False
         acc_id = False
         bank_id = False
+        fiscal_position = False
 
         opt = [('uid', str(uid))]
         if partner_id:
@@ -276,7 +362,7 @@ class account_invoice(osv.osv):
                 acc_id = p.property_account_receivable.id
             else:
                 acc_id = p.property_account_payable.id
-
+            fiscal_position = p.property_account_position and p.property_account_position.id or False
             partner_payment_term = p.property_payment_term and p.property_payment_term.id or False
             if p.bank_ids:
                 bank_id = p.bank_ids[0].id
@@ -286,21 +372,14 @@ class account_invoice(osv.osv):
             'address_invoice_id': invoice_addr_id,
             'account_id': acc_id,
             'payment_term': partner_payment_term,
+            'fiscal_position': fiscal_position
             }
         }
 
         if type in ('in_invoice', 'in_refund'):
             result['value']['partner_bank'] = bank_id
 
-        if payment_term != partner_payment_term:
-            if partner_payment_term:
-                to_update = self.onchange_payment_term_date_invoice(
-                    cr,uid,ids,partner_payment_term,date_invoice)
-                result['value'].update(to_update['value'])
-            else:
-                result['value']['date_due'] = False
-
-        if partner_bank_id != bank_id:
+        if partner_bank != bank_id:
             to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
             result['value'].update(to_update['value'])
         return result
@@ -313,23 +392,24 @@ class account_invoice(osv.osv):
             return {}
         res={}
         pt_obj= self.pool.get('account.payment.term')
-
         if not date_invoice :
             date_invoice = time.strftime('%Y-%m-%d')
 
-        pterm_list= pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
+        pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
 
         if pterm_list:
             pterm_list = [line[0] for line in pterm_list]
             pterm_list.sort()
             res= {'value':{'date_due': pterm_list[-1]}}
+        else:
+             raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
 
         return res
 
     def onchange_invoice_line(self, cr, uid, ids, lines):
         return {}
 
-    def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
+    def onchange_partner_bank(self, cursor, user, ids, partner_bank):
         return {'value': {}}
 
     # go from canceled state to draft state
@@ -346,14 +426,15 @@ class account_invoice(osv.osv):
     # return the ids of the move lines which has the same account than the invoice
     # whose id is in ids
     def move_line_id_payment_get(self, cr, uid, ids, *args):
-        ml = self.pool.get('account.move.line')
         res = []
-        for inv in self.read(cr, uid, ids, ['move_id','account_id']):
-            if inv['move_id']:
-                move_line_ids = ml.search(cr, uid, [('move_id', '=', inv['move_id'][0])])
-                for line in ml.read(cr, uid, move_line_ids, ['account_id']):
-                    if line['account_id']==inv['account_id']:
-                        res.append(line['id'])
+        if not ids: return res
+        cr.execute('SELECT l.id '\
+                   'FROM account_move_line l '\
+                   'LEFT JOIN account_invoice i ON (i.move_id=l.move_id) '\
+                   'WHERE i.id IN %s '\
+                   'AND l.account_id=i.account_id',
+                   (tuple(ids),))
+        res = map(itemgetter(0), cr.fetchall())
         return res
 
     def copy(self, cr, uid, id, default=None, context=None):
@@ -378,11 +459,20 @@ class account_invoice(osv.osv):
         return ok
 
     def button_reset_taxes(self, cr, uid, ids, context=None):
+        if not context:
+            context = {}
+        ctx = context.copy()
         ait_obj = self.pool.get('account.invoice.tax')
         for id in ids:
             cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
-            for taxe in ait_obj.compute(cr, uid, id).values():
+            partner = self.browse(cr, uid, id,context=ctx).partner_id
+            if partner.lang:
+                ctx.update({'lang': partner.lang})
+            for taxe in ait_obj.compute(cr, uid, id, context=ctx).values():
                 ait_obj.create(cr, uid, taxe)
+         # Update the stored value (fields.function), so we write to trigger recompute
+        self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=ctx)    
+#        self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
         return True
 
     def button_compute(self, cr, uid, ids, context=None, set_total=False):
@@ -426,26 +516,42 @@ class account_invoice(osv.osv):
                 })]
         return iml
 
+    def action_date_assign(self, cr, uid, ids, *args):
+        for inv in self.browse(cr, uid, ids):
+            res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
+            if res and res['value']:
+                self.write(cr, uid, [inv.id], res['value'])
+        return True
+    
+    def finalize_invoice_move_lines(self, cr, uid, invoice_browse, move_lines):
+        """finalize_invoice_move_lines(cr, uid, invoice, move_lines) -> move_lines
+        Hook method to be overridden in additional modules to verify and possibly alter the 
+        move lines to be created by an invoice, for special cases.
+        :param invoice_browse: browsable record of the invoice that is generating the move lines
+        :param move_lines: list of dictionaries with the account.move.lines (as for create())
+        :return: the (possibly updated) final move_lines to create for this invoice 
+        """
+        return move_lines
+
     def action_move_create(self, cr, uid, ids, *args):
         ait_obj = self.pool.get('account.invoice.tax')
         cur_obj = self.pool.get('res.currency')
-        acc_obj = self.pool.get('account.account')
-        self.button_compute(cr, uid, ids, context={}, set_total=False)
+        context = {}
         for inv in self.browse(cr, uid, ids):
             if inv.move_id:
                 continue
-            if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
-                raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
+
             if not inv.date_invoice:
                 self.write(cr, uid, [inv.id], {'date_invoice':time.strftime('%Y-%m-%d')})
             company_currency = inv.company_id.currency_id.id
             # create the analytical lines
             line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
-            ils = self.pool.get('account.invoice.line').read(cr, uid, line_ids)
             # one move line per invoice line
             iml = self._get_analytic_lines(cr, uid, inv.id)
             # check if taxes are all computed
-            compute_taxes = ait_obj.compute(cr, uid, inv.id)
+            ctx = context.copy()
+            ctx.update({'lang': inv.partner_id.lang})
+            compute_taxes = ait_obj.compute(cr, uid, inv.id, context=ctx)
             if not inv.tax_line:
                 for tax in compute_taxes.values():
                     ait_obj.create(cr, uid, tax)
@@ -457,7 +563,7 @@ class account_invoice(osv.osv):
                     key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
                     tax_key.append(key)
                     if not key in compute_taxes:
-                        raise osv.except_osv(_('Warning !'), _('Global taxes defined, but not in invoice lines !'))
+                        raise osv.except_osv(_('Warning !'), _('Global taxes defined, but are not in invoice lines !'))
                     base = compute_taxes[key]['base']
                     if abs(base - tax.base) > inv.company_id.currency_id.rounding:
                         raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base'))
@@ -465,6 +571,9 @@ class account_invoice(osv.osv):
                     if not key in tax_key:
                         raise osv.except_osv(_('Warning !'), _('Taxes missing !'))
 
+            if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
+                raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
+
             # one move line per tax line
             iml += ait_obj.move_line_get(cr, uid, inv.id)
 
@@ -477,11 +586,7 @@ class account_invoice(osv.osv):
             # create one move line for the total and possibly adjust the other lines amount
             total = 0
             total_currency = 0
-            key_line=[]
             for i in iml:
-                if 'account_id' in i and 'taxes' in i:
-                    if not (i['account_id'],i['taxes']) in key_line:
-                        key_line.append((i['account_id'],i['taxes']))
                 if inv.currency_id.id != company_currency:
                     i['currency_id'] = inv.currency_id.id
                     i['amount_currency'] = i['price']
@@ -502,36 +607,6 @@ class account_invoice(osv.osv):
             acc_id = inv.account_id.id
 
             name = inv['name'] or '/'
-            iml_temp=[]
-            move_list=[]
-
-            for item in key_line:
-                move_temp={}
-                if acc_obj.browse(cr,uid,item[0]).merge_invoice:
-                    repeat=False
-                    for move_line in iml:
-                        if (move_line.has_key('account_id') and move_line['account_id']==item[0]) and ('taxes' in move_line and move_line['taxes']==item[1]):
-                            move_list.append(move_line)
-                            if repeat:
-                                for key in move_line:
-                                    if key in ['name','amount_currency','price_unit','price','quantity']:
-                                        if key=='name':
-                                            move_temp[key]=move_temp[key] + "," +move_line[key]
-                                        else:
-                                            move_temp[key] +=move_line[key]
-                            else:
-                                for key in move_line:
-                                    move_temp[key]=move_line[key]
-                                repeat=True
-                if move_temp:
-                    iml_temp.append(move_temp)
-
-            if len(iml_temp)<len(move_list):
-                for old_elem in move_list:
-                    iml.remove(old_elem)
-                for new_elem in iml_temp:
-                    iml.append(new_elem)
-
             totlines = False
             if inv.payment_term:
                 totlines = self.pool.get('account.payment.term').compute(cr,
@@ -587,10 +662,11 @@ class account_invoice(osv.osv):
                 line2 = {}
                 for x, y, l in line:
                     tmp = str(l['account_id'])
-                    tmp += '-'+str('tax_code_id' in l and l['tax_code_id'] or "False")
-                    tmp += '-'+str('product_id' in l and l['product_id'] or "False")
-                    tmp += '-'+str('analytic_account_id' in l and l['analytic_account_id'] or "False")
-
+                    tmp += '-'+str(l.get('tax_code_id',"False"))
+                    tmp += '-'+str(l.get('product_id',"False"))
+                    tmp += '-'+str(l.get('analytic_account_id',"False"))
+                    tmp += '-'+str(l.get('date_maturity',"False"))
+                    
                     if tmp in line2:
                         am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
                         line2[tmp]['debit'] = (am > 0) and am or 0.0
@@ -607,7 +683,10 @@ class account_invoice(osv.osv):
             journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
             if journal.centralisation:
                 raise osv.except_osv(_('UserError'),
-                        _('Can not create invoice move on centralized journal'))
+                        _('Cannot create invoice move on centralised journal'))
+
+            line = self.finalize_invoice_move_lines(cr, uid, inv, line)
+
             move = {'ref': inv.number, 'line_id': line, 'journal_id': journal_id, 'date': date}
             period_id=inv.period_id and inv.period_id.id or False
             if not period_id:
@@ -619,14 +698,13 @@ class account_invoice(osv.osv):
                 for i in line:
                     i[2]['period_id'] = period_id
 
-            if not 'name' in move:
-                move['name'] = inv.name or '/'
-
-            move_id = self.pool.get('account.move').create(cr, uid, move)
+            move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
             new_move_name = self.pool.get('account.move').browse(cr, uid, move_id).name
             # make the invoice point to that move
             self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name})
-            self.pool.get('account.move').post(cr, uid, [move_id])
+            # Pass invoice in context in method post: used if you want to get the same
+            # account move reference when creating the same invoice after a cancelled one:
+            self.pool.get('account.move').post(cr, uid, [move_id], context={'invoice':inv})
         self._log_event(cr, uid, ids)
         return True
 
@@ -635,6 +713,7 @@ class account_invoice(osv.osv):
             'date_maturity': x.get('date_maturity', False),
             'partner_id':part,
             'name':x['name'][:64],
+            'date': date,
             'debit':x['price']>0 and x['price'],
             'credit':x['price']<0 and -x['price'],
             'account_id':x['account_id'],
@@ -652,12 +731,26 @@ class account_invoice(osv.osv):
 
     def action_number(self, cr, uid, ids, *args):
         cr.execute('SELECT id, type, number, move_id, reference ' \
-                'FROM account_invoice ' \
-                'WHERE id IN ('+','.join(map(str,ids))+')')
+                   'FROM account_invoice ' \
+                   'WHERE id IN %s',
+                   (tuple(ids),))
+        obj_inv = self.browse(cr, uid, ids)[0]
         for (id, invtype, number, move_id, reference) in cr.fetchall():
             if not number:
-                number = self.pool.get('ir.sequence').get(cr, uid,
-                        'account.invoice.' + invtype)
+                tmp_context = {
+                    'fiscalyear_id' : obj_inv.period_id.fiscalyear_id.id,
+                }
+                if obj_inv.journal_id.invoice_sequence_id:
+                    sid = obj_inv.journal_id.invoice_sequence_id.id
+                    number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', context=tmp_context)
+                else:
+                    number = self.pool.get('ir.sequence').get_id(cr, uid,
+                                                                 'account.invoice.' + invtype,
+                                                                 'code=%s',
+                                                                 context=tmp_context)
+                if not number:
+                    raise osv.except_osv(_('Warning !'), _('There is no active invoice sequence defined for the journal !'))
+
                 if invtype in ('in_invoice', 'in_refund'):
                     ref = reference
                 else:
@@ -679,7 +772,7 @@ class account_invoice(osv.osv):
 
     def action_cancel(self, cr, uid, ids, *args):
         account_move_obj = self.pool.get('account.move')
-        invoices = self.read(cr, uid, ids, ['move_id'])
+        invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
         for i in invoices:
             if i['move_id']:
                 account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
@@ -687,6 +780,13 @@ class account_invoice(osv.osv):
                 # Note that the corresponding move_lines and move_reconciles
                 # will be automatically deleted too
                 account_move_obj.unlink(cr, uid, [i['move_id'][0]])
+            if i['payment_ids']:
+                account_move_line_obj = self.pool.get('account.move.line')
+                pay_ids = account_move_line_obj.browse(cr, uid , i['payment_ids'])
+                for move_line in pay_ids:
+                    if move_line.reconcile_partial_id and move_line.reconcile_partial_id.line_partial_ids:
+                        raise osv.except_osv(_('Error !'), _('You cannot cancel the Invoice which is Partially Paid! You need to unreconcile concerned payment entries!'))
+
         self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
         self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
         return True
@@ -744,7 +844,7 @@ class account_invoice(osv.osv):
             ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
         return self.name_get(cr, user, ids, context)
 
-    def _refund_cleanup_lines(self, lines):
+    def _refund_cleanup_lines(self, cr, uid, lines):
         for line in lines:
             del line['id']
             del line['invoice_id']
@@ -782,11 +882,11 @@ class account_invoice(osv.osv):
 
 
             invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
-            invoice_lines = self._refund_cleanup_lines(invoice_lines)
+            invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines)
 
             tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line'])
             tax_lines = filter(lambda l: l['manual'], tax_lines)
-            tax_lines = self._refund_cleanup_lines(tax_lines)
+            tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
             if not date :
                 date = time.strftime('%Y-%m-%d')
             invoice.update({
@@ -828,39 +928,64 @@ class account_invoice(osv.osv):
             date=context['date_p']
         else:
             date=time.strftime('%Y-%m-%d')
+            
+        # Take the amount in currency and the currency of the payment
+        if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
+            amount_currency = context['amount_currency']
+            currency_id = context['currency_id']
+        else:
+            amount_currency = False
+            currency_id = False
+        
+        if invoice.type in ('in_invoice', 'in_refund'):
+            ref = invoice.reference
+        else:
+            ref = self._convert_ref(cr, uid, invoice.number)        
+        # Pay attention to the sign for both debit/credit AND amount_currency
         l1 = {
             'debit': direction * pay_amount>0 and direction * pay_amount,
             'credit': direction * pay_amount<0 and - direction * pay_amount,
             'account_id': src_account_id,
             'partner_id': invoice.partner_id.id,
-            'ref':invoice.number,
+            'ref':ref,
+            'date': date,
+            'currency_id':currency_id,
+            'amount_currency':amount_currency and direction * amount_currency or 0.0,
         }
         l2 = {
             'debit': direction * pay_amount<0 and - direction * pay_amount,
             'credit': direction * pay_amount>0 and direction * pay_amount,
             'account_id': pay_account_id,
             'partner_id': invoice.partner_id.id,
-            'ref':invoice.number,
+            'ref':ref,
+            'date': date,
+            'currency_id':currency_id,
+            'amount_currency':amount_currency and - direction * amount_currency or 0.0,
         }
 
-        name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
+        if not name:
+            name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
         l1['name'] = name
         l2['name'] = name
 
         lines = [(0, 0, l1), (0, 0, l2)]
-        move = {'ref': invoice.number, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
-        move_id = self.pool.get('account.move').create(cr, uid, move)
+        move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
+        move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
 
         line_ids = []
         total = 0.0
         line = self.pool.get('account.move.line')
-        cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')')
+        cr.execute('SELECT id FROM account_move_line '\
+                   'WHERE move_id in %s',
+                   ((move_id, invoice.move_id.id),))
         lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
-        for l in lines:
+
+        for l in lines+invoice.payment_ids:
             if l.account_id.id==src_account_id:
                 line_ids.append(l.id)
                 total += (l.debit or 0.0) - (l.credit or 0.0)
-        if (not total) or writeoff_acc_id:
+
+        if (not round(total,int(config['price_accuracy']))) or writeoff_acc_id:
             self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
         else:
             self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
@@ -873,17 +998,24 @@ account_invoice()
 class account_invoice_line(osv.osv):
     def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
         res = {}
+        cur_obj=self.pool.get('res.currency')
         for line in self.browse(cr, uid, ids):
-            res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),2)
+            if line.invoice_id:
+                res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
+                cur = line.invoice_id.currency_id
+                res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
+            else:
+                res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),int(config['price_accuracy']))
         return res
 
+
     def _price_unit_default(self, cr, uid, context=None):
         if context is None:
             context = {}
         if 'check_total' in context:
             t = context['check_total']
             for l in context.get('invoice_line', {}):
-                if len(l) >= 3 and l[2]:
+                if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
                     tax_obj = self.pool.get('account.tax')
                     p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
                     t = t - (p * l[2].get('quantity'))
@@ -905,9 +1037,9 @@ class account_invoice_line(osv.osv):
         'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
         '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."),
         'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
-        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True),
+        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True, type="float", digits=(16, int(config['price_accuracy']))),
         'quantity': fields.float('Quantity', required=True),
-        'discount': fields.float('Discount (%)', digits=(16,2)),
+        'discount': fields.float('Discount (%)', digits=(16, int(config['price_accuracy']))),
         'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
         'note': fields.text('Notes'),
         'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
@@ -926,33 +1058,23 @@ class account_invoice_line(osv.osv):
                 price_unit = price_unit - tax['amount']
         return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
 
-    def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, context=None):
+    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):
         if context is None:
             context = {}
         if not partner_id:
             raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
         if not product:
             if type in ('in_invoice', 'in_refund'):
-                return {'domain':{'product_uom':[]}}
+                return {'value':{}, 'domain':{'product_uom':[]}}
             else:
                 return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
         part = self.pool.get('res.partner').browse(cr, uid, partner_id)
-        lang=part.lang
-        context.update({'lang': lang})
-        res = self.pool.get('product.product').browse(cr, uid, product, context=context)
-        taxep=None
-        tax_obj = self.pool.get('account.tax')
-        if type in ('out_invoice', 'out_refund'):
-            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, part, res.taxes_id)
-        else:
-            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, part, res.supplier_taxes_id)
-        if type in ('in_invoice', 'in_refund'):
-            result = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context)
-        else:
-            result = {'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}
+        fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
 
-        if not name:
-            result['name'] = res.name
+        if part.lang:
+            context.update({'lang': part.lang})
+        result = {}
+        res = self.pool.get('product.product').browse(cr, uid, product, context=context)
 
         if type in ('out_invoice','out_refund'):
             a =  res.product_tmpl_id.property_account_income.id
@@ -963,12 +1085,30 @@ class account_invoice_line(osv.osv):
             if not a:
                 a = res.categ_id.property_account_expense_categ.id
 
-        a = self.pool.get('account.fiscal.position').map_account(cr, uid, part, a)
+        a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
         if a:
             result['account_id'] = a
 
+        taxep=None
+        tax_obj = self.pool.get('account.tax')
+        if type in ('out_invoice', 'out_refund'):
+            taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
+            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
+        else:
+            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)
+            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
+        if type in ('in_invoice', 'in_refund'):
+            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)
+            result.update(to_update)
+        else:
+            result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
+
+        if not name:
+            result['name'] = res.partner_ref
+
         domain = {}
         result['uos_id'] = uom or res.uom_id.id or False
+        result['note'] = res.description
         if result['uos_id']:
             res2 = res.uom_id.category_id.id
             if res2 :
@@ -1031,15 +1171,14 @@ class account_invoice_line(osv.osv):
             'taxes':line.invoice_line_tax_id,
         }
     #
-    # Set the tax field according to the account and the partner
+    # Set the tax field according to the account and the fiscal position
     #
-    def onchange_account_id(self, cr, uid, ids, partner_id,account_id):
-        if not (partner_id and account_id):
+    def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
+        if not account_id:
             return {}
         taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
-        part = self.pool.get('res.partner').browse(cr, uid, partner_id)
-
-        res = self.pool.get('account.fiscal.position').map_tax(cr, uid, part, taxes)
+        fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
+        res = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
         r = {'value':{'invoice_line_tax_id': res}}
         return r
 account_invoice_line()
@@ -1051,31 +1190,48 @@ class account_invoice_tax(osv.osv):
         'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
         'name': fields.char('Tax Description', size=64, required=True),
         'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
-        'base': fields.float('Base', digits=(16,2)),
-        'amount': fields.float('Amount', digits=(16,2)),
+        'base': fields.float('Base', digits=(16,int(config['price_accuracy']))),
+        'amount': fields.float('Amount', digits=(16,int(config['price_accuracy']))),
         'manual': fields.boolean('Manual'),
         'sequence': fields.integer('Sequence'),
 
-        'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The case of the tax declaration."),
-        'base_amount': fields.float('Base Code Amount', digits=(16,2)),
-        'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The case of the tax declaration."),
-        'tax_amount': fields.float('Tax Code Amount', digits=(16,2)),
+        'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
+        'base_amount': fields.float('Base Code Amount', digits=(16,int(config['price_accuracy']))),
+        'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
+        'tax_amount': fields.float('Tax Code Amount', digits=(16,int(config['price_accuracy']))),
     }
-    def base_change(self, cr, uid, ids, base):
+    
+    def base_change(self, cr, uid, ids, base,currency_id=False,company_id=False,date_invoice=False):
+        cur_obj = self.pool.get('res.currency')
+        company_obj = self.pool.get('res.company')
+        company_currency=False
+        if company_id:            
+            company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
+        if currency_id and company_currency:
+            base = cur_obj.compute(cr, uid, currency_id, company_currency, base, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
         return {'value': {'base_amount':base}}
-    def amount_change(self, cr, uid, ids, amount):
+
+    def amount_change(self, cr, uid, ids, amount,currency_id=False,company_id=False,date_invoice=False):
+        cur_obj = self.pool.get('res.currency')
+        company_obj = self.pool.get('res.company')
+        company_currency=False
+        if company_id:
+            company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
+        if currency_id and company_currency:
+            amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
         return {'value': {'tax_amount':amount}}
+    
     _order = 'sequence'
     _defaults = {
         'manual': lambda *a: 1,
         'base_amount': lambda *a: 0.0,
         'tax_amount': lambda *a: 0.0,
     }
-    def compute(self, cr, uid, invoice_id):
+    def compute(self, cr, uid, invoice_id, context={}):
         tax_grouped = {}
         tax_obj = self.pool.get('account.tax')
         cur_obj = self.pool.get('res.currency')
-        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
+        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context)
         cur = inv.currency_id
         company_currency = inv.company_id.currency_id.id
 
@@ -1092,14 +1248,14 @@ class account_invoice_tax(osv.osv):
                 if inv.type in ('out_invoice','in_invoice'):
                     val['base_code_id'] = tax['base_code_id']
                     val['tax_code_id'] = tax['tax_code_id']
-                    val['base_amount'] = 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')})
-                    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')})
+                    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)
+                    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)
                     val['account_id'] = tax['account_collected_id'] or line.account_id.id
                 else:
                     val['base_code_id'] = tax['ref_base_code_id']
                     val['tax_code_id'] = tax['ref_tax_code_id']
-                    val['base_amount'] = 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')})
-                    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')})
+                    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)
+                    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)
                     val['account_id'] = tax['account_paid_id'] or line.account_id.id
 
                 key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
@@ -1112,7 +1268,10 @@ class account_invoice_tax(osv.osv):
                     tax_grouped[key]['tax_amount'] += val['tax_amount']
 
         for t in tax_grouped.values():
+            t['base'] = cur_obj.round(cr, uid, cur, t['base'])
             t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
+            t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
+            t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
         return tax_grouped
 
     def move_line_get(self, cr, uid, invoice_id):