[FIX] account: residual amount on invoice wrongly computed when payment term creating...
[odoo/odoo.git] / addons / account / invoice.py
index f9d53f2..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 _
 
@@ -63,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:
@@ -101,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 + lines.debit
-            res[inv.id] = to_pay - abs(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:
@@ -118,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:
@@ -126,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):
@@ -148,10 +170,12 @@ 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
@@ -212,10 +236,10 @@ class account_invoice(osv.osv):
             ('cancel','Cancelled')
         ],'State', select=True, readonly=True),
 
-        'date_invoice': fields.date('Date Invoiced', 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."),
+                "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)]}),
@@ -223,52 +247,53 @@ class account_invoice(osv.osv):
             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 date."),
+        '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={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50),
+                '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),
             },
@@ -287,40 +312,37 @@ 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 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):
-        for inv in self.browse(cr, uid, ids, context):
-            print inv.name, inv.amount_total
-            for line in inv.invoice_line:
-                print line.price_subtotal
-#        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) that are already opened or paid !'))
-#        osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
-#        return True
-
-    #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) that are already opened or paid !'))
-    #    osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
-    #    return True
+        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) 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):
 #       res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
 #       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
@@ -357,15 +379,7 @@ class account_invoice(osv.osv):
         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
@@ -378,7 +392,6 @@ 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')
 
@@ -390,12 +403,13 @@ class account_invoice(osv.osv):
             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
@@ -412,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):
@@ -446,15 +461,17 @@ class account_invoice(osv.osv):
     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,))
-            partner = self.browse(cr, uid, id).partner_id
-            context.update({'lang': partner.lang})
-            for taxe in ait_obj.compute(cr, uid, id, context=context).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, {}, context=context)    
+        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
 
@@ -499,6 +516,23 @@ 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')
@@ -515,9 +549,9 @@ class account_invoice(osv.osv):
             # one move line per invoice line
             iml = self._get_analytic_lines(cr, uid, inv.id)
             # check if taxes are all computed
-
-            context.update({'lang': inv.partner_id.lang})
-            compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
+            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)
@@ -628,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
@@ -649,6 +684,9 @@ class account_invoice(osv.osv):
             if journal.centralisation:
                 raise osv.except_osv(_('UserError'),
                         _('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:
@@ -660,11 +698,13 @@ class account_invoice(osv.osv):
                 for i in line:
                     i[2]['period_id'] = period_id
 
-            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
 
@@ -691,17 +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:
+                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', {'fiscalyear_id': obj_inv.period_id.fiscalyear_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(cr, uid,
-                            'account.invoice.' + invtype)
+                    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:
@@ -723,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]])
@@ -731,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
@@ -788,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']
@@ -826,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({
@@ -872,19 +928,39 @@ 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,
         }
 
         if not name:
@@ -893,19 +969,23 @@ class account_invoice(osv.osv):
         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+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)
@@ -918,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'))
@@ -950,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'),
@@ -978,14 +1065,14 @@ class account_invoice_line(osv.osv):
             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)
         fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
 
-        lang=part.lang
-        context.update({'lang': lang})
+        if part.lang:
+            context.update({'lang': part.lang})
         result = {}
         res = self.pool.get('product.product').browse(cr, uid, product, context=context)
 
@@ -1011,16 +1098,17 @@ class account_invoice_line(osv.osv):
             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, qty, address_invoice_id, product, partner_id, context=context)
+            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.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 :
@@ -1102,20 +1190,37 @@ 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 account basis of the tax declaration."),
-        'base_amount': fields.float('Base Code Amount', digits=(16,2)),
+        '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,2)),
+        '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,
@@ -1163,6 +1268,7 @@ 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'])