[FIX] account_voucher: don't break non-form views when overriding fields_view_get...
[odoo/odoo.git] / addons / account_voucher / account_voucher.py
index c82cea4..2b63157 100644 (file)
@@ -29,6 +29,15 @@ from tools.translate import _
 
 
 class account_voucher(osv.osv):
+    def _check_paid(self, cr, uid, ids, name, args, context=None):
+        res = {}
+        for voucher in self.browse(cr, uid, ids, context=context):
+            ok = True
+            for line in voucher.move_ids:
+                if (line.account_id.type, 'in', ('receivable', 'payable')) and not line.reconcile_id:
+                    ok = False
+            res[voucher.id] = ok
+        return res
 
     def _get_type(self, cr, uid, context=None):
         if context is None:
@@ -81,13 +90,27 @@ class account_voucher(osv.osv):
             return tax_id
         return False
 
+    def _get_payment_rate_currency(self, cr, uid, context=None):
+        """
+        Return the default value for field payment_rate_currency_id: the currency of the journal
+        if there is one, otherwise the currency of the user's company
+        """
+        if context is None: context = {}
+        journal_pool = self.pool.get('account.journal')
+        journal_id = context.get('journal_id', False)
+        if journal_id:
+            journal = journal_pool.browse(cr, uid, journal_id, context=context)
+            if journal.currency:
+                return journal.currency.id
+        #no journal given in the context, use company currency as default
+        return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
+
     def _get_currency(self, cr, uid, context=None):
         if context is None: context = {}
         journal_pool = self.pool.get('account.journal')
         journal_id = context.get('journal_id', False)
         if journal_id:
             journal = journal_pool.browse(cr, uid, journal_id, context=context)
-#            currency_id = journal.company_id.currency_id.id
             if journal.currency:
                 return journal.currency.id
         return False
@@ -118,38 +141,31 @@ class account_voucher(osv.osv):
     def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
         mod_obj = self.pool.get('ir.model.data')
         if context is None: context = {}
-        if not view_id and context.get('invoice_type', False):
-            if context.get('invoice_type', False) in ('out_invoice', 'out_refund'):
-                result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
-            else:
-                result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
-            result = result and result[1] or False
-            view_id = result
-        if not view_id and view_type == 'form' and context.get('line_type', False):
-            if context.get('line_type', False) == 'customer':
-                result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
-            else:
-                result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
-            result = result and result[1] or False
-            view_id = result
+
+        if view_type == 'form':
+            if not view_id and context.get('invoice_type'):
+                if context.get('invoice_type') in ('out_invoice', 'out_refund'):
+                    result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
+                else:
+                    result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
+                result = result and result[1] or False
+                view_id = result
+            if not view_id and context.get('line_type'):
+                if context.get('line_type') == 'customer':
+                    result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
+                else:
+                    result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
+                result = result and result[1] or False
+                view_id = result
 
         res = super(account_voucher, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
         doc = etree.XML(res['arch'])
-        nodes = doc.xpath("//field[@name='partner_id']")
+
         if context.get('type', 'sale') in ('purchase', 'payment'):
+            nodes = doc.xpath("//field[@name='partner_id']")
             for node in nodes:
                 node.set('domain', "[('supplier', '=', True)]")
-            res['arch'] = etree.tostring(doc)
-        return res
-
-    def _check_paid(self, cr, uid, ids, name, args, context=None):
-        res = {}
-        for voucher in self.browse(cr, uid, ids, context=context):
-            ok = False
-            for line in voucher.move_ids:
-                if (line.account_id.type, 'in', ('receivable', 'payable')) and line.reconcile_id:
-                    ok = True
-            res[voucher.id] = ok
+        res['arch'] = etree.tostring(doc)
         return res
 
     def _compute_writeoff_amount(self, cr, uid, line_dr_ids, line_cr_ids, amount):
@@ -160,15 +176,25 @@ class account_voucher(osv.osv):
             credit += l['amount']
         return abs(amount - abs(credit - debit))
 
-    def onchange_line_ids(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount):
+    def onchange_line_ids(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount, voucher_currency, context=None):
+        context = context or {}
         if not line_dr_ids and not line_cr_ids:
             return {'value':{}}
-        line_dr_ids = [x[2] for x in line_dr_ids]
-        line_cr_ids = [x[2] for x in line_cr_ids]
-        return {'value': {'writeoff_amount': self._compute_writeoff_amount(cr, uid, line_dr_ids, line_cr_ids, amount)}}
+        line_osv = self.pool.get("account.voucher.line")
+        line_dr_ids = resolve_o2m_operations(cr, uid, line_osv, line_dr_ids, ['amount'], context)
+        line_cr_ids = resolve_o2m_operations(cr, uid, line_osv, line_cr_ids, ['amount'], context)
+
+        #loop into the lines to see if there is an amount allocated on a voucher line with a currency different than the voucher currency
+        is_multi_currency = False
+        for voucher_line in line_dr_ids+line_cr_ids:
+            if voucher_line.get('currency_id',False) != voucher_currency:
+                is_multi_currency = True
+                break
+        return {'value': {'writeoff_amount': self._compute_writeoff_amount(cr, uid, line_dr_ids, line_cr_ids, amount), 'is_multi_currency': is_multi_currency}}
 
     def _get_writeoff_amount(self, cr, uid, ids, name, args, context=None):
         if not ids: return {}
+        currency_obj = self.pool.get('res.currency')
         res = {}
         debit = credit = 0.0
         for voucher in self.browse(cr, uid, ids, context=context):
@@ -176,7 +202,24 @@ class account_voucher(osv.osv):
                 debit += l.amount
             for l in voucher.line_cr_ids:
                 credit += l.amount
-            res[voucher.id] =  abs(voucher.amount - abs(credit - debit))
+            currency = voucher.currency_id or voucher.company_id.currency_id
+            res[voucher.id] =  currency_obj.round(cr, uid, currency, abs(voucher.amount - abs(credit - debit)))
+        return res
+
+    def _paid_amount_in_company_currency(self, cr, uid, ids, name, args, context=None):
+        if not ids: return {}
+        res = {}
+        voucher_rate = company_currency_rate = 1.0
+        for voucher in self.browse(cr, uid, ids, context=context):
+            if voucher.currency_id:
+                ctx = context.copy()
+                ctx.update({'date': voucher.date})
+                voucher_rate = self.browse(cr, uid, voucher.id, context=ctx).currency_id.rate
+                if voucher.company_id.currency_id.id == voucher.payment_rate_currency_id.id:
+                    company_currency_rate =  voucher.payment_rate
+                else:
+                    company_currency_rate = voucher.company_id.currency_id.rate
+            res[voucher.id] =  voucher.amount / voucher_rate * company_currency_rate
         return res
 
     _name = 'account.voucher'
@@ -201,8 +244,8 @@ class account_voucher(osv.osv):
             domain=[('type','=','dr')], context={'default_type':'dr'}, readonly=True, states={'draft':[('readonly',False)]}),
         'period_id': fields.many2one('account.period', 'Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
         'narration':fields.text('Notes', readonly=True, states={'draft':[('readonly',False)]}),
-        'currency_id':fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
-#        'currency_id': fields.related('journal_id','currency', type='many2one', relation='res.currency', string='Currency', store=True, readonly=True, states={'draft':[('readonly',False)]}),
+#        'currency_id':fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
+        'currency_id': fields.related('journal_id','currency', type='many2one', relation='res.currency', string='Currency', readonly=True),
         'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
         'state':fields.selection(
             [('draft','Draft'),
@@ -222,22 +265,27 @@ class account_voucher(osv.osv):
         'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True),
         'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}),
         'audit': fields.related('move_id','to_check', type='boolean', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.', relation='account.move', string='To Review'),
+        'paid': fields.function(_check_paid, string='Paid', type='boolean', help="The Voucher has been totally paid."),
         'pay_now':fields.selection([
             ('pay_now','Pay Directly'),
             ('pay_later','Pay Later or Group Funds'),
         ],'Payment', select=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'tax_id':fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}),
+        'tax_id': fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}, domain=[('price_include','=', False)], help="Only for tax excluded from price"),
         'pre_line':fields.boolean('Previous Payments ?', required=False),
         'date_due': fields.date('Due Date', readonly=True, select=True, states={'draft':[('readonly',False)]}),
         'payment_option':fields.selection([
                                            ('without_writeoff', 'Keep Open'),
-                                           ('with_writeoff', 'Reconcile with Write-Off'),
-                                           ], 'Payment Difference', required=True, readonly=True, states={'draft': [('readonly', False)]}),
-        'writeoff_acc_id': fields.many2one('account.account', 'Write-Off account', readonly=True, states={'draft': [('readonly', False)]}),
-        'comment': fields.char('Write-Off Comment', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
+                                           ('with_writeoff', 'Reconcile Payment Balance'),
+                                           ], 'Payment Difference', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="This field helps you to choose what you want to do with the eventual difference between the paid amount and the sum of allocated amounts. You can either choose to keep open this difference on the partner's account, or reconcile it with the payment(s)"),
+        'writeoff_acc_id': fields.many2one('account.account', 'Counterpart Account', readonly=True, states={'draft': [('readonly', False)]}),
+        'comment': fields.char('Counterpart Comment', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'analytic_id': fields.many2one('account.analytic.account','Write-Off Analytic Account', readonly=True, states={'draft': [('readonly', False)]}),
-        'writeoff_amount': fields.function(_get_writeoff_amount, method=True, string='Write-Off Amount', type='float', readonly=True),
-        'paid': fields.function(_check_paid, method=True, string='Paid', type='boolean', help="The Voucher has been totally paid."),
+        'writeoff_amount': fields.function(_get_writeoff_amount, string='Difference Amount', type='float', readonly=True, help="Computed as the difference between the amount stated in the voucher and the sum of allocation on the voucher lines."),
+        'payment_rate_currency_id': fields.many2one('res.currency', 'Payment Rate Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
+        'payment_rate': fields.float('Exchange Rate', digits=(12,6), required=True, readonly=True, states={'draft': [('readonly', False)]},
+            help='The specific rate that will be used, in this voucher, between the selected currency (in \'Payment Rate Currency\' field)  and the voucher currency.'),
+        'paid_amount_in_company_currency': fields.function(_paid_amount_in_company_currency, string='Paid Amount in Company Currency', type='float', readonly=True),
+        'is_multi_currency': fields.boolean('Multi Currency Voucher', help='Fields with internal purpose only that depicts if the voucher is a multi currency one or not'),
     }
     _defaults = {
         'period_id': _get_period,
@@ -256,6 +304,8 @@ class account_voucher(osv.osv):
         'tax_id': _get_tax,
         'payment_option': 'without_writeoff',
         'comment': _('Write-Off'),
+        'payment_rate': 1.0,
+        'payment_rate_currency_id': _get_payment_rate_currency,
     }
 
     def compute_tax(self, cr, uid, ids, context=None):
@@ -305,22 +355,24 @@ class account_voucher(osv.osv):
         return True
 
     def onchange_price(self, cr, uid, ids, line_ids, tax_id, partner_id=False, context=None):
+        context = context or {}
         tax_pool = self.pool.get('account.tax')
         partner_pool = self.pool.get('res.partner')
         position_pool = self.pool.get('account.fiscal.position')
+        line_pool = self.pool.get('account.voucher.line')
         res = {
             'tax_amount': False,
             'amount': False,
         }
         voucher_total = 0.0
-        voucher_line_ids = []
+
+        line_ids = resolve_o2m_operations(cr, uid, line_pool, line_ids, ["amount"], context)
 
         total = 0.0
         total_tax = 0.0
         for line in line_ids:
             line_amount = 0.0
-            line_amount = line[2] and line[2].get('amount',0.0) or 0.0
-            voucher_line_ids += [line[1]]
+            line_amount = line.get('amount',0.0)
             voucher_total += line_amount
 
         total = voucher_total
@@ -359,7 +411,7 @@ class account_voucher(osv.osv):
             })
         return {'value':default}
 
-    def onchange_journal_voucher(self, cr, uid, ids, line_ids=False, tax_id=False, price=0.0, partner_id=False, journal_id=False, ttype=False, context=None):
+    def onchange_journal_voucher(self, cr, uid, ids, line_ids=False, tax_id=False, price=0.0, partner_id=False, journal_id=False, ttype=False, company_id=False, context=None):
         """price
         Returns a dict that contains new values and context
 
@@ -398,13 +450,79 @@ class account_voucher(osv.osv):
         default['value']['account_id'] = account_id
         default['value']['type'] = ttype or tr_type
 
-        vals = self.onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context)
+        vals = self.onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, company_id, context)
         default['value'].update(vals.get('value'))
 
         return default
 
-    def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
-        """price
+    def onchange_rate(self, cr, uid, ids, rate, amount, currency_id, payment_rate_currency_id, company_id, context=None):
+        res =  {'value': {'paid_amount_in_company_currency': amount}}
+        company_currency = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id
+        if rate and amount and currency_id:# and currency_id == payment_rate_currency_id:
+            voucher_rate = self.pool.get('res.currency').browse(cr, uid, currency_id, context).rate
+            if company_currency.id == payment_rate_currency_id:
+                company_rate = rate
+            else:
+                company_rate = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.rate
+            res['value']['paid_amount_in_company_currency'] = amount / voucher_rate * company_rate
+        return res
+
+    def onchange_amount(self, cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=None):
+        if context is None:
+            context = {}
+        res = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=context)
+        ctx = context.copy()
+        ctx.update({'date': date})
+        vals = self.onchange_rate(cr, uid, ids, rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
+        for key in vals.keys():
+            res[key].update(vals[key])
+        return res
+
+    def recompute_payment_rate(self, cr, uid, ids, vals, currency_id, date, ttype, journal_id, amount, context=None):
+        if context is None:
+            context = {}
+        #on change of the journal, we need to set also the default value for payment_rate and payment_rate_currency_id
+        currency_obj = self.pool.get('res.currency')
+        journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
+        company_id = journal.company_id.id
+        payment_rate = 1.0
+        payment_rate_currency_id = currency_id
+        ctx = context.copy()
+        ctx.update({'date': date})
+        o2m_to_loop = False
+        if ttype == 'receipt':
+            o2m_to_loop = 'line_cr_ids'
+        elif ttype == 'payment':
+            o2m_to_loop = 'line_dr_ids'
+        if o2m_to_loop and 'value' in vals and o2m_to_loop in vals['value']:
+            for voucher_line in vals['value'][o2m_to_loop]:
+                if voucher_line['currency_id'] != currency_id:
+                    # we take as default value for the payment_rate_currency_id, the currency of the first invoice that
+                    # is not in the voucher currency
+                    payment_rate_currency_id = voucher_line['currency_id']
+                    tmp = currency_obj.browse(cr, uid, payment_rate_currency_id, context=ctx).rate
+                    voucher_currency_id = currency_id or journal.company_id.currency_id.id
+                    payment_rate = tmp / currency_obj.browse(cr, uid, voucher_currency_id, context=ctx).rate
+                    break
+        res = self.onchange_rate(cr, uid, ids, payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
+        for key in res.keys():
+            vals[key].update(res[key])
+        vals['value'].update({'payment_rate': payment_rate})
+        if payment_rate_currency_id:
+            vals['value'].update({'payment_rate_currency_id': payment_rate_currency_id})
+        return vals
+
+    def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=None):
+        if not journal_id:
+            return {}
+        res = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=context)
+        vals = self.recompute_payment_rate(cr, uid, ids, res, currency_id, date, ttype, journal_id, amount, context=context)
+        for key in vals.keys():
+            res[key].update(vals[key])
+        return res
+
+    def recompute_voucher_lines(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
+        """
         Returns a dict that contains new values and context
 
         @param partner_id: latest value from user input for field partner_id
@@ -415,40 +533,32 @@ class account_voucher(osv.osv):
         """
         if context is None:
             context = {}
-        if not journal_id:
-            return {}
         context_multi_currency = context.copy()
         if date:
             context_multi_currency.update({'date': date})
 
-        line_pool = self.pool.get('account.voucher.line')
-        line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) or False
-        if line_ids:
-            line_pool.unlink(cr, uid, line_ids)
-
         currency_pool = self.pool.get('res.currency')
         move_line_pool = self.pool.get('account.move.line')
         partner_pool = self.pool.get('res.partner')
         journal_pool = self.pool.get('account.journal')
+        line_pool = self.pool.get('account.voucher.line')
 
-        vals = self.onchange_journal(cr, uid, ids, journal_id, [], False, partner_id, context)
-        vals = vals.get('value')
-        currency_id = vals.get('currency_id', currency_id)
+        #set default values
         default = {
-            'value':{'line_ids':[], 'line_dr_ids':[], 'line_cr_ids':[], 'pre_line': False, 'currency_id':currency_id},
+            'value': {'line_ids': [] ,'line_dr_ids': [] ,'line_cr_ids': [] ,'pre_line': False,},
         }
 
-        if not partner_id:
-            return default
+        #drop existing lines
+        line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) or False
+        if line_ids:
+            line_pool.unlink(cr, uid, line_ids)
 
-        if not partner_id and ids:
-            line_ids = line_pool.search(cr, uid, [('voucher_id', '=', ids[0])])
-            if line_ids:
-                line_pool.unlink(cr, uid, line_ids)
+        if not partner_id or not journal_id:
             return default
 
         journal = journal_pool.browse(cr, uid, journal_id, context=context)
         partner = partner_pool.browse(cr, uid, partner_id, context=context)
+        currency_id = currency_id or journal.company_id.currency_id.id
         account_id = False
         if journal.type in ('sale','sale_refund'):
             account_id = partner.property_account_receivable.id
@@ -476,51 +586,83 @@ class account_voucher(osv.osv):
             ids = move_line_pool.search(cr, uid, [('state','=','valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context)
         else:
             ids = context['move_line_ids']
-        ids.reverse()
-        moves = move_line_pool.browse(cr, uid, ids, context=context)
-
+        invoice_id = context.get('invoice_id', False)
         company_currency = journal.company_id.currency_id.id
-        if company_currency != currency_id and ttype == 'payment':
-            total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit, context=context_multi_currency)
-        elif company_currency != currency_id and ttype == 'receipt':
-            total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit, context=context_multi_currency)
+        move_line_found = False
 
-        for line in moves:
+        #order the lines by most old first
+        ids.reverse()
+        account_move_lines = move_line_pool.browse(cr, uid, ids, context=context)
+
+        for line in account_move_lines:
             if line.credit and line.reconcile_partial_id and ttype == 'receipt':
                 continue
             if line.debit and line.reconcile_partial_id and ttype == 'payment':
                 continue
-            total_credit += line.credit or 0.0
-            total_debit += line.debit or 0.0
-        for line in moves:
+            if invoice_id:
+                if line.invoice.id == invoice_id:
+                    #if the invoice linked to the voucher line is equal to the invoice_id in context
+                    #then we assign the amount on that line, whatever the other voucher lines
+                    move_line_found = line.id
+                    break
+            elif currency_id == company_currency:
+                #otherwise treatments is the same but with other field names
+                if line.amount_residual == price:
+                    #if the amount residual is equal the amount voucher, we assign it to that voucher
+                    #line, whatever the other voucher lines
+                    move_line_found = line.id
+                    break
+                #otherwise we will split the voucher amount on each line (by most old first)
+                total_credit += line.credit or 0.0
+                total_debit += line.debit or 0.0
+            elif currency_id == line.currency_id.id:
+                if line.amount_residual_currency == price:
+                    move_line_found = line.id
+                    break
+                total_credit += line.credit and line.amount_currency or 0.0
+                total_debit += line.debit and line.amount_currency or 0.0
+
+        #voucher line creation
+        for line in account_move_lines:
             if line.credit and line.reconcile_partial_id and ttype == 'receipt':
                 continue
             if line.debit and line.reconcile_partial_id and ttype == 'payment':
                 continue
-            original_amount = line.credit or line.debit or 0.0
-            amount_unreconciled = currency_pool.compute(cr, uid, line.currency_id and line.currency_id.id or company_currency, currency_id, abs(line.amount_residual_currency), context=context_multi_currency)
+            if line.currency_id and currency_id==line.currency_id.id:
+                amount_original = abs(line.amount_currency)
+                amount_unreconciled = abs(line.amount_residual_currency)
+            else:
+                amount_original = currency_pool.compute(cr, uid, company_currency, currency_id, line.credit or line.debit or 0.0)
+                amount_unreconciled = currency_pool.compute(cr, uid, company_currency, currency_id, abs(line.amount_residual))
+            line_currency_id = line.currency_id and line.currency_id.id or company_currency
             rs = {
                 'name':line.move_id.name,
                 'type': line.credit and 'dr' or 'cr',
                 'move_line_id':line.id,
                 'account_id':line.account_id.id,
-                'amount_original': currency_pool.compute(cr, uid, line.currency_id and line.currency_id.id or company_currency, currency_id, line.currency_id and abs(line.amount_currency) or original_amount, context=context_multi_currency),
+                'amount_original': amount_original,
+                'amount': (move_line_found == line.id) and min(price, amount_unreconciled) or 0.0,
                 'date_original':line.date,
                 'date_due':line.date_maturity,
                 'amount_unreconciled': amount_unreconciled,
-
+                'currency_id': line_currency_id,
             }
 
-            if line.credit:
-                amount = min(amount_unreconciled, currency_pool.compute(cr, uid, company_currency, currency_id, abs(total_debit), context=context_multi_currency))
-                rs['amount'] = amount
-                total_debit -= amount
-            else:
-                amount = min(amount_unreconciled, currency_pool.compute(cr, uid, company_currency, currency_id, abs(total_credit), context=context_multi_currency))
-                rs['amount'] = amount
-                total_credit -= amount
+            #split voucher amount by most old first, but only for lines in the same currency
+            if not move_line_found:
+                if currency_id == line_currency_id:
+                    if line.credit:
+                        amount = min(amount_unreconciled, abs(total_debit))
+                        rs['amount'] = amount
+                        total_debit -= amount
+                    else:
+                        amount = min(amount_unreconciled, abs(total_credit))
+                        rs['amount'] = amount
+                        total_credit -= amount
+
+            if rs['amount_unreconciled'] == rs['amount']:
+                rs['reconcile'] = True
 
-            default['value']['line_ids'].append(rs)
             if rs['type'] == 'cr':
                 default['value']['line_cr_ids'].append(rs)
             else:
@@ -533,23 +675,51 @@ class account_voucher(osv.osv):
             default['value']['writeoff_amount'] = self._compute_writeoff_amount(cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price)
         return default
 
-    def onchange_date(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
+    def onchange_payment_rate_currency(self, cr, uid, ids, currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context=None):
+        if context is None:
+            context = {}
+        res = {'value': {}}
+        #set the default payment rate of the voucher and compute the paid amount in company currency
+        if currency_id and currency_id == payment_rate_currency_id:
+            ctx = context.copy()
+            ctx.update({'date': date})
+            vals = self.onchange_rate(cr, uid, ids, payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
+            for key in vals.keys():
+                res[key].update(vals[key])
+        return res
+
+    def onchange_date(self, cr, uid, ids, date, currency_id, payment_rate_currency_id, amount, company_id, context=None):
         """
         @param date: latest value from user input for field date
         @param args: other arguments
         @param context: context arguments, like lang, time zone
         @return: Returns a dict which contains new values, and context
         """
+        if context is None:
+            context ={}
+        res = {'value': {}}
+        #set the period of the voucher
         period_pool = self.pool.get('account.period')
-        res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=context)
-        pids = period_pool.search(cr, uid, [('date_start', '<=', date), ('date_stop', '>=', date)])
+        currency_obj = self.pool.get('res.currency')
+        ctx = context.copy()
+        ctx.update({'company_id': company_id})
+        pids = period_pool.find(cr, uid, date, context=ctx)
         if pids:
-            if not 'value' in res:
-                res['value'] = {}
             res['value'].update({'period_id':pids[0]})
+        if payment_rate_currency_id:
+            ctx.update({'date': date})
+            payment_rate = 1.0
+            if payment_rate_currency_id != currency_id:
+                tmp = currency_obj.browse(cr, uid, payment_rate_currency_id, context=ctx).rate
+                voucher_currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
+                payment_rate = tmp / currency_obj.browse(cr, uid, voucher_currency_id, context=ctx).rate
+            vals = self.onchange_payment_rate_currency(cr, uid, ids, currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context=context)
+            vals['value'].update({'payment_rate': payment_rate})
+            for key in vals.keys():
+                res[key].update(vals[key])
         return res
 
-    def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context=None):
+    def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id, context=None):
         if not journal_id:
             return False
         journal_pool = self.pool.get('account.journal')
@@ -560,11 +730,14 @@ class account_voucher(osv.osv):
             tax_id = account_id.tax_ids[0].id
 
         vals = self.onchange_price(cr, uid, ids, line_ids, tax_id, partner_id, context)
-        vals['value'].update({'tax_id':tax_id})
-        currency_id = journal.company_id.currency_id.id
+        vals['value'].update({'tax_id':tax_id,'amount': amount})
+        currency_id = False
         if journal.currency:
             currency_id = journal.currency.id
-        vals['value'].update({'currency_id':currency_id})
+        vals['value'].update({'currency_id': currency_id})
+        res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context)
+        for key in res.keys():
+            vals[key].update(res[key])
         return vals
 
     def proforma_voucher(self, cr, uid, ids, context=None):
@@ -608,7 +781,6 @@ class account_voucher(osv.osv):
                 raise osv.except_osv(_('Invalid action !'), _('Cannot delete Voucher(s) which are already opened or paid !'))
         return super(account_voucher, self).unlink(cr, uid, ids, context=context)
 
-    # TODO: may be we can remove this method if not used anyware
     def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype='sale'):
         res = {}
         if not partner_id:
@@ -628,173 +800,431 @@ class account_voucher(osv.osv):
             res['account_id'] = account_id
         return {'value':res}
 
-    def action_move_line_create(self, cr, uid, ids, context=None):
+    def _sel_context(self, cr, uid, voucher_id,context=None):
+        """
+        Select the context to use accordingly if it needs to be multicurrency or not.
 
-        def _get_payment_term_lines(term_id, amount):
-            term_pool = self.pool.get('account.payment.term')
-            if term_id and amount:
-                terms = term_pool.compute(cr, uid, term_id, amount)
-                return terms
-            return False
+        :param voucher_id: Id of the actual voucher
+        :return: The returned context will be the same as given in parameter if the voucher currency is the same
+                 than the company currency, otherwise it's a copy of the parameter with an extra key 'date' containing
+                 the date of the voucher.
+        :rtype: dict
+        """
+        company_currency = self._get_company_currency(cr, uid, voucher_id, context)
+        current_currency = self._get_current_currency(cr, uid, voucher_id, context)
+        if current_currency <> company_currency:
+            context_multi_currency = context.copy()
+            voucher_brw = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context)
+            context_multi_currency.update({'date': voucher_brw.date})
+            return context_multi_currency
+        return context
+
+    def first_move_line_get(self, cr, uid, voucher_id, move_id, company_currency, current_currency, context=None):
+        '''
+        Return a dict to be use to create the first account move line of given voucher.
+
+        :param voucher_id: Id of voucher what we are creating account_move.
+        :param move_id: Id of account move where this line will be added.
+        :param company_currency: id of currency of the company to which the voucher belong
+        :param current_currency: id of currency of the voucher
+        :return: mapping between fieldname and value of account move line to create
+        :rtype: dict
+        '''
+        move_line_obj = self.pool.get('account.move.line')
+        currency_obj = self.pool.get('res.currency')
+        voucher_brw = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        debit = credit = 0.0
+        # TODO: is there any other alternative then the voucher type ??
+        # ANSWER: We can have payment and receipt "In Advance".
+        # TODO: Make this logic available.
+        # -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
+        if voucher_brw.type in ('purchase', 'payment'):
+            credit = voucher_brw.paid_amount_in_company_currency
+        elif voucher_brw.type in ('sale', 'receipt'):
+            debit = voucher_brw.paid_amount_in_company_currency
+        if debit < 0: credit = -debit; debit = 0.0
+        if credit < 0: debit = -credit; credit = 0.0
+        sign = debit - credit < 0 and -1 or 1
+        #set the first line of the voucher
+        move_line = {
+                'name': voucher_brw.name or '/',
+                'debit': debit,
+                'credit': credit,
+                'account_id': voucher_brw.account_id.id,
+                'move_id': move_id,
+                'journal_id': voucher_brw.journal_id.id,
+                'period_id': voucher_brw.period_id.id,
+                'partner_id': voucher_brw.partner_id.id,
+                'currency_id': company_currency <> current_currency and  current_currency or False,
+                'amount_currency': company_currency <> current_currency and sign * voucher_brw.amount or 0.0,
+                'date': voucher_brw.date,
+                'date_maturity': voucher_brw.date_due
+            }
+        return move_line
+
+    def account_move_get(self, cr, uid, voucher_id, context=None):
+        '''
+        This method prepare the creation of the account move related to the given voucher.
+
+        :param voucher_id: Id of voucher for which we are creating account_move.
+        :return: mapping between fieldname and value of account move to create
+        :rtype: dict
+        '''
+        move_obj = self.pool.get('account.move')
+        seq_obj = self.pool.get('ir.sequence')
+        voucher_brw = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        if voucher_brw.number:
+            name = voucher_brw.number
+        elif voucher_brw.journal_id.sequence_id:
+            name = seq_obj.next_by_id(cr, uid, voucher_brw.journal_id.sequence_id.id)
+        else:
+            raise osv.except_osv(_('Error !'),
+                        _('Please define a sequence on the journal !'))
+        if not voucher_brw.reference:
+            ref = name.replace('/','')
+        else:
+            ref = voucher_brw.reference
+
+        move = {
+            'name': name,
+            'journal_id': voucher_brw.journal_id.id,
+            'narration': voucher_brw.narration,
+            'date': voucher_brw.date,
+            'ref': ref,
+            'period_id': voucher_brw.period_id and voucher_brw.period_id.id or False
+        }
+        return move
+
+    def _get_exchange_lines(self, cr, uid, line, move_id, amount_residual, company_currency, current_currency, context=None):
+        '''
+        Prepare the two lines in company currency due to currency rate difference.
+
+        :param line: browse record of the voucher.line for which we want to create currency rate difference accounting
+            entries
+        :param move_id: Account move wher the move lines will be.
+        :param amount_residual: Amount to be posted.
+        :param company_currency: id of currency of the company to which the voucher belong
+        :param current_currency: id of currency of the voucher
+        :return: the account move line and its counterpart to create, depicted as mapping between fieldname and value
+        :rtype: tuple of dict
+        '''
+        if amount_residual > 0:
+            account_id = line.voucher_id.company_id.expense_currency_exchange_account_id
+            if not account_id:
+                raise osv.except_osv(_('Warning'),_("Unable to create accounting entry for currency rate difference. You have to configure the field 'Income Currency Rate' on the company! "))
+        else:
+            account_id = line.voucher_id.company_id.income_currency_exchange_account_id
+            if not account_id:
+                raise osv.except_osv(_('Warning'),_("Unable to create accounting entry for currency rate difference. You have to configure the field 'Expense Currency Rate' on the company! "))
+        # Even if the amount_currency is never filled, we need to pass the foreign currency because otherwise
+        # the receivable/payable account may have a secondary currency, which render this field mandatory
+        account_currency_id = company_currency <> current_currency and current_currency or False
+        move_line = {
+            'journal_id': line.voucher_id.journal_id.id,
+            'period_id': line.voucher_id.period_id.id,
+            'name': _('change')+': '+(line.name or '/'),
+            'account_id': line.account_id.id,
+            'move_id': move_id,
+            'partner_id': line.voucher_id.partner_id.id,
+            'currency_id': account_currency_id,
+            'amount_currency': 0.0,
+            'quantity': 1,
+            'credit': amount_residual > 0 and amount_residual or 0.0,
+            'debit': amount_residual < 0 and -amount_residual or 0.0,
+            'date': line.voucher_id.date,
+        }
+        move_line_counterpart = {
+            'journal_id': line.voucher_id.journal_id.id,
+            'period_id': line.voucher_id.period_id.id,
+            'name': _('change')+': '+(line.name or '/'),
+            'account_id': account_id.id,
+            'move_id': move_id,
+            'amount_currency': 0.0,
+            'partner_id': line.voucher_id.partner_id.id,
+            'currency_id': account_currency_id,
+            'quantity': 1,
+            'debit': amount_residual > 0 and amount_residual or 0.0,
+            'credit': amount_residual < 0 and -amount_residual or 0.0,
+            'date': line.voucher_id.date,
+        }
+        return (move_line, move_line_counterpart)
+
+    def _convert_amount(self, cr, uid, amount, voucher_id, context=None):
+        '''
+        This function convert the amount given in company currency. It takes either the rate in the voucher (if the
+        payment_rate_currency_id is relevant) either the rate encoded in the system.
+
+        :param amount: float. The amount to convert
+        :param voucher: id of the voucher on which we want the conversion
+        :param context: to context to use for the conversion. It may contain the key 'date' set to the voucher date
+            field in order to select the good rate to use.
+        :return: the amount in the currency of the voucher's company
+        :rtype: float
+        '''
+        currency_obj = self.pool.get('res.currency')
+        voucher = self.browse(cr, uid, voucher_id, context=context)
+        res = amount
+        if voucher.payment_rate_currency_id.id == voucher.company_id.currency_id.id:
+            # the rate specified on the voucher is for the company currency
+            rate_between_voucher_and_base = voucher.currency_id.rate or 1.0
+            rate_between_base_and_company = voucher.payment_rate or 1.0
+            res = currency_obj.round(cr, uid, voucher.company_id.currency_id, (amount / rate_between_voucher_and_base * rate_between_base_and_company))
+        else:
+            # the rate specified on the voucher is not relevant, we use all the rates in the system
+            res = currency_obj.compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, amount, context=context)
+        return res
+
+    def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, company_currency, current_currency, context=None):
+        '''
+        Create one account move line, on the given account move, per voucher line where amount is not 0.0.
+        It returns Tuple with tot_line what is total of difference between debit and credit and
+        a list of lists with ids to be reconciled with this format (total_deb_cred,list_of_lists).
+
+        :param voucher_id: Voucher id what we are working with
+        :param line_total: Amount of the first line, which correspond to the amount we should totally split among all voucher lines.
+        :param move_id: Account move wher those lines will be joined.
+        :param company_currency: id of currency of the company to which the voucher belong
+        :param current_currency: id of currency of the voucher
+        :return: Tuple build as (remaining amount not allocated on voucher lines, list of account_move_line created in this method)
+        :rtype: tuple(float, list of int)
+        '''
         if context is None:
             context = {}
-        move_pool = self.pool.get('account.move')
-        move_line_pool = self.pool.get('account.move.line')
-        currency_pool = self.pool.get('res.currency')
-        tax_obj = self.pool.get('account.tax')
-        seq_obj = self.pool.get('ir.sequence')
-        for inv in self.browse(cr, uid, ids, context=context):
-            if inv.move_id:
+        move_line_obj = self.pool.get('account.move.line')
+        currency_obj = self.pool.get('res.currency')
+        tot_line = line_total
+        rec_lst_ids = []
+
+        voucher_brw = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context)
+        ctx = context.copy()
+        ctx.update({'date': voucher_brw.date})
+        for line in voucher_brw.line_ids:
+            #create one move line per voucher line where amount is not 0.0
+            if not line.amount:
                 continue
-            context_multi_currency = context.copy()
-            context_multi_currency.update({'date': inv.date})
-
-            if inv.number:
-                name = inv.number
-            elif inv.journal_id.sequence_id:
-                name = seq_obj.get_id(cr, uid, inv.journal_id.sequence_id.id)
-            else:
-                raise osv.except_osv(_('Error !'), _('Please define a sequence on the journal !'))
-            if not inv.reference:
-                ref = name.replace('/','')
+            # convert the amount set on the voucher line into the currency of the voucher's company
+            amount = self._convert_amount(cr, uid, line.untax_amount or line.amount, voucher_brw.id, context=ctx)
+            # if the amount encoded in voucher is equal to the amount unreconciled, we need to compute the
+            # currency rate difference
+            if line.amount == line.amount_unreconciled:
+                currency_rate_difference = line.move_line_id.amount_residual - amount
             else:
-                ref = inv.reference
-
-            move = {
-                'name': name,
-                'journal_id': inv.journal_id.id,
-                'narration': inv.narration,
-                'date': inv.date,
-                'ref': ref,
-                'period_id': inv.period_id and inv.period_id.id or False
-            }
-            move_id = move_pool.create(cr, uid, move)
-
-            #create the first line manually
-            company_currency = inv.journal_id.company_id.currency_id.id
-            current_currency = inv.currency_id.id
-            debit = 0.0
-            credit = 0.0
-            # TODO: is there any other alternative then the voucher type ??
-            # -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
-            if inv.type in ('purchase', 'payment'):
-                credit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount, context=context_multi_currency)
-            elif inv.type in ('sale', 'receipt'):
-                debit = currency_pool.compute(cr, uid, current_currency, company_currency, inv.amount, context=context_multi_currency)
-            if debit < 0:
-                credit = -debit
-                debit = 0.0
-            if credit < 0:
-                debit = -credit
-                credit = 0.0
-            sign = debit - credit < 0 and -1 or 1
-            #create the first line of the voucher
+                currency_rate_difference = 0.0
             move_line = {
-                'name': inv.name or '/',
-                'debit': debit,
-                'credit': credit,
-                'account_id': inv.account_id.id,
+                'journal_id': voucher_brw.journal_id.id,
+                'period_id': voucher_brw.period_id.id,
+                'name': line.name or '/',
+                'account_id': line.account_id.id,
                 'move_id': move_id,
-                'journal_id': inv.journal_id.id,
-                'period_id': inv.period_id.id,
-                'partner_id': inv.partner_id.id,
-                'currency_id': company_currency <> current_currency and  current_currency or False,
-                'amount_currency': company_currency <> current_currency and sign * inv.amount or 0.0,
-                'date': inv.date,
-                'date_maturity': inv.date_due
+                'partner_id': voucher_brw.partner_id.id,
+                'currency_id': line.move_line_id and (company_currency <> line.move_line_id.currency_id.id and line.move_line_id.currency_id.id) or False,
+                'analytic_account_id': line.account_analytic_id and line.account_analytic_id.id or False,
+                'quantity': 1,
+                'credit': 0.0,
+                'debit': 0.0,
+                'date': voucher_brw.date
             }
-            move_line_pool.create(cr, uid, move_line)
-            rec_list_ids = []
-            line_total = debit - credit
-            if inv.type == 'sale':
-                line_total = line_total - currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount, context=context_multi_currency)
-            elif inv.type == 'purchase':
-                line_total = line_total + currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount, context=context_multi_currency)
-
-            for line in inv.line_ids:
-                #create one move line per voucher line where amount is not 0.0
-                if not line.amount:
-                    continue
-                #we check if the voucher line is fully paid or not and create a move line to balance the payment and initial invoice if needed
-                if line.amount == line.amount_unreconciled:
-                    amount = line.move_line_id.amount_residual #residual amount in company currency
+            if amount < 0:
+                amount = -amount
+                if line.type == 'dr':
+                    line.type = 'cr'
                 else:
-                    amount = currency_pool.compute(cr, uid, current_currency, company_currency, line.untax_amount or line.amount, context=context_multi_currency)
-                move_line = {
-                    'journal_id': inv.journal_id.id,
-                    'period_id': inv.period_id.id,
-                    'name': line.name or '/',
+                    line.type = 'dr'
+
+            if (line.type=='dr'):
+                tot_line += amount
+                move_line['debit'] = amount
+            else:
+                tot_line -= amount
+                move_line['credit'] = amount
+
+            if voucher_brw.tax_id and voucher_brw.type in ('sale', 'purchase'):
+                move_line.update({
+                    'account_tax_id': voucher_brw.tax_id.id,
+                })
+
+            if move_line.get('account_tax_id', False):
+                tax_data = tax_obj.browse(cr, uid, [move_line['account_tax_id']], context=context)[0]
+                if not (tax_data.base_code_id and tax_data.tax_code_id):
+                    raise osv.except_osv(_('No Account Base Code and Account Tax Code!'),_("You have to configure account base code and account tax code on the '%s' tax!") % (tax_data.name))
+
+            # compute the amount in foreign currency
+            foreign_currency_diff = 0.0
+            amount_currency = False
+            if line.move_line_id:
+                voucher_currency = voucher_brw.currency_id and voucher_brw.currency_id.id or voucher_brw.journal_id.company_id.currency_id.id
+                # We want to set it on the account move line as soon as the original line had a foreign currency
+                if line.move_line_id.currency_id and line.move_line_id.currency_id.id != company_currency:
+                    # we compute the amount in that foreign currency. 
+                    if line.move_line_id.currency_id.id == current_currency:
+                        # if the voucher and the voucher line share the same currency, there is no computation to do
+                        sign = (move_line['debit'] - move_line['credit']) < 0 and -1 or 1
+                        amount_currency = sign * (line.amount)
+                    elif line.move_line_id.currency_id.id == voucher_brw.payment_rate_currency_id.id:
+                        # if the rate is specified on the voucher, we must use it
+                        voucher_rate = currency_obj.browse(cr, uid, voucher_currency, context=ctx).rate
+                        amount_currency = (move_line['debit'] - move_line['credit']) * voucher_brw.payment_rate * voucher_rate
+                    else:
+                        # otherwise we use the rates of the system (giving the voucher date in the context)
+                        amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx)
+                if line.amount == line.amount_unreconciled and line.move_line_id.currency_id.id == voucher_currency:
+                    foreign_currency_diff = line.move_line_id.amount_residual_currency + amount_currency
+
+            move_line['amount_currency'] = amount_currency
+            voucher_line = move_line_obj.create(cr, uid, move_line)
+            rec_ids = [voucher_line, line.move_line_id.id]
+
+            if not currency_obj.is_zero(cr, uid, voucher_brw.company_id.currency_id, currency_rate_difference):
+                # Change difference entry in company currency
+                exch_lines = self._get_exchange_lines(cr, uid, line, move_id, currency_rate_difference, company_currency, current_currency, context=context)
+                new_id = move_line_obj.create(cr, uid, exch_lines[0],context)
+                move_line_obj.create(cr, uid, exch_lines[1], context)
+                rec_ids.append(new_id)
+
+            if line.move_line_id and line.move_line_id.currency_id and not currency_obj.is_zero(cr, uid, line.move_line_id.currency_id, foreign_currency_diff):
+                # Change difference entry in voucher currency
+                move_line_foreign_currency = {
+                    'journal_id': line.voucher_id.journal_id.id,
+                    'period_id': line.voucher_id.period_id.id,
+                    'name': _('change')+': '+(line.name or '/'),
                     'account_id': line.account_id.id,
                     'move_id': move_id,
-                    'partner_id': inv.partner_id.id,
-                    'currency_id': company_currency <> current_currency and current_currency or False,
-                    'analytic_account_id': line.account_analytic_id and line.account_analytic_id.id or False,
+                    'partner_id': line.voucher_id.partner_id.id,
+                    'currency_id': line.move_line_id.currency_id.id,
+                    'amount_currency': -1 * foreign_currency_diff,
                     'quantity': 1,
                     'credit': 0.0,
                     'debit': 0.0,
-                    'date': inv.date
-                }
-                if amount < 0:
-                    amount = -amount
-                    if line.type == 'dr':
-                        line.type = 'cr'
-                    else:
-                        line.type = 'dr'
-                if (line.type=='dr'):
-                    line_total += amount
-                    move_line['debit'] = amount
-                else:
-                    line_total -= amount
-                    move_line['credit'] = amount
-
-                if inv.tax_id and inv.type in ('sale', 'purchase'):
-                    move_line.update({
-                        'account_tax_id': inv.tax_id.id,
-                    })
-                if move_line.get('account_tax_id', False):
-                    tax_data = tax_obj.browse(cr, uid, [move_line['account_tax_id']], context=context)[0]
-                    if not (tax_data.base_code_id and tax_data.tax_code_id):
-                        raise osv.except_osv(_('No Account Base Code and Account Tax Code!'),_("You have to configure account base code and account tax code on the '%s' tax!") % (tax_data.name))
-                sign = (move_line['debit'] - move_line['credit']) < 0 and -1 or 1
-                move_line['amount_currency'] = company_currency <> current_currency and sign * line.amount or 0.0
-                voucher_line = move_line_pool.create(cr, uid, move_line)
-                if line.move_line_id.id:
-                    rec_ids = [voucher_line, line.move_line_id.id]
-                    rec_list_ids.append(rec_ids)
-
-            if not currency_pool.is_zero(cr, uid, inv.currency_id, line_total):
-                diff = line_total
-                account_id = False
-                write_off_name = ''
-                if inv.payment_option == 'with_writeoff':
-                    account_id = inv.writeoff_acc_id.id
-                    write_off_name = inv.comment
-                elif inv.type in ('sale', 'receipt'):
-                    account_id = inv.partner_id.property_account_receivable.id
-                else:
-                    account_id = inv.partner_id.property_account_payable.id
-                move_line = {
-                    'name': write_off_name or name,
-                    'account_id': account_id,
-                    'move_id': move_id,
-                    'partner_id': inv.partner_id.id,
-                    'date': inv.date,
-                    'credit': diff > 0 and diff or 0.0,
-                    'debit': diff < 0 and -diff or 0.0,
-                    #'amount_currency': company_currency <> current_currency and currency_pool.compute(cr, uid, company_currency, current_currency, diff * -1, context=context_multi_currency) or 0.0,
-                    #'currency_id': company_currency <> current_currency and current_currency or False,
+                    'date': line.voucher_id.date,
                 }
-                move_line_pool.create(cr, uid, move_line)
-            self.write(cr, uid, [inv.id], {
+                new_id = move_line_obj.create(cr, uid, move_line_foreign_currency, context=context)
+                rec_ids.append(new_id)
+
+            if line.move_line_id.id:
+                rec_lst_ids.append(rec_ids)
+
+        return (tot_line, rec_lst_ids)
+
+    def writeoff_move_line_get(self, cr, uid, voucher_id, line_total, move_id, name, company_currency, current_currency, context=None):
+        '''
+        Set a dict to be use to create the writeoff move line.
+
+        :param voucher_id: Id of voucher what we are creating account_move.
+        :param line_total: Amount remaining to be allocated on lines.
+        :param move_id: Id of account move where this line will be added.
+        :param name: Description of account move line.
+        :param company_currency: id of currency of the company to which the voucher belong
+        :param current_currency: id of currency of the voucher
+        :return: mapping between fieldname and value of account move line to create
+        :rtype: dict
+        '''
+        move_line_obj = self.pool.get('account.move.line')
+        currency_obj = self.pool.get('res.currency')
+        move_line = {}
+
+        voucher_brw = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        current_currency_obj = voucher_brw.currency_id or voucher_brw.journal_id.company_id.currency_id
+
+        if not currency_obj.is_zero(cr, uid, current_currency_obj, line_total):
+            diff = line_total
+            account_id = False
+            write_off_name = ''
+            if voucher_brw.payment_option == 'with_writeoff':
+                account_id = voucher_brw.writeoff_acc_id.id
+                write_off_name = voucher_brw.comment
+            elif voucher_brw.type in ('sale', 'receipt'):
+                account_id = voucher_brw.partner_id.property_account_receivable.id
+            else:
+                account_id = voucher_brw.partner_id.property_account_payable.id
+            move_line = {
+                'name': write_off_name or name,
+                'account_id': account_id,
+                'move_id': move_id,
+                'partner_id': voucher_brw.partner_id.id,
+                'date': voucher_brw.date,
+                'credit': diff > 0 and diff or 0.0,
+                'debit': diff < 0 and -diff or 0.0,
+                'amount_currency': company_currency <> current_currency and voucher_brw.writeoff_amount or False,
+                'currency_id': company_currency <> current_currency and current_currency or False,
+                'analytic_account_id': voucher_brw.analytic_id and voucher_brw.analytic_id.id or False,
+            }
+
+        return move_line
+
+    def _get_company_currency(self, cr, uid, voucher_id, context=None):
+        '''
+        Get the currency of the actual company.
+
+        :param voucher_id: Id of the voucher what i want to obtain company currency.
+        :return: currency id of the company of the voucher
+        :rtype: int
+        '''
+        return self.pool.get('account.voucher').browse(cr,uid,voucher_id,context).journal_id.company_id.currency_id.id
+
+    def _get_current_currency(self, cr, uid, voucher_id, context=None):
+        '''
+        Get the currency of the voucher.
+
+        :param voucher_id: Id of the voucher what i want to obtain current currency.
+        :return: currency id of the voucher
+        :rtype: int
+        '''
+        voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        return voucher.currency_id.id or self._get_company_currency(cr,uid,voucher.id,context)
+
+    def action_move_line_create(self, cr, uid, ids, context=None):
+        '''
+        Confirm the vouchers given in ids and create the journal entries for each of them
+        '''
+        if context is None:
+            context = {}
+        move_pool = self.pool.get('account.move')
+        move_line_pool = self.pool.get('account.move.line')
+        currency_pool = self.pool.get('res.currency')
+        tax_obj = self.pool.get('account.tax')
+        seq_obj = self.pool.get('ir.sequence')
+        for voucher in self.browse(cr, uid, ids, context=context):
+            if voucher.move_id:
+                continue
+            company_currency = self._get_company_currency(cr, uid, voucher.id, context)
+            current_currency = self._get_current_currency(cr, uid, voucher.id, context)
+            # we select the context to use accordingly if it's a multicurrency case or not
+            context = self._sel_context(cr, uid, voucher.id, context)
+            # But for the operations made by _convert_amount, we always need to give the date in the context
+            ctx = context.copy()
+            ctx.update({'date': voucher.date})
+            # Create the account move record.
+            move_id = move_pool.create(cr, uid, self.account_move_get(cr, uid, voucher.id, context=context), context=context)
+            # Get the name of the account_move just created
+            name = move_pool.browse(cr, uid, move_id, context=context).name
+            # Create the first line of the voucher
+            move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, context), context)
+            move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context)
+            line_total = move_line_brw.debit - move_line_brw.credit
+            rec_list_ids = []
+            if voucher.type == 'sale':
+                line_total = line_total - self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx)
+            elif voucher.type == 'purchase':
+                line_total = line_total + self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx)
+            # Create one move line per voucher line where amount is not 0.0
+            line_total, rec_list_ids = self.voucher_move_line_create(cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context)
+
+            # Create the writeoff line if needed
+            ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, context)
+            if ml_writeoff:
+                ml_writeoff_id = move_line_pool.create(cr, uid, ml_writeoff, context)
+            # We post the voucher.
+            self.write(cr, uid, [voucher.id], {
                 'move_id': move_id,
                 'state': 'posted',
                 'number': name,
             })
-            if inv.journal_id.entry_posted:
+            if voucher.journal_id.entry_posted:
                 move_pool.post(cr, uid, [move_id], context={})
+            # We automatically reconcile the account move lines.
             for rec_ids in rec_list_ids:
                 if len(rec_ids) >= 2:
-                    move_line_pool.reconcile_partial(cr, uid, rec_ids)
+                    move_line_pool.reconcile_partial(cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id)
         return True
 
     def copy(self, cr, uid, id, default={}, context=None):
@@ -817,6 +1247,8 @@ class account_voucher_line(osv.osv):
     _description = 'Voucher Lines'
     _order = "move_line_id"
 
+    # If the payment is in the same currency than the invoice, we keep the same amount
+    # Otherwise, we compute from company currency to payment currency
     def _compute_balance(self, cr, uid, ids, name, args, context=None):
         currency_pool = self.pool.get('res.currency')
         rs_data = {}
@@ -825,45 +1257,73 @@ class account_voucher_line(osv.osv):
             ctx.update({'date': line.voucher_id.date})
             res = {}
             company_currency = line.voucher_id.journal_id.company_id.currency_id.id
-            voucher_currency = line.voucher_id.currency_id.id
+            voucher_currency = line.voucher_id.currency_id and line.voucher_id.currency_id.id or company_currency
             move_line = line.move_line_id or False
 
             if not move_line:
                 res['amount_original'] = 0.0
                 res['amount_unreconciled'] = 0.0
-
-            elif move_line.currency_id:
-                res['amount_original'] = currency_pool.compute(cr, uid, move_line.currency_id.id, voucher_currency, move_line.amount_currency, context=ctx)
+            elif move_line.currency_id and voucher_currency==move_line.currency_id.id:
+                res['amount_original'] = currency_pool.compute(cr, uid, move_line.currency_id.id, voucher_currency, abs(move_line.amount_currency), context=ctx)
+                res['amount_unreconciled'] = currency_pool.compute(cr, uid, move_line.currency_id and move_line.currency_id.id or company_currency, voucher_currency, abs(move_line.amount_residual_currency), context=ctx)
             elif move_line and move_line.credit > 0:
                 res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit, context=ctx)
+                res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, abs(move_line.amount_residual), context=ctx)
             else:
                 res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.debit, context=ctx)
+                res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, abs(move_line.amount_residual), context=ctx)
 
-            if move_line:
-                res['amount_unreconciled'] = currency_pool.compute(cr, uid, move_line.currency_id and move_line.currency_id.id or company_currency, voucher_currency, abs(move_line.amount_residual_currency), context=ctx)
             rs_data[line.id] = res
         return rs_data
 
+    def _currency_id(self, cr, uid, ids, name, args, context=None):
+        '''
+        This function returns the currency id of a voucher line. It's either the currency of the 
+        associated move line (if any) or the currency of the voucher or the company currency.
+        '''
+        res = {}
+        for line in self.browse(cr, uid, ids, context=context):
+            move_line = line.move_line_id
+            if move_line:
+                res[line.id] = move_line.currency_id and move_line.currency_id.id or move_line.company_id.currency_id.id
+            else:
+                res[line.id] = line.voucher_id.currency_id and line.voucher_id.currency_id.id or line.voucher_id.company_id.currency_id.id
+        return res
+
     _columns = {
         'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'),
         'name':fields.char('Description', size=256),
         'account_id':fields.many2one('account.account','Account', required=True),
         'partner_id':fields.related('voucher_id', 'partner_id', type='many2one', relation='res.partner', string='Partner'),
         'untax_amount':fields.float('Untax Amount'),
-        'amount':fields.float('Amount', digits_compute=dp.get_precision('Account')),
+        'amount':fields.float('Allocation', digits_compute=dp.get_precision('Account')),
+        'reconcile': fields.boolean('Full Reconcile'),
         'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Dr/Cr'),
         'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
         'move_line_id': fields.many2one('account.move.line', 'Journal Item'),
         'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Date', readonly=1),
         'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=1),
-        'amount_original': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Original Amount', store=True),
-        'amount_unreconciled': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Open Balance', store=True),
+        'amount_original': fields.function(_compute_balance, multi='dc', type='float', string='Original Amount', store=True),
+        'amount_unreconciled': fields.function(_compute_balance, multi='dc', type='float', string='Open Balance', store=True),
         'company_id': fields.related('voucher_id','company_id', relation='res.company', type='many2one', string='Company', store=True, readonly=True),
+        'currency_id': fields.function(_currency_id, string='Currency', type='many2one', relation='res.currency', readonly=True),
     }
     _defaults = {
-        'name': ''
+        'name': '',
     }
 
+    def onchange_reconcile(self, cr, uid, ids, reconcile, amount, amount_unreconciled, context=None):
+        vals = { 'amount': 0.0}
+        if reconcile:
+            vals = { 'amount': amount_unreconciled}
+        return {'value': vals}
+
+    def onchange_amount(self, cr, uid, ids, amount, amount_unreconciled, context=None):
+        vals = {}
+        if amount:
+            vals['reconcile'] = (amount == amount_unreconciled)
+        return {'value': vals}
+
     def onchange_move_line_id(self, cr, user, ids, move_line_id, context=None):
         """
         Returns a dict that contains new values and context
@@ -882,10 +1342,10 @@ class account_voucher_line(osv.osv):
                 ttype = 'dr'
             else:
                 ttype = 'cr'
-            account_id = move_line.account_id.id
             res.update({
-                'account_id':account_id,
-                'type': ttype
+                'account_id': move_line.account_id.id,
+                'type': ttype,
+                'currency_id': move_line.currency_id and move_line.currency_id.id or move_line.company_id.currency_id.id,
             })
         return {
             'value':res,
@@ -973,17 +1433,10 @@ class account_bank_statement_line(osv.osv):
     def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
         if not ids:
             return {}
-
         res = {}
-#        company_currency_id = False
         for line in self.browse(cursor, user, ids, context=context):
-#            if not company_currency_id:
-#                company_currency_id = line.company_id.id
             if line.voucher_id:
                 res[line.id] = line.voucher_id.amount#
-#                        res_currency_obj.compute(cursor, user,
-#                        company_currency_id, line.statement_id.currency.id,
-#                        line.voucher_id.amount, context=context)
             else:
                 res[line.id] = 0.0
         return res
@@ -992,7 +1445,7 @@ class account_bank_statement_line(osv.osv):
         for obj in self.browse(cr, uid, ids, context=context):
             if obj.voucher_id:
                 diff = abs(obj.amount) - obj.voucher_id.amount
-                if not self.pool.get('res.currency').is_zero(cr, uid, obj.voucher_id.currency_id, diff):
+                if not self.pool.get('res.currency').is_zero(cr, uid, obj.statement_id.currency, diff):
                     return False
         return True
 
@@ -1002,9 +1455,8 @@ class account_bank_statement_line(osv.osv):
 
     _columns = {
         'amount_reconciled': fields.function(_amount_reconciled,
-            string='Amount reconciled', method=True, type='float'),
+            string='Amount reconciled', type='float'),
         'voucher_id': fields.many2one('account.voucher', 'Payment'),
-
     }
 
     def unlink(self, cr, uid, ids, context=None):
@@ -1019,4 +1471,38 @@ class account_bank_statement_line(osv.osv):
 
 account_bank_statement_line()
 
+def resolve_o2m_operations(cr, uid, target_osv, operations, fields, context):
+    results = []
+    for operation in operations:
+        result = None
+        if not isinstance(operation, (list, tuple)):
+            result = target_osv.read(cr, uid, operation, fields, context=context)
+        elif operation[0] == 0:
+            # may be necessary to check if all the fields are here and get the default values?
+            result = operation[2]
+        elif operation[0] == 1:
+            result = target_osv.read(cr, uid, operation[1], fields, context=context)
+            if not result: result = {}
+            result.update(operation[2])
+        elif operation[0] == 4:
+            result = target_osv.read(cr, uid, operation[1], fields, context=context)
+        if result != None:
+            results.append(result)
+    return results
+
+class res_company(osv.osv):
+    _inherit = "res.company"
+    _columns = {
+        'income_currency_exchange_account_id': fields.many2one(
+            'account.account',
+            string="Income Currency Rate",
+            domain="[('type', '=', 'other')]",),
+        'expense_currency_exchange_account_id': fields.many2one(
+            'account.account',
+            string="Expense Currency Rate",
+            domain="[('type', '=', 'other')]",),
+    }
+
+res_company()
+
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: