[MERGE] forward port of branch 8.0 up to 2e092ac
[odoo/odoo.git] / addons / account_voucher / account_voucher.py
index 24ce6d2..e56f4be 100644 (file)
@@ -26,48 +26,21 @@ from openerp.osv import fields, osv
 import openerp.addons.decimal_precision as dp
 from openerp.tools.translate import _
 from openerp.tools import float_compare
+from openerp.report import report_sxw
+import openerp
 
-class res_company(osv.osv):
-    _inherit = "res.company"
-    _columns = {
-        'income_currency_exchange_account_id': fields.many2one(
-            'account.account',
-            string="Gain Exchange Rate Account",
-            domain="[('type', '=', 'other')]",),
-        'expense_currency_exchange_account_id': fields.many2one(
-            'account.account',
-            string="Loss Exchange Rate Account",
-            domain="[('type', '=', 'other')]",),
-    }
+class res_currency(osv.osv):
+    _inherit = "res.currency"
 
-
-class account_config_settings(osv.osv_memory):
-    _inherit = 'account.config.settings'
-    _columns = {
-        'income_currency_exchange_account_id': fields.related(
-            'company_id', 'income_currency_exchange_account_id',
-            type='many2one',
-            relation='account.account',
-            string="Gain Exchange Rate Account", 
-            domain="[('type', '=', 'other')]"),
-        'expense_currency_exchange_account_id': fields.related(
-            'company_id', 'expense_currency_exchange_account_id',
-            type="many2one",
-            relation='account.account',
-            string="Loss Exchange Rate Account",
-            domain="[('type', '=', 'other')]"),
-    }
-    def onchange_company_id(self, cr, uid, ids, company_id, context=None):
-        res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
-        if company_id:
-            company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
-            res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False, 
-                                 'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
-        else: 
-            res['value'].update({'income_currency_exchange_account_id': False, 
-                                 'expense_currency_exchange_account_id': False})
+    def _get_current_rate(self, cr, uid, ids, raise_on_no_rate=True, context=None):
+        if context is None:
+            context = {}
+        res = super(res_currency, self)._get_current_rate(cr, uid, ids, raise_on_no_rate, context=context)
+        if context.get('voucher_special_currency') in ids and context.get('voucher_special_currency_rate'):
+            res[context.get('voucher_special_currency')] = context.get('voucher_special_currency_rate')
         return res
 
+
 class account_voucher(osv.osv):
     def _check_paid(self, cr, uid, ids, name, args, context=None):
         res = {}
@@ -96,8 +69,10 @@ class account_voucher(osv.osv):
         invoice_pool = self.pool.get('account.invoice')
         journal_pool = self.pool.get('account.journal')
         if context.get('invoice_id', False):
-            currency_id = invoice_pool.browse(cr, uid, context['invoice_id'], context=context).currency_id.id
-            journal_id = journal_pool.search(cr, uid, [('currency', '=', currency_id)], limit=1)
+            invoice = invoice_pool.browse(cr, uid, context['invoice_id'], context=context)
+            journal_id = journal_pool.search(cr, uid, [
+                ('currency', '=', invoice.currency_id.id), ('company_id', '=', invoice.company_id.id)
+            ], limit=1, context=context)
             return journal_id and journal_id[0] or False
         if context.get('journal_id', False):
             return context.get('journal_id')
@@ -150,10 +125,13 @@ class account_voucher(osv.osv):
         journal_pool = self.pool.get('account.journal')
         journal_id = context.get('journal_id', False)
         if journal_id:
+            if isinstance(journal_id, (list, tuple)):
+                # sometimes journal_id is a pair (id, display_name)
+                journal_id = journal_id[0]
             journal = journal_pool.browse(cr, uid, journal_id, context=context)
             if journal.currency:
                 return journal.currency.id
-        return False
+        return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
     def _get_partner(self, cr, uid, context=None):
         if context is None: context = {}
@@ -176,7 +154,7 @@ class account_voucher(osv.osv):
         if not ids:
             return []
         if context is None: context = {}
-        return [(r['id'], (str("%.2f" % r['amount']) or '')) for r in self.read(cr, uid, ids, ['amount'], context, load='_classic_write')]
+        return [(r['id'], (r['number'] or _('Voucher'))) for r in self.read(cr, uid, ids, ['number'], context, load='_classic_write')]
 
     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')
@@ -204,7 +182,7 @@ class account_voucher(osv.osv):
         if context.get('type', 'sale') in ('purchase', 'payment'):
             nodes = doc.xpath("//field[@name='partner_id']")
             for node in nodes:
-                node.set('context', "{'search_default_supplier': 1}")
+                node.set('context', "{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}")
                 if context.get('invoice_type','') in ('in_invoice', 'in_refund'):
                     node.set('string', _("Supplier"))
         res['arch'] = etree.tostring(doc)
@@ -214,29 +192,36 @@ class account_voucher(osv.osv):
         debit = credit = 0.0
         sign = type == 'payment' and -1 or 1
         for l in line_dr_ids:
-            debit += l['amount']
+            if isinstance(l, dict):
+                debit += l['amount']
         for l in line_cr_ids:
-            credit += l['amount']
+            if isinstance(l, dict):
+                credit += l['amount']
         return amount - sign * (credit - debit)
 
     def onchange_line_ids(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount, voucher_currency, type, context=None):
         context = context or {}
         if not line_dr_ids and not line_cr_ids:
-            return {'value':{'writeoff_amount': 0.0, 'is_multi_currency': False}}
-        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)
-
+            return {'value':{'writeoff_amount': 0.0}}
+        # resolve lists of commands into lists of dicts
+        line_dr_ids = self.resolve_2many_commands(cr, uid, 'line_dr_ids', line_dr_ids, ['amount'], context)
+        line_cr_ids = self.resolve_2many_commands(cr, uid, 'line_cr_ids', line_cr_ids, ['amount'], context)
         #compute the field is_multi_currency that is used to hide/display options linked to secondary currency on the voucher
         is_multi_currency = False
         #loop on the voucher lines to see if one of these has a secondary currency. If yes, we need to see the options
         for voucher_line in line_dr_ids+line_cr_ids:
-            line_currency = voucher_line.get('move_line_id', False) and self.pool.get('account.move.line').browse(cr, uid, voucher_line.get('move_line_id'), context=context).currency_id
-            if line_currency:
+            line_id = voucher_line.get('id') and self.pool.get('account.voucher.line').browse(cr, uid, voucher_line['id'], context=context).move_line_id.id or voucher_line.get('move_line_id')
+            if line_id and self.pool.get('account.move.line').browse(cr, uid, line_id, context=context).currency_id:
                 is_multi_currency = True
                 break
         return {'value': {'writeoff_amount': self._compute_writeoff_amount(cr, uid, line_dr_ids, line_cr_ids, amount, type), 'is_multi_currency': is_multi_currency}}
 
+    def _get_journal_currency(self, cr, uid, ids, name, args, context=None):
+        res = {}
+        for voucher in self.browse(cr, uid, ids, context=context):
+            res[voucher.id] = voucher.journal_id.currency and voucher.journal_id.currency.id or voucher.company_id.currency_id.id
+        return res
+
     def _get_writeoff_amount(self, cr, uid, ids, name, args, context=None):
         if not ids: return {}
         currency_obj = self.pool.get('res.currency')
@@ -253,20 +238,48 @@ class account_voucher(osv.osv):
         return res
 
     def _paid_amount_in_company_currency(self, cr, uid, ids, name, args, context=None):
-        if not ids: return {}
+        if context is None:
+            context = {}
+        res = {}
+        ctx = context.copy()
+        for v in self.browse(cr, uid, ids, context=context):
+            ctx.update({'date': v.date})
+            #make a new call to browse in order to have the right date in the context, to get the right currency rate
+            voucher = self.browse(cr, uid, v.id, context=ctx)
+            ctx.update({
+              'voucher_special_currency': voucher.payment_rate_currency_id and voucher.payment_rate_currency_id.id or False,
+              'voucher_special_currency_rate': voucher.currency_id.rate * voucher.payment_rate,})
+            res[voucher.id] =  self.pool.get('res.currency').compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, voucher.amount, context=ctx)
+        return res
+
+    def _get_currency_help_label(self, cr, uid, currency_id, payment_rate, payment_rate_currency_id, context=None):
+        """
+        This function builds a string to help the users to understand the behavior of the payment rate fields they can specify on the voucher. 
+        This string is only used to improve the usability in the voucher form view and has no other effect.
+
+        :param currency_id: the voucher currency
+        :type currency_id: integer
+        :param payment_rate: the value of the payment_rate field of the voucher
+        :type payment_rate: float
+        :param payment_rate_currency_id: the value of the payment_rate_currency_id field of the voucher
+        :type payment_rate_currency_id: integer
+        :return: translated string giving a tip on what's the effect of the current payment rate specified
+        :rtype: str
+        """
+        rml_parser = report_sxw.rml_parse(cr, uid, 'currency_help_label', context=context)
+        currency_pool = self.pool.get('res.currency')
+        currency_str = payment_rate_str = ''
+        if currency_id:
+            currency_str = rml_parser.formatLang(1, currency_obj=currency_pool.browse(cr, uid, currency_id, context=context))
+        if payment_rate_currency_id:
+            payment_rate_str  = rml_parser.formatLang(payment_rate, currency_obj=currency_pool.browse(cr, uid, payment_rate_currency_id, context=context))
+        currency_help_label = _('At the operation date, the exchange rate was\n%s = %s') % (currency_str, payment_rate_str)
+        return currency_help_label
+
+    def _fnct_currency_help_label(self, cr, uid, ids, name, args, context=None):
         res = {}
-        rate = 1.0
         for voucher in self.browse(cr, uid, ids, context=context):
-            if voucher.currency_id:
-                if voucher.company_id.currency_id.id == voucher.payment_rate_currency_id.id:
-                    rate =  1 / voucher.payment_rate
-                else:
-                    ctx = context.copy()
-                    ctx.update({'date': voucher.date})
-                    voucher_rate = self.browse(cr, uid, voucher.id, context=ctx).currency_id.rate
-                    company_currency_rate = voucher.company_id.currency_id.rate
-                    rate = voucher_rate * company_currency_rate
-            res[voucher.id] =  voucher.amount / rate
+            res[voucher.id] = self._get_currency_help_label(cr, uid, voucher.currency_id.id, voucher.payment_rate, voucher.payment_rate_currency_id.id, context=context)
         return res
 
     _name = 'account.voucher'
@@ -281,42 +294,44 @@ class account_voucher(osv.osv):
     }
 
     _columns = {
-        'active': fields.boolean('Active', help="By default, reconciliation vouchers made on draft bank statements are set as inactive, which allow to hide the customer/supplier payment while the bank statement isn't confirmed."),
         'type':fields.selection([
             ('sale','Sale'),
             ('purchase','Purchase'),
             ('payment','Payment'),
             ('receipt','Receipt'),
         ],'Default Type', readonly=True, states={'draft':[('readonly',False)]}),
-        'name':fields.char('Memo', size=256, readonly=True, states={'draft':[('readonly',False)]}),
-        'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"),
+        'name':fields.char('Memo', readonly=True, states={'draft':[('readonly',False)]}),
+        'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]},
+                           help="Effective date for accounting entries", copy=False),
         'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
         'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'line_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}),
+        'line_ids':fields.one2many('account.voucher.line', 'voucher_id', 'Voucher Lines',
+                                   readonly=True, copy=True,
+                                   states={'draft':[('readonly',False)]}),
         'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits',
             domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}),
         'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits',
             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', readonly=True),
+        'currency_id': fields.function(_get_journal_currency, type='many2one', relation='res.currency', string='Currency', readonly=True, required=True),
         'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
         'state':fields.selection(
             [('draft','Draft'),
              ('cancel','Cancelled'),
              ('proforma','Pro-forma'),
              ('posted','Posted')
-            ], 'Status', readonly=True, size=32, track_visibility='onchange',
+            ], 'Status', readonly=True, track_visibility='onchange', copy=False,
             help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Voucher. \
                         \n* The \'Pro-forma\' when voucher is in Pro-forma status,voucher does not have an voucher number. \
                         \n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \
                         \n* The \'Cancelled\' status is used when user cancel voucher.'),
         'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
-        'reference': fields.char('Ref #', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."),
-        'number': fields.char('Number', size=32, readonly=True,),
-        'move_id':fields.many2one('account.move', 'Account Entry'),
+        'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True),
+        'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]},
+                                 help="Transaction reference number.", copy=False),
+        'number': fields.char('Number', readonly=True, copy=False),
+        'move_id':fields.many2one('account.move', 'Account Entry', copy=False),
         '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'),
@@ -329,11 +344,11 @@ class account_voucher(osv.osv):
         '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 Payment Balance'),
+                                           ('without_writeoff', 'Keep it open'),
+                                           ('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)]}),
+        'comment': fields.char('Counterpart Comment', 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, 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)]}),
@@ -341,9 +356,9 @@ class account_voucher(osv.osv):
             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'),
+        'currency_help_label': fields.function(_fnct_currency_help_label, type='text', string="Helping Sentence", help="This sentence helps you to know how to specify the payment rate by giving you the direct effect it has"), 
     }
     _defaults = {
-        'active': True,
         'period_id': _get_period,
         'partner_id': _get_partner,
         'journal_id':_get_journal,
@@ -416,14 +431,16 @@ class account_voucher(osv.osv):
         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')
+        if not line_ids:
+            line_ids = []
         res = {
             'tax_amount': False,
             'amount': False,
         }
         voucher_total = 0.0
 
-        line_ids = resolve_o2m_operations(cr, uid, line_pool, line_ids, ["amount"], context)
+        # resolve the list of commands into a list of dicts
+        line_ids = self.resolve_2many_commands(cr, uid, 'line_ids', line_ids, ['amount'], context)
 
         total_tax = 0.0
         for line in line_ids:
@@ -511,23 +528,26 @@ class account_voucher(osv.osv):
         return default
 
     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
+        res =  {'value': {'paid_amount_in_company_currency': amount, 'currency_help_label': self._get_currency_help_label(cr, uid, currency_id, rate, payment_rate_currency_id, context=context)}}
+        if rate and amount and currency_id:
+            company_currency = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id
+            #context should contain the date, the payment currency and the payment rate specified on the voucher
+            amount_in_company_currency = self.pool.get('res.currency').compute(cr, uid, currency_id, company_currency.id, amount, context=context)
+            res['value']['paid_amount_in_company_currency'] = amount_in_company_currency
         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})
+        #read the voucher rate with the right date in the context
+        currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
+        voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
+        ctx.update({
+            'voucher_special_currency': payment_rate_currency_id,
+            'voucher_special_currency_rate': rate * voucher_rate})
+        res = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=ctx)
         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])
@@ -541,6 +561,7 @@ class account_voucher(osv.osv):
         journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
         company_id = journal.company_id.id
         payment_rate = 1.0
+        currency_id = currency_id or journal.company_id.currency_id.id
         payment_rate_currency_id = currency_id
         ctx = context.copy()
         ctx.update({'date': date})
@@ -551,29 +572,69 @@ class account_voucher(osv.osv):
             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 not isinstance(voucher_line, dict):
+                    continue
                 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
+                    payment_rate = tmp / currency_obj.browse(cr, uid, currency_id, context=ctx).rate
                     break
+        vals['value'].update({
+            'payment_rate': payment_rate,
+            'currency_id': currency_id,
+            'payment_rate_currency_id': payment_rate_currency_id
+        })
+        #read the voucher rate with the right date in the context
+        voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
+        ctx.update({
+            'voucher_special_currency_rate': payment_rate * voucher_rate,
+            'voucher_special_currency': payment_rate_currency_id})
         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 basic_onchange_partner(self, cr, uid, ids, partner_id, journal_id, ttype, context=None):
+        partner_pool = self.pool.get('res.partner')
+        journal_pool = self.pool.get('account.journal')
+        res = {'value': {'account_id': False}}
+        if not partner_id or not journal_id:
+            return res
+
+        journal = journal_pool.browse(cr, uid, journal_id, context=context)
+        partner = partner_pool.browse(cr, uid, partner_id, context=context)
+        account_id = False
+        if journal.type in ('sale','sale_refund'):
+            account_id = partner.property_account_receivable.id
+        elif journal.type in ('purchase', 'purchase_refund','expense'):
+            account_id = partner.property_account_payable.id
+        else:
+            account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
+
+        res['value']['account_id'] = account_id
+        return res
+
     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)
+        if context is None:
+            context = {}
+        #TODO: comment me and use me directly in the sales/purchases views
+        res = self.basic_onchange_partner(cr, uid, ids, partner_id, journal_id, ttype, context=context)
+        if ttype in ['sale', 'purchase']:
+            return res
+        ctx = context.copy()
+        # not passing the payment_rate currency and the payment_rate in the context but it's ok because they are reset in recompute_payment_rate
+        ctx.update({'date': date})
+        vals = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=ctx)
+        vals2 = self.recompute_payment_rate(cr, uid, ids, vals, currency_id, date, ttype, journal_id, amount, context=context)
         for key in vals.keys():
             res[key].update(vals[key])
+        for key in vals2.keys():
+            res[key].update(vals2[key])
+        #TODO: can probably be removed now
         #TODO: onchange_partner_id() should not returns [pre_line, line_dr_ids, payment_rate...] for type sale, and not 
         # [pre_line, line_cr_ids, payment_rate...] for type purchase.
         # We should definitively split account.voucher object in two and make distinct on_change functions. In the 
@@ -616,8 +677,6 @@ class account_voucher(osv.osv):
         if context is None:
             context = {}
         context_multi_currency = context.copy()
-        if date:
-            context_multi_currency.update({'date': date})
 
         currency_pool = self.pool.get('res.currency')
         move_line_pool = self.pool.get('account.move.line')
@@ -627,13 +686,16 @@ class account_voucher(osv.osv):
 
         #set default values
         default = {
-            'value': {'line_dr_ids': [] ,'line_cr_ids': [] ,'pre_line': False,},
+            'value': {'line_dr_ids': [], 'line_cr_ids': [], 'pre_line': False},
         }
 
-        #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)
+        # drop existing lines
+        line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])])
+        for line in line_pool.browse(cr, uid, line_ids, context=context):
+            if line.type == 'cr':
+                default['value']['line_cr_ids'].append((2, line.id))
+            else:
+                default['value']['line_dr_ids'].append((2, line.id))
 
         if not partner_id or not journal_id:
             return default
@@ -641,28 +703,20 @@ class account_voucher(osv.osv):
         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
-        elif journal.type in ('purchase', 'purchase_refund','expense'):
-            account_id = partner.property_account_payable.id
-        else:
-            account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
-
-        default['value']['account_id'] = account_id
-
-        if journal.type not in ('cash', 'bank'):
-            return default
 
         total_credit = 0.0
         total_debit = 0.0
-        account_type = 'receivable'
+        account_type = None
+        if context.get('account_id'):
+            account_type = self.pool['account.account'].browse(cr, uid, context['account_id'], context=context).type
         if ttype == 'payment':
-            account_type = 'payable'
+            if not account_type:
+                account_type = 'payable'
             total_debit = price or 0.0
         else:
             total_credit = price or 0.0
-            account_type = 'receivable'
+            if not account_type:
+                account_type = 'receivable'
 
         if not context.get('move_line_ids', False):
             ids = move_line_pool.search(cr, uid, [('state','=','valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context)
@@ -670,7 +724,7 @@ class account_voucher(osv.osv):
             ids = context['move_line_ids']
         invoice_id = context.get('invoice_id', False)
         company_currency = journal.company_id.currency_id.id
-        move_line_found = False
+        move_lines_found = []
 
         #order the lines by most old first
         ids.reverse()
@@ -685,37 +739,38 @@ class account_voucher(osv.osv):
                 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
+                    move_lines_found.append(line.id)
             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
+                    move_lines_found.append(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
+                    move_lines_found.append(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
 
+        remaining_amount = price
         #voucher line creation
         for line in account_move_lines:
 
             if _remove_noise_in_o2m():
                 continue
 
-            if line.currency_id and currency_id==line.currency_id.id:
+            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))
+                #always use the amount booked in the company currency as the basis of the conversion into the voucher currency
+                amount_original = currency_pool.compute(cr, uid, company_currency, currency_id, line.credit or line.debit or 0.0, context=context_multi_currency)
+                amount_unreconciled = currency_pool.compute(cr, uid, company_currency, currency_id, abs(line.amount_residual), context=context_multi_currency)
             line_currency_id = line.currency_id and line.currency_id.id or company_currency
             rs = {
                 'name':line.move_id.name,
@@ -723,15 +778,16 @@ class account_voucher(osv.osv):
                 'move_line_id':line.id,
                 'account_id':line.account_id.id,
                 'amount_original': amount_original,
-                'amount': (move_line_found == line.id) and min(abs(price), amount_unreconciled) or 0.0,
+                'amount': (line.id in move_lines_found) and min(abs(remaining_amount), amount_unreconciled) or 0.0,
                 'date_original':line.date,
                 'date_due':line.date_maturity,
                 'amount_unreconciled': amount_unreconciled,
                 'currency_id': line_currency_id,
             }
+            remaining_amount -= rs['amount']
             #in case a corresponding move_line hasn't been found, we now try to assign the voucher amount
             #on existing invoices: we split voucher amount by most old first, but only for lines in the same currency
-            if not move_line_found:
+            if not move_lines_found:
                 if currency_id == line_currency_id:
                     if line.credit:
                         amount = min(amount_unreconciled, abs(total_debit))
@@ -750,9 +806,9 @@ class account_voucher(osv.osv):
             else:
                 default['value']['line_dr_ids'].append(rs)
 
-            if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0:
+            if len(default['value']['line_cr_ids']) > 0:
                 default['value']['pre_line'] = 1
-            elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0:
+            elif len(default['value']['line_dr_ids']) > 0:
                 default['value']['pre_line'] = 1
             default['value']['writeoff_amount'] = self._compute_writeoff_amount(cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price, ttype)
         return default
@@ -761,10 +817,15 @@ class account_voucher(osv.osv):
         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:
+        if currency_id:
+            #set the default payment rate of the voucher and compute the paid amount in company currency
             ctx = context.copy()
             ctx.update({'date': date})
+            #read the voucher rate with the right date in the context
+            voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
+            ctx.update({
+                'voucher_special_currency_rate': payment_rate * voucher_rate, 
+                'voucher_special_currency': payment_rate_currency_id})
             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])
@@ -784,7 +845,8 @@ class account_voucher(osv.osv):
         period_pool = self.pool.get('account.period')
         currency_obj = self.pool.get('res.currency')
         ctx = context.copy()
-        ctx.update({'company_id': company_id})
+        ctx.update({'company_id': company_id, 'account_period_prefer_normal': True})
+        voucher_currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
         pids = period_pool.find(cr, uid, date, context=ctx)
         if pids:
             res['value'].update({'period_id':pids[0]})
@@ -793,15 +855,16 @@ class account_voucher(osv.osv):
             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 = self.onchange_payment_rate_currency(cr, uid, ids, voucher_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, date, amount, ttype, company_id, context=None):
+        if context is None:
+            context = {}
         if not journal_id:
             return False
         journal_pool = self.pool.get('account.journal')
@@ -820,14 +883,39 @@ class account_voucher(osv.osv):
             currency_id = journal.currency.id
         else:
             currency_id = journal.company_id.currency_id.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])
+
+        period_ids = self.pool['account.period'].find(cr, uid, context=dict(context, company_id=company_id))
+        vals['value'].update({
+            'currency_id': currency_id,
+            'payment_rate_currency_id': currency_id,
+            'period_id': period_ids and period_ids[0] or False
+        })
+        #in case we want to register the payment directly from an invoice, it's confusing to allow to switch the journal 
+        #without seeing that the amount is expressed in the journal currency, and not in the invoice currency. So to avoid
+        #this common mistake, we simply reset the amount to 0 if the currency is not the invoice currency.
+        if context.get('payment_expected_currency') and currency_id != context.get('payment_expected_currency'):
+            vals['value']['amount'] = 0
+            amount = 0
+        if partner_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 onchange_company(self, cr, uid, ids, partner_id, journal_id, currency_id, company_id, context=None):
+        """
+        If the company changes, check that the journal is in the right company.
+        If not, fetch a new journal.
+        """
+        journal_pool = self.pool['account.journal']
+        journal = journal_pool.browse(cr, uid, journal_id, context=context)
+        if journal.company_id.id != company_id:
+            # can not guess type of journal, better remove it
+            return {'value': {'journal_id': False}}
+        return {}
+
     def button_proforma_voucher(self, cr, uid, ids, context=None):
-        self.signal_proforma_voucher(cr, uid, ids)
+        self.signal_workflow(cr, uid, ids, 'proforma_voucher')
         return {'type': 'ir.actions.act_window_close'}
 
     def proforma_voucher(self, cr, uid, ids, context=None):
@@ -842,17 +930,19 @@ class account_voucher(osv.osv):
     def cancel_voucher(self, cr, uid, ids, context=None):
         reconcile_pool = self.pool.get('account.move.reconcile')
         move_pool = self.pool.get('account.move')
-
+        move_line_pool = self.pool.get('account.move.line')
         for voucher in self.browse(cr, uid, ids, context=context):
-            recs = []
+            # refresh to make sure you don't unlink an already removed move
+            voucher.refresh()
             for line in voucher.move_ids:
+                # refresh to make sure you don't unreconcile an already unreconciled entry
+                line.refresh()
                 if line.reconcile_id:
-                    recs += [line.reconcile_id.id]
-                if line.reconcile_partial_id:
-                    recs += [line.reconcile_partial_id.id]
-
-            reconcile_pool.unlink(cr, uid, recs)
-
+                    move_lines = [move_line.id for move_line in line.reconcile_id.line_id]
+                    move_lines.remove(line.id)
+                    reconcile_pool.unlink(cr, uid, [line.reconcile_id.id])
+                    if len(move_lines) >= 2:
+                        move_line_pool.reconcile_partial(cr, uid, move_lines, 'auto',context=context)
             if voucher.move_id:
                 move_pool.button_cancel(cr, uid, [voucher.move_id.id])
                 move_pool.unlink(cr, uid, [voucher.move_id.id])
@@ -873,7 +963,7 @@ class account_voucher(osv.osv):
         res = {}
         if not partner_id:
             return res
-        res = {'account_id':False}
+        res = {}
         partner_pool = self.pool.get('res.partner')
         journal_pool = self.pool.get('account.journal')
         if pay_now == 'pay_later':
@@ -885,7 +975,8 @@ class account_voucher(osv.osv):
                 account_id = partner.property_account_payable.id
             else:
                 account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
-            res['account_id'] = account_id
+            if account_id:
+                res['account_id'] = account_id
         return {'value':res}
 
     def _sel_context(self, cr, uid, voucher_id, context=None):
@@ -902,8 +993,8 @@ class account_voucher(osv.osv):
         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})
+            voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context)
+            context_multi_currency.update({'date': voucher.date})
             return context_multi_currency
         return context
 
@@ -918,33 +1009,34 @@ class account_voucher(osv.osv):
         :return: mapping between fieldname and value of account move line to create
         :rtype: dict
         '''
-        voucher_brw = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        voucher = 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 voucher.type in ('purchase', 'payment'):
+            credit = voucher.paid_amount_in_company_currency
+        elif voucher.type in ('sale', 'receipt'):
+            debit = voucher.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 '/',
+                'name': voucher.name or '/',
                 'debit': debit,
                 'credit': credit,
-                'account_id': voucher_brw.account_id.id,
+                'account_id': voucher.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,
+                'journal_id': voucher.journal_id.id,
+                'period_id': voucher.period_id.id,
+                'partner_id': voucher.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
+                'amount_currency': (sign * abs(voucher.amount) # amount < 0 for refunds
+                    if company_currency != current_currency else 0.0),
+                'date': voucher.date,
+                'date_maturity': voucher.date_due
             }
         return move_line
 
@@ -957,31 +1049,31 @@ class account_voucher(osv.osv):
         :rtype: dict
         '''
         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:
-            if not voucher_brw.journal_id.sequence_id.active:
+        voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        if voucher.number:
+            name = voucher.number
+        elif voucher.journal_id.sequence_id:
+            if not voucher.journal_id.sequence_id.active:
                 raise osv.except_osv(_('Configuration Error !'),
                     _('Please activate the sequence of selected journal !'))
             c = dict(context)
-            c.update({'fiscalyear_id': voucher_brw.period_id.fiscalyear_id.id})
-            name = seq_obj.next_by_id(cr, uid, voucher_brw.journal_id.sequence_id.id, context=c)
+            c.update({'fiscalyear_id': voucher.period_id.fiscalyear_id.id})
+            name = seq_obj.next_by_id(cr, uid, voucher.journal_id.sequence_id.id, context=c)
         else:
             raise osv.except_osv(_('Error!'),
                         _('Please define a sequence on the journal.'))
-        if not voucher_brw.reference:
+        if not voucher.reference:
             ref = name.replace('/','')
         else:
-            ref = voucher_brw.reference
+            ref = voucher.reference
 
         move = {
             'name': name,
-            'journal_id': voucher_brw.journal_id.id,
-            'narration': voucher_brw.narration,
-            'date': voucher_brw.date,
+            'journal_id': voucher.journal_id.id,
+            'narration': voucher.narration,
+            'date': voucher.date,
             'ref': ref,
-            'period_id': voucher_brw.period_id.id,
+            'period_id': voucher.period_id.id,
         }
         return move
 
@@ -1001,14 +1093,21 @@ class account_voucher(osv.osv):
         if amount_residual > 0:
             account_id = line.voucher_id.company_id.expense_currency_exchange_account_id
             if not account_id:
-                raise osv.except_osv(_('Insufficient Configuration!'),_("You should configure the 'Loss Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
+                model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_form')
+                msg = _("You should configure the 'Loss Exchange Rate Account' to manage automatically the booking of accounting entries related to differences between exchange rates.")
+                raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel'))
         else:
             account_id = line.voucher_id.company_id.income_currency_exchange_account_id
             if not account_id:
-                raise osv.except_osv(_('Insufficient Configuration!'),_("You should configure the 'Gain Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
+                model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_form')
+                msg = _("You should configure the 'Gain Exchange Rate Account' to manage automatically the booking of accounting entries related to differences between exchange rates.")
+                raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel'))
         # 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
+        if line.account_id.currency_id:
+            account_currency_id = line.account_id.currency_id.id
+        else:
+            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,
@@ -1051,16 +1150,11 @@ class account_voucher(osv.osv):
         :return: the amount in the currency of the voucher's company
         :rtype: float
         '''
+        if context is None:
+            context = {}
         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
-            res = currency_obj.round(cr, uid, voucher.company_id.currency_id, (amount * voucher.payment_rate))
-        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
+        return currency_obj.compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, amount, context=context)
 
     def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, company_currency, current_currency, context=None):
         '''
@@ -1084,39 +1178,45 @@ class account_voucher(osv.osv):
         tot_line = line_total
         rec_lst_ids = []
 
-        voucher_brw = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context)
+        date = self.read(cr, uid, [voucher_id], ['date'], context=context)[0]['date']
         ctx = context.copy()
-        ctx.update({'date': voucher_brw.date})
+        ctx.update({'date': date})
+        voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context=ctx)
+        voucher_currency = voucher.journal_id.currency or voucher.company_id.currency_id
+        ctx.update({
+            'voucher_special_currency_rate': voucher_currency.rate * voucher.payment_rate ,
+            'voucher_special_currency': voucher.payment_rate_currency_id and voucher.payment_rate_currency_id.id or False,})
         prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
-        for line in voucher_brw.line_ids:
+        for line in voucher.line_ids:
             #create one move line per voucher line where amount is not 0.0
             # AND (second part of the clause) only if the original move line was not having debit = credit = 0 (which is a legal value)
-            if not line.amount and not (line.move_line_id and not float_compare(line.move_line_id.debit, line.move_line_id.credit, precision_rounding=prec) and not float_compare(line.move_line_id.debit, 0.0, precision_rounding=prec)):
+            if not line.amount and not (line.move_line_id and not float_compare(line.move_line_id.debit, line.move_line_id.credit, precision_digits=prec) and not float_compare(line.move_line_id.debit, 0.0, precision_digits=prec)):
                 continue
             # 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)
+            # this calls res_curreny.compute() with the right context, so that it will take either the rate on the voucher if it is relevant or will use the default behaviour
+            amount = self._convert_amount(cr, uid, line.untax_amount or line.amount, voucher.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:
                 if not line.move_line_id:
                     raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore."))
-                sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1
+                sign = line.type =='dr' and -1 or 1
                 currency_rate_difference = sign * (line.move_line_id.amount_residual - amount)
             else:
                 currency_rate_difference = 0.0
             move_line = {
-                'journal_id': voucher_brw.journal_id.id,
-                'period_id': voucher_brw.period_id.id,
+                'journal_id': voucher.journal_id.id,
+                'period_id': voucher.period_id.id,
                 'name': line.name or '/',
                 'account_id': line.account_id.id,
                 'move_id': move_id,
-                'partner_id': voucher_brw.partner_id.id,
+                'partner_id': voucher.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
+                'date': voucher.date
             }
             if amount < 0:
                 amount = -amount
@@ -1132,9 +1232,9 @@ class account_voucher(osv.osv):
                 tot_line -= amount
                 move_line['credit'] = amount
 
-            if voucher_brw.tax_id and voucher_brw.type in ('sale', 'purchase'):
+            if voucher.tax_id and voucher.type in ('sale', 'purchase'):
                 move_line.update({
-                    'account_tax_id': voucher_brw.tax_id.id,
+                    'account_tax_id': voucher.tax_id.id,
                 })
 
             if move_line.get('account_tax_id', False):
@@ -1146,7 +1246,6 @@ class account_voucher(osv.osv):
             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.
@@ -1154,27 +1253,18 @@ class account_voucher(osv.osv):
                         # 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
-                        payment_rate = voucher_brw.payment_rate
-                        if voucher_currency != company_currency:
-                            #if the voucher currency is not the company currency, we need to consider the rate of the line's currency
-                            voucher_rate = currency_obj.browse(cr, uid, voucher_currency, context=ctx).rate
-                            payment_rate = voucher_rate * payment_rate
-                        amount_currency = (move_line['debit'] - move_line['credit']) * payment_rate
-
                     else:
-                        # otherwise we use the rates of the system (giving the voucher date in the context)
+                        # if the rate is specified on the voucher, it will be used thanks to the special keys in the context
+                        # otherwise we use the rates of the system
                         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:
-                    sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1
-                    foreign_currency_diff = sign * line.move_line_id.amount_residual_currency + amount_currency
+                if line.amount == line.amount_unreconciled:
+                    foreign_currency_diff = line.move_line_id.amount_residual_currency - abs(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):
+            if not currency_obj.is_zero(cr, uid, voucher.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)
@@ -1199,10 +1289,8 @@ class account_voucher(osv.osv):
                 }
                 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):
@@ -1221,32 +1309,36 @@ class account_voucher(osv.osv):
         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
+        voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
+        current_currency_obj = voucher.currency_id or voucher.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
+            if voucher.payment_option == 'with_writeoff':
+                account_id = voucher.writeoff_acc_id.id
+                write_off_name = voucher.comment
+            elif voucher.partner_id:
+                if voucher.type in ('sale', 'receipt'):
+                    account_id = voucher.partner_id.property_account_receivable.id
+                else:
+                    account_id = voucher.partner_id.property_account_payable.id
             else:
-                account_id = voucher_brw.partner_id.property_account_payable.id
-            sign = voucher_brw.type == 'payment' and -1 or 1
+                # fallback on account of voucher
+                account_id = voucher.account_id.id
+            sign = voucher.type == 'payment' and -1 or 1
             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,
+                'partner_id': voucher.partner_id.id,
+                'date': voucher.date,
                 'credit': diff > 0 and diff or 0.0,
                 'debit': diff < 0 and -diff or 0.0,
-                'amount_currency': company_currency <> current_currency and (sign * -1 * voucher_brw.writeoff_amount) or False,
+                'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or 0.0,
                 '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,
+                'analytic_account_id': voucher.analytic_id and voucher.analytic_id.id or False,
             }
 
         return move_line
@@ -1281,6 +1373,7 @@ class account_voucher(osv.osv):
         move_pool = self.pool.get('account.move')
         move_line_pool = self.pool.get('account.move.line')
         for voucher in self.browse(cr, uid, ids, context=context):
+            local_context = dict(context, force_company=voucher.journal_id.company_id.id)
             if voucher.move_id:
                 continue
             company_currency = self._get_company_currency(cr, uid, voucher.id, context)
@@ -1295,7 +1388,7 @@ class account_voucher(osv.osv):
             # 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_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, local_context), local_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 = []
@@ -1307,9 +1400,9 @@ class account_voucher(osv.osv):
             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)
+            ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, local_context)
             if ml_writeoff:
-                move_line_pool.create(cr, uid, ml_writeoff, context)
+                move_line_pool.create(cr, uid, ml_writeoff, local_context)
             # We post the voucher.
             self.write(cr, uid, [voucher.id], {
                 'move_id': move_id,
@@ -1325,35 +1418,23 @@ class account_voucher(osv.osv):
                     reconcile = 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=None, context=None):
-        if default is None:
-            default = {}
-        default.update({
-            'state': 'draft',
-            'number': False,
-            'move_id': False,
-            'line_cr_ids': False,
-            'line_dr_ids': False,
-            'reference': False
-        })
-        if 'date' not in default:
-            default['date'] = time.strftime('%Y-%m-%d')
-        return super(account_voucher, self).copy(cr, uid, id, default, context)
-
-
 class account_voucher_line(osv.osv):
     _name = 'account.voucher.line'
     _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
+    # Otherwise, we compute from invoice currency to payment currency
     def _compute_balance(self, cr, uid, ids, name, args, context=None):
         currency_pool = self.pool.get('res.currency')
         rs_data = {}
         for line in self.browse(cr, uid, ids, context=context):
             ctx = context.copy()
             ctx.update({'date': line.voucher_id.date})
+            voucher_rate = self.pool.get('res.currency').read(cr, uid, line.voucher_id.currency_id.id, ['rate'], context=ctx)['rate']
+            ctx.update({
+                'voucher_special_currency': line.voucher_id.payment_rate_currency_id and line.voucher_id.payment_rate_currency_id.id or False,
+                'voucher_special_currency_rate': line.voucher_id.payment_rate * voucher_rate})
             res = {}
             company_currency = line.voucher_id.journal_id.company_id.currency_id.id
             voucher_currency = line.voucher_id.currency_id and line.voucher_id.currency_id.id or company_currency
@@ -1363,13 +1444,11 @@ class account_voucher_line(osv.osv):
                 res['amount_original'] = 0.0
                 res['amount_unreconciled'] = 0.0
             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)
+                res['amount_original'] = abs(move_line.amount_currency)
+                res['amount_unreconciled'] = abs(move_line.amount_residual_currency)
             else:
-                res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.debit, context=ctx)
+                #always use the amount booked in the company currency as the basis of the conversion into the voucher currency
+                res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit or move_line.debit or 0.0, context=ctx)
                 res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, abs(move_line.amount_residual), context=ctx)
 
             rs_data[line.id] = res
@@ -1391,7 +1470,7 @@ class account_voucher_line(osv.osv):
 
     _columns = {
         'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'),
-        'name':fields.char('Description', size=256),
+        'name':fields.char('Description',),
         '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'),
@@ -1399,7 +1478,7 @@ class account_voucher_line(osv.osv):
         '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'),
+        'move_line_id': fields.many2one('account.move.line', 'Journal Item', copy=False),
         '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, multi='dc', type='float', string='Original Amount', store=True, digits_compute=dp.get_precision('Account')),
@@ -1490,128 +1569,4 @@ class account_voucher_line(osv.osv):
         })
         return values
 
-class account_bank_statement(osv.osv):
-    _inherit = 'account.bank.statement'
-
-    def button_confirm_bank(self, cr, uid, ids, context=None):
-        voucher_obj = self.pool.get('account.voucher')
-        voucher_ids = []
-        for statement in self.browse(cr, uid, ids, context=context):
-            voucher_ids += [line.voucher_id.id for line in statement.line_ids if line.voucher_id]
-        if voucher_ids:
-            voucher_obj.write(cr, uid, voucher_ids, {'active': True}, context=context)
-        return super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, context=context)
-
-    def button_cancel(self, cr, uid, ids, context=None):
-        voucher_obj = self.pool.get('account.voucher')
-        for st in self.browse(cr, uid, ids, context=context):
-            voucher_ids = []
-            for line in st.line_ids:
-                if line.voucher_id:
-                    voucher_ids.append(line.voucher_id.id)
-            voucher_obj.cancel_voucher(cr, uid, voucher_ids, context)
-        return super(account_bank_statement, self).button_cancel(cr, uid, ids, context=context)
-
-    def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, next_number, context=None):
-        voucher_obj = self.pool.get('account.voucher')
-        move_line_obj = self.pool.get('account.move.line')
-        bank_st_line_obj = self.pool.get('account.bank.statement.line')
-        st_line = bank_st_line_obj.browse(cr, uid, st_line_id, context=context)
-        if st_line.voucher_id:
-            voucher_obj.write(cr, uid, [st_line.voucher_id.id], {'number': next_number}, context=context)
-            if st_line.voucher_id.state == 'cancel':
-                voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context)
-            voucher_obj.signal_proforma_voucher(cr, uid, [st_line.voucher_id.id])
-
-            v = voucher_obj.browse(cr, uid, st_line.voucher_id.id, context=context)
-            bank_st_line_obj.write(cr, uid, [st_line_id], {
-                'move_ids': [(4, v.move_id.id, False)]
-            })
-
-            return move_line_obj.write(cr, uid, [x.id for x in v.move_ids], {'statement_id': st_line.statement_id.id}, context=context)
-        return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context)
-
-    def write(self, cr, uid, ids, vals, context=None):
-        # Restrict to modify the journal if we already have some voucher of reconciliation created/generated.
-        # Because the voucher keeps in memory the journal it was created with.
-        for bk_st in self.browse(cr, uid, ids, context=context):
-            if vals.get('journal_id') and bk_st.line_ids:
-                if any([x.voucher_id and True or False for x in bk_st.line_ids]):
-                    raise osv.except_osv(_('Unable to change journal !'), _('You can not change the journal as you already reconciled some statement lines!'))
-        return super(account_bank_statement, self).write(cr, uid, ids, vals, context=context)
-
-
-class account_bank_statement_line(osv.osv):
-    _inherit = 'account.bank.statement.line'
-
-    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
-        res = super(account_bank_statement_line, self).onchange_partner_id(cr, uid, ids, partner_id, context=context)
-        if 'value' not in res:
-            res['value'] = {}
-        res['value'].update({'voucher_id' : False})
-        return res
-
-    def onchange_amount(self, cr, uid, ids, amount, context=None):
-        return {'value' :  {'voucher_id' : False}}
-
-    def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
-        if not ids:
-            return {}
-        res = {}
-        for line in self.browse(cursor, user, ids, context=context):
-            if line.voucher_id:
-                res[line.id] = line.voucher_id.amount#
-            else:
-                res[line.id] = 0.0
-        return res
-
-    def _check_amount(self, cr, uid, ids, context=None):
-        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.statement_id.currency, diff):
-                    return False
-        return True
-
-    _constraints = [
-        (_check_amount, 'The amount of the voucher must be the same amount as the one on the statement line.', ['amount']),
-    ]
-
-    _columns = {
-        'amount_reconciled': fields.function(_amount_reconciled,
-            string='Amount reconciled', type='float'),
-        'voucher_id': fields.many2one('account.voucher', 'Reconciliation'),
-    }
-
-    def unlink(self, cr, uid, ids, context=None):
-        voucher_obj = self.pool.get('account.voucher')
-        statement_line = self.browse(cr, uid, ids, context=context)
-        unlink_ids = []
-        for st_line in statement_line:
-            if st_line.voucher_id:
-                unlink_ids.append(st_line.voucher_id.id)
-        voucher_obj.unlink(cr, uid, unlink_ids, context=context)
-        return super(account_bank_statement_line, self).unlink(cr, uid, ids, context=context)
-
-
-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
-
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: