[MERGE] forward port of branch saas-3 up to e1e7dc0
[odoo/odoo.git] / addons / account / account_invoice.py
index 284dba8..e94a60a 100644 (file)
 #
 ##############################################################################
 
-import time
+import itertools
 from lxml import etree
-import openerp.addons.decimal_precision as dp
-import openerp.exceptions
 
-from openerp.osv import fields, osv, orm
+from openerp import models, fields, api, _
+from openerp.exceptions import except_orm, Warning, RedirectWarning
 from openerp.tools import float_compare
-from openerp.tools.translate import _
-from openerp import SUPERUSER_ID
-
-class account_invoice(osv.osv):
-    def _amount_all(self, cr, uid, ids, name, args, context=None):
-        res = {}
-        for invoice in self.browse(cr, uid, ids, context=context):
-            res[invoice.id] = {
-                'amount_untaxed': 0.0,
-                'amount_tax': 0.0,
-                'amount_total': 0.0
-            }
-            for line in invoice.invoice_line:
-                res[invoice.id]['amount_untaxed'] += line.price_subtotal
-            for line in invoice.tax_line:
-                res[invoice.id]['amount_tax'] += line.amount
-            res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed']
-        return res
-
-    def _get_journal(self, cr, uid, context=None):
-        if context is None:
-            context = {}
-        type_inv = context.get('type', 'out_invoice')
-        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
-        company_id = context.get('company_id', user.company_id.id)
-        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'}
-        journal_obj = self.pool.get('account.journal')
-        domain = [('company_id', '=', company_id)]
-        if isinstance(type_inv, list):
-            domain.append(('type', 'in', [type2journal.get(type) for type in type_inv if type2journal.get(type)]))
-        else:
-            domain.append(('type', '=', type2journal.get(type_inv, 'sale')))
-        res = journal_obj.search(cr, uid, domain, limit=1)
-        return res and res[0] or False
-
-    def _get_currency(self, cr, uid, context=None):
-        res = False
-        journal_id = self._get_journal(cr, uid, context=context)
-        if journal_id:
-            journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
-            res = journal.currency and journal.currency.id or journal.company_id.currency_id.id
-        return res
-
-    def _get_journal_analytic(self, cr, uid, type_inv, context=None):
-        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
-        tt = type2journal.get(type_inv, 'sale')
-        result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
-        if not result:
-            raise osv.except_osv(_('No Analytic Journal!'),_("You must define an analytic journal of type '%s'!") % (tt,))
-        return result[0]
-
-    def _get_type(self, cr, uid, context=None):
-        if context is None:
-            context = {}
-        return context.get('type', 'out_invoice')
-
-    def _reconciled(self, cr, uid, ids, name, args, context=None):
-        res = {}
-        for inv in self.browse(cr, uid, ids, context=context):
-            res[inv.id] = self.test_paid(cr, uid, [inv.id])
-            if not res[inv.id] and inv.state == 'paid':
-                self.signal_open_test(cr, uid, [inv.id])
-        return res
-
-    def _get_reference_type(self, cr, uid, context=None):
-        return [('none', _('Free Reference'))]
+import openerp.addons.decimal_precision as dp
 
-    def _amount_residual(self, cr, uid, ids, name, args, context=None):
-        """Function of the field residua. It computes the residual amount (balance) for each invoice"""
-        if context is None:
-            context = {}
-        ctx = context.copy()
-        result = {}
-        currency_obj = self.pool.get('res.currency')
-        for invoice in self.browse(cr, SUPERUSER_ID, ids, context=context):
-            nb_inv_in_partial_rec = max_invoice_id = 0
-            result[invoice.id] = 0.0
-            if invoice.move_id:
-                for aml in invoice.move_id.line_id:
-                    if aml.account_id.type in ('receivable','payable'):
-                        if aml.currency_id and aml.currency_id.id == invoice.currency_id.id:
-                            result[invoice.id] += aml.amount_residual_currency
-                        else:
-                            ctx['date'] = aml.date
-                            result[invoice.id] += currency_obj.compute(cr, uid, aml.company_id.currency_id.id, invoice.currency_id.id, aml.amount_residual, context=ctx)
-
-                        if aml.reconcile_partial_id.line_partial_ids:
-                            #we check if the invoice is partially reconciled and if there are other invoices
-                            #involved in this partial reconciliation (and we sum these invoices)
-                            for line in aml.reconcile_partial_id.line_partial_ids:
-                                if line.invoice and invoice.type == line.invoice.type:
-                                    nb_inv_in_partial_rec += 1
-                                    #store the max invoice id as for this invoice we will make a balance instead of a simple division
-                                    max_invoice_id = max(max_invoice_id, line.invoice.id)
-            if nb_inv_in_partial_rec:
-                #if there are several invoices in a partial reconciliation, we split the residual by the number
-                #of invoice to have a sum of residual amounts that matches the partner balance
-                new_value = currency_obj.round(cr, uid, invoice.currency_id, result[invoice.id] / nb_inv_in_partial_rec)
-                if invoice.id == max_invoice_id:
-                    #if it's the last the invoice of the bunch of invoices partially reconciled together, we make a
-                    #balance to avoid rounding errors
-                    result[invoice.id] = result[invoice.id] - ((nb_inv_in_partial_rec - 1) * new_value)
-                else:
-                    result[invoice.id] = new_value
+# mapping invoice type to journal type
+TYPE2JOURNAL = {
+    'out_invoice': 'sale',
+    'in_invoice': 'purchase',
+    'out_refund': 'sale_refund',
+    'in_refund': 'purchase_refund',
+}
 
-            #prevent the residual amount on the invoice to be less than 0
-            result[invoice.id] = max(result[invoice.id], 0.0)            
-        return result
+# mapping invoice type to refund type
+TYPE2REFUND = {
+    'out_invoice': 'out_refund',        # Customer Invoice
+    'in_invoice': 'in_refund',          # Supplier Invoice
+    'out_refund': 'out_invoice',        # Customer Refund
+    'in_refund': 'in_invoice',          # Supplier Refund
+}
 
-    # Give Journal Items related to the payment reconciled to this invoice
-    # Return ids of partial and total payments related to the selected invoices
-    def _get_lines(self, cr, uid, ids, name, arg, context=None):
-        res = {}
-        for invoice in self.browse(cr, uid, ids, context=context):
-            id = invoice.id
-            res[id] = []
-            if not invoice.move_id:
-                continue
-            data_lines = [x for x in invoice.move_id.line_id if x.account_id.id == invoice.account_id.id]
-            partial_ids = []
-            for line in data_lines:
-                ids_line = []
-                if line.reconcile_id:
-                    ids_line = line.reconcile_id.line_id
-                elif line.reconcile_partial_id:
-                    ids_line = line.reconcile_partial_id.line_partial_ids
-                l = map(lambda x: x.id, ids_line)
-                partial_ids.append(line.id)
-                res[id] =[x for x in l if x <> line.id and x not in partial_ids]
-        return res
+MAGIC_COLUMNS = ('id', 'create_uid', 'create_date', 'write_uid', 'write_date')
 
-    def _get_invoice_line(self, cr, uid, ids, context=None):
-        result = {}
-        for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
-            result[line.invoice_id.id] = True
-        return result.keys()
-
-    def _get_invoice_tax(self, cr, uid, ids, context=None):
-        result = {}
-        for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
-            result[tax.invoice_id.id] = True
-        return result.keys()
-
-    def _compute_lines(self, cr, uid, ids, name, args, context=None):
-        result = {}
-        for invoice in self.browse(cr, uid, ids, context=context):
-            src = []
-            lines = []
-            if invoice.move_id:
-                for m in invoice.move_id.line_id:
-                    if m.account_id != invoice.account_id:
-                        continue
-                    temp_lines = []
-                    if m.reconcile_id:
-                        temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
-                    elif m.reconcile_partial_id:
-                        temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
-                    lines += [x for x in temp_lines if x not in lines]
-                    src.append(m.id)
-
-            lines = filter(lambda x: x not in src, lines)
-            result[invoice.id] = lines
-        return result
-
-    def _get_invoice_from_line(self, cr, uid, ids, context=None):
-        move = {}
-        for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context):
-            if line.reconcile_partial_id:
-                for line2 in line.reconcile_partial_id.line_partial_ids:
-                    move[line2.move_id.id] = True
-            if line.reconcile_id:
-                for line2 in line.reconcile_id.line_id:
-                    move[line2.move_id.id] = True
-        invoice_ids = []
-        if move:
-            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
-        return invoice_ids
-
-    def _get_invoice_from_reconcile(self, cr, uid, ids, context=None):
-        move = {}
-        for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids, context=context):
-            for line in r.line_partial_ids:
-                move[line.move_id.id] = True
-            for line in r.line_id:
-                move[line.move_id.id] = True
-
-        invoice_ids = []
-        if move:
-            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
-        return invoice_ids
 
+class account_invoice(models.Model):
     _name = "account.invoice"
     _inherit = ['mail.thread']
-    _description = 'Invoice'
-    _order = "id desc"
+    _description = "Invoice"
+    _order = "number desc, id desc"
     _track = {
         'type': {
         },
@@ -229,692 +59,720 @@ class account_invoice(osv.osv):
             'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'open' and obj.type in ('out_invoice', 'out_refund'),
         },
     }
-    _columns = {
-        'name': fields.char('Description', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}),
-        'supplier_invoice_number': fields.char('Supplier Invoice Number', size=64, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}),
-        'type': fields.selection([
+
+    @api.one
+    @api.depends('invoice_line.price_subtotal', 'tax_line.amount')
+    def _compute_amount(self):
+        self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line)
+        self.amount_tax = sum(line.amount for line in self.tax_line)
+        self.amount_total = self.amount_untaxed + self.amount_tax
+
+    @api.model
+    def _default_journal(self):
+        inv_type = self._context.get('type', 'out_invoice')
+        inv_types = inv_type if isinstance(inv_type, list) else [inv_type]
+        company_id = self._context.get('company_id', self.env.user.company_id.id)
+        domain = [
+            ('type', 'in', filter(None, map(TYPE2JOURNAL.get, inv_types))),
+            ('company_id', '=', company_id),
+        ]
+        return self.env['account.journal'].search(domain, limit=1)
+
+    @api.model
+    def _default_currency(self):
+        journal = self._default_journal()
+        return journal.currency or journal.company_id.currency_id
+
+    @api.model
+    @api.returns('account.analytic.journal', lambda r: r.id)
+    def _get_journal_analytic(self, inv_type):
+        """ Return the analytic journal corresponding to the given invoice type. """
+        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
+        journal_type = type2journal.get(inv_type, 'sale')
+        journal = self.env['account.analytic.journal'].search([('type', '=', journal_type)], limit=1)
+        if not journal:
+            raise except_orm(_('No Analytic Journal!'),
+                _("You must define an analytic journal of type '%s'!") % (journal_type,))
+        return journal[0]
+
+    @api.one
+    @api.depends('account_id', 'move_id.line_id.account_id', 'move_id.line_id.reconcile_id')
+    def _compute_reconciled(self):
+        self.reconciled = self.test_paid()
+
+    @api.model
+    def _get_reference_type(self):
+        return [('none', _('Free Reference'))]
+
+    @api.one
+    @api.depends(
+        'state', 'currency_id', 'invoice_line.price_subtotal',
+        'move_id.line_id.account_id.type',
+        'move_id.line_id.amount_residual',
+        # Fixes the fact that move_id.line_id.amount_residual, being not stored and old API, doesn't trigger recomputation
+        'move_id.line_id.reconcile_id',
+        'move_id.line_id.amount_residual_currency',
+        'move_id.line_id.currency_id',
+        'move_id.line_id.reconcile_partial_id.line_partial_ids.invoice.type',
+    )
+    # An invoice's residual amount is the sum of its unreconciled move lines and,
+    # for partially reconciled move lines, their residual amount divided by the
+    # number of times this reconciliation is used in an invoice (so we split
+    # the residual amount between all invoice)
+    def _compute_residual(self):
+        self.residual = 0.0
+        # Each partial reconciliation is considered only once for each invoice it appears into,
+        # and its residual amount is divided by this number of invoices
+        partial_reconciliations_done = []
+        for line in self.sudo().move_id.line_id:
+            if line.account_id.type not in ('receivable', 'payable'):
+                continue
+            if line.reconcile_partial_id and line.reconcile_partial_id.id in partial_reconciliations_done:
+                continue
+            # Get the correct line residual amount
+            if line.currency_id == self.currency_id:
+                line_amount = line.currency_id and line.amount_residual_currency or line.amount_residual
+            else:
+                from_currency = line.company_id.currency_id.with_context(date=line.date)
+                line_amount = from_currency.compute(line.amount_residual, self.currency_id)
+            # For partially reconciled lines, split the residual amount
+            if line.reconcile_partial_id:
+                partial_reconciliation_invoices = set()
+                for pline in line.reconcile_partial_id.line_partial_ids:
+                    if pline.invoice and self.type == pline.invoice.type:
+                        partial_reconciliation_invoices.update([pline.invoice.id])
+                line_amount = self.currency_id.round(line_amount / len(partial_reconciliation_invoices))
+                partial_reconciliations_done.append(line.reconcile_partial_id.id)
+            self.residual += line_amount
+        self.residual = max(self.residual, 0.0)
+
+    @api.one
+    @api.depends(
+        'move_id.line_id.account_id',
+        'move_id.line_id.reconcile_id.line_id',
+        'move_id.line_id.reconcile_partial_id.line_partial_ids',
+    )
+    def _compute_move_lines(self):
+        # Give Journal Items related to the payment reconciled to this invoice.
+        # Return partial and total payments related to the selected invoice.
+        self.move_lines = self.env['account.move.line']
+        if not self.move_id:
+            return
+        data_lines = self.move_id.line_id.filtered(lambda l: l.account_id == self.account_id)
+        partial_lines = self.env['account.move.line']
+        for data_line in data_lines:
+            if data_line.reconcile_id:
+                lines = data_line.reconcile_id.line_id
+            elif data_line.reconcile_partial_id:
+                lines = data_line.reconcile_partial_id.line_partial_ids
+            else:
+                lines = self.env['account.move.line']
+            partial_lines += data_line
+            self.move_lines = lines - partial_lines
+
+    @api.one
+    @api.depends(
+        'move_id.line_id.reconcile_id.line_id',
+        'move_id.line_id.reconcile_partial_id.line_partial_ids',
+    )
+    def _compute_payments(self):
+        partial_lines = lines = self.env['account.move.line']
+        for line in self.move_id.line_id:
+            if line.account_id != self.account_id:
+                continue
+            if line.reconcile_id:
+                lines |= line.reconcile_id.line_id
+            elif line.reconcile_partial_id:
+                lines |= line.reconcile_partial_id.line_partial_ids
+            partial_lines += line
+        self.payment_ids = (lines - partial_lines).sorted()
+
+    name = fields.Char(string='Reference/Description', index=True,
+        readonly=True, states={'draft': [('readonly', False)]})
+    origin = fields.Char(string='Source Document',
+        help="Reference of the document that produced this invoice.",
+        readonly=True, states={'draft': [('readonly', False)]})
+    supplier_invoice_number = fields.Char(string='Supplier Invoice Number',
+        help="The reference of this invoice as provided by the supplier.",
+        readonly=True, states={'draft': [('readonly', False)]})
+    type = fields.Selection([
             ('out_invoice','Customer Invoice'),
             ('in_invoice','Supplier Invoice'),
             ('out_refund','Customer Refund'),
             ('in_refund','Supplier Refund'),
-            ],'Type', readonly=True, select=True, change_default=True, track_visibility='always'),
-
-        'number': fields.related('move_id','name', type='char', readonly=True, size=64, relation='account.move', store=True, string='Number'),
-        'internal_number': fields.char('Invoice Number', size=32, readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."),
-        'reference': fields.char('Invoice Reference', size=64, help="The partner reference of this invoice."),
-        'reference_type': fields.selection(_get_reference_type, 'Payment Reference',
-            required=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'comment': fields.text('Additional Information'),
-
-        'state': fields.selection([
+        ], string='Type', readonly=True, index=True, change_default=True,
+        default=lambda self: self._context.get('type', 'out_invoice'),
+        track_visibility='always')
+
+    number = fields.Char(related='move_id.name', store=True, readonly=True, copy=False)
+    internal_number = fields.Char(string='Invoice Number', readonly=True,
+        default=False, copy=False,
+        help="Unique number of the invoice, computed automatically when the invoice is created.")
+    reference = fields.Char(string='Invoice Reference',
+        help="The partner reference of this invoice.")
+    reference_type = fields.Selection('_get_reference_type', string='Payment Reference',
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        default='none')
+    comment = fields.Text('Additional Information')
+
+    state = fields.Selection([
             ('draft','Draft'),
             ('proforma','Pro-forma'),
             ('proforma2','Pro-forma'),
             ('open','Open'),
             ('paid','Paid'),
             ('cancel','Cancelled'),
-            ],'Status', select=True, readonly=True, track_visibility='onchange',
-            help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \
-            \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \
-            \n* The \'Open\' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice. \
-            \n* The \'Paid\' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled. \
-            \n* The \'Cancelled\' status is used when user cancel invoice.'),
-        'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."),
-        'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"),
-        'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True,
-            help="If you use payment terms, the due date will be computed automatically at the generation "\
-                "of accounting entries. The payment term may compute several due dates, for example 50% now and 50% in one month, but if you want to force a due date, make sure that the payment term is not set on the invoice. If you keep the payment term and the due date empty, it means direct payment."),
-        'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
-        'payment_term': fields.many2one('account.payment.term', 'Payment Terms',readonly=True, states={'draft':[('readonly',False)]},
-            help="If you use payment terms, the due date will be computed automatically at the generation "\
-                "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\
-                "The payment term may compute several due dates, for example 50% now, 50% in one month."),
-        'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}),
-
-        'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
-        'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
-        'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
-
-        'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."),
-        'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Subtotal', track_visibility='always',
-            store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
-                'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
-            },
-            multi='all'),
-        'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax',
-            store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
-                'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
-            },
-            multi='all'),
-        'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total',
-            store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
-                'account.invoice.tax': (_get_invoice_tax, None, 20),
-                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
-            },
-            multi='all'),
-        'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
-        'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]},
-                                      domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"),
-        'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
-        'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',
-            store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
-                'account.move.line': (_get_invoice_from_line, None, 50),
-                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
-            }, help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment."),
-        'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account',
-            help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.', readonly=True, states={'draft':[('readonly',False)]}),
-        'move_lines':fields.function(_get_lines, type='many2many', relation='account.move.line', string='Entry Lines'),
-        'residual': fields.function(_amount_residual, digits_compute=dp.get_precision('Account'), string='Balance',
-            store={
-                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','move_id'], 50),
-                'account.invoice.tax': (_get_invoice_tax, None, 50),
-                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 50),
-                'account.move.line': (_get_invoice_from_line, None, 50),
-                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
-            },
-            help="Remaining amount due."),
-        'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments', groups='base.group_user'),
-        'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}),
-        'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, track_visibility='onchange', states={'draft':[('readonly',False)]}),
-        'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}),
-        'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one',
-                                                relation='res.partner', store=True, readonly=True,
-                                                help="The commercial entity that will be used on Journal Entries for this invoice")
-    }
-    _defaults = {
-        'type': _get_type,
-        'state': 'draft',
-        'journal_id': _get_journal,
-        'currency_id': _get_currency,
-        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.invoice', context=c),
-        'reference_type': 'none',
-        'check_total': 0.0,
-        'internal_number': False,
-        'user_id': lambda s, cr, u, c: u,
-        'sent': False,
-    }
+        ], string='Status', index=True, readonly=True, default='draft',
+        track_visibility='onchange', copy=False,
+        help=" * The 'Draft' status is used when a user is encoding a new and unconfirmed Invoice.\n"
+             " * The 'Pro-forma' when invoice is in Pro-forma status,invoice does not have an invoice number.\n"
+             " * The 'Open' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice.\n"
+             " * The 'Paid' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled.\n"
+             " * The 'Cancelled' status is used when user cancel invoice.")
+    sent = fields.Boolean(readonly=True, default=False, copy=False,
+        help="It indicates that the invoice has been sent.")
+    date_invoice = fields.Date(string='Invoice Date',
+        readonly=True, states={'draft': [('readonly', False)]}, index=True,
+        help="Keep empty to use the current date", copy=False)
+    date_due = fields.Date(string='Due Date',
+        readonly=True, states={'draft': [('readonly', False)]}, index=True, copy=False,
+        help="If you use payment terms, the due date will be computed automatically at the generation "
+             "of accounting entries. The payment term may compute several due dates, for example 50% "
+             "now and 50% in one month, but if you want to force a due date, make sure that the payment "
+             "term is not set on the invoice. If you keep the payment term and the due date empty, it "
+             "means direct payment.")
+    partner_id = fields.Many2one('res.partner', string='Partner', change_default=True,
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        track_visibility='always')
+    payment_term = fields.Many2one('account.payment.term', string='Payment Terms',
+        readonly=True, states={'draft': [('readonly', False)]},
+        help="If you use payment terms, the due date will be computed automatically at the generation "
+             "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "
+             "The payment term may compute several due dates, for example 50% now, 50% in one month.")
+    period_id = fields.Many2one('account.period', string='Force Period',
+        domain=[('state', '!=', 'done')], copy=False,
+        help="Keep empty to use the period of the validation(invoice) date.",
+        readonly=True, states={'draft': [('readonly', False)]})
+
+    account_id = fields.Many2one('account.account', string='Account',
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        help="The partner account used for this invoice.")
+    invoice_line = fields.One2many('account.invoice.line', 'invoice_id', string='Invoice Lines',
+        readonly=True, states={'draft': [('readonly', False)]}, copy=True)
+    tax_line = fields.One2many('account.invoice.tax', 'invoice_id', string='Tax Lines',
+        readonly=True, states={'draft': [('readonly', False)]}, copy=True)
+    move_id = fields.Many2one('account.move', string='Journal Entry',
+        readonly=True, index=True, ondelete='restrict', copy=False,
+        help="Link to the automatically generated Journal Items.")
+
+    amount_untaxed = fields.Float(string='Subtotal', digits=dp.get_precision('Account'),
+        store=True, readonly=True, compute='_compute_amount', track_visibility='always')
+    amount_tax = fields.Float(string='Tax', digits=dp.get_precision('Account'),
+        store=True, readonly=True, compute='_compute_amount')
+    amount_total = fields.Float(string='Total', digits=dp.get_precision('Account'),
+        store=True, readonly=True, compute='_compute_amount')
+
+    currency_id = fields.Many2one('res.currency', string='Currency',
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        default=_default_currency, track_visibility='always')
+    journal_id = fields.Many2one('account.journal', string='Journal',
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        default=_default_journal,
+        domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]")
+    company_id = fields.Many2one('res.company', string='Company', change_default=True,
+        required=True, readonly=True, states={'draft': [('readonly', False)]},
+        default=lambda self: self.env['res.company']._company_default_get('account.invoice'))
+    check_total = fields.Float(string='Verification Total', digits=dp.get_precision('Account'),
+        readonly=True, states={'draft': [('readonly', False)]}, default=0.0)
+
+    reconciled = fields.Boolean(string='Paid/Reconciled',
+        store=True, readonly=True, compute='_compute_reconciled',
+        help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment.")
+    partner_bank_id = fields.Many2one('res.partner.bank', string='Bank Account',
+        help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.',
+        readonly=True, states={'draft': [('readonly', False)]})
+
+    move_lines = fields.Many2many('account.move.line', string='Entry Lines',
+        compute='_compute_move_lines')
+    residual = fields.Float(string='Balance', digits=dp.get_precision('Account'),
+        compute='_compute_residual', store=True,
+        help="Remaining amount due.")
+    payment_ids = fields.Many2many('account.move.line', string='Payments',
+        compute='_compute_payments')
+    move_name = fields.Char(string='Journal Entry', readonly=True,
+        states={'draft': [('readonly', False)]}, copy=False)
+    user_id = fields.Many2one('res.users', string='Salesperson', track_visibility='onchange',
+        readonly=True, states={'draft': [('readonly', False)]},
+        default=lambda self: self.env.user)
+    fiscal_position = fields.Many2one('account.fiscal.position', string='Fiscal Position',
+        readonly=True, states={'draft': [('readonly', False)]})
+    commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity',
+        related='partner_id.commercial_partner_id', store=True, readonly=True,
+        help="The commercial entity that will be used on Journal Entries for this invoice")
+
     _sql_constraints = [
-        ('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'),
+        ('number_uniq', 'unique(number, company_id, journal_id, type)',
+            'Invoice Number must be unique per Company!'),
     ]
 
+    @api.model
+    def fields_view_get(self, view_id=None, view_type=False, toolbar=False, submenu=False):
+        context = self._context
 
+        def get_view_id(xid, name):
+            try:
+                return self.env['ir.model.data'].xmlid_to_res_id('account.' + xid, raise_if_not_found=True)
+            except ValueError:
+                try:
+                    return self.env['ir.ui.view'].search([('name', '=', name)], limit=1).id
+                except Exception:
+                    return False    # view not found
 
-
-    def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
-        journal_obj = self.pool.get('account.journal')
-        if context is None:
-            context = {}
-
-        if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']:
-            partner = self.pool[context['active_model']].read(cr, uid, context['active_ids'], ['supplier','customer'])[0]
+        if context.get('active_model') == 'res.partner' and context.get('active_ids'):
+            partner = self.env['res.partner'].browse(context['active_ids'])[0]
             if not view_type:
-                try:
-                    view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_tree')[1]
-                except ValueError:
-                    view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')], limit=1)
+                view_id = get_view_id('invoice_tree', 'account.invoice.tree')
                 view_type = 'tree'
-            if view_type == 'form':
-                if partner['supplier'] and not partner['customer']:
-                    try:
-                        view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_supplier_form')[1]
-                    except ValueError:
-                        view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')], limit=1)
-                elif partner['customer'] and not partner['supplier']:
-                    try:
-                        view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_form')[1]
-                    except ValueError:
-                        view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')], limit=1)
-        if view_id and isinstance(view_id, (list, tuple)):
-            view_id = view_id[0]
-        res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
-
-        type = context.get('journal_type', False)
+            elif view_type == 'form':
+                if partner.supplier and not partner.customer:
+                    view_id = get_view_id('invoice_supplier_form', 'account.invoice.supplier.form')
+                elif partner.customer and not partner.supplier:
+                    view_id = get_view_id('invoice_form', 'account.invoice.form')
+
+        res = super(account_invoice, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
+
+        # adapt selection of field journal_id
         for field in res['fields']:
             if field == 'journal_id' and type:
-                journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1)
+                journal_select = self.env['account.journal']._name_search('', [('type', '=', type)], name_get_uid=1)
                 res['fields'][field]['selection'] = journal_select
 
         doc = etree.XML(res['arch'])
 
-        if context.get('type', False):
+        if context.get('type'):
             for node in doc.xpath("//field[@name='partner_bank_id']"):
                 if context['type'] == 'in_refund':
                     node.set('domain', "[('partner_id.ref_companies', 'in', [company_id])]")
                 elif context['type'] == 'out_refund':
                     node.set('domain', "[('partner_id', '=', partner_id)]")
-            res['arch'] = etree.tostring(doc)
 
         if view_type == 'search':
-            if context.get('type', 'in_invoice') in ('out_invoice', 'out_refund'):
+            if context.get('type') in ('out_invoice', 'out_refund'):
                 for node in doc.xpath("//group[@name='extended filter']"):
                     doc.remove(node)
-            res['arch'] = etree.tostring(doc)
 
         if view_type == 'tree':
             partner_string = _('Customer')
-            if context.get('type', 'out_invoice') in ('in_invoice', 'in_refund'):
+            if context.get('type') in ('in_invoice', 'in_refund'):
                 partner_string = _('Supplier')
                 for node in doc.xpath("//field[@name='reference']"):
                     node.set('invisible', '0')
             for node in doc.xpath("//field[@name='partner_id']"):
                 node.set('string', partner_string)
-            res['arch'] = etree.tostring(doc)
-        return res
 
-    def get_log_context(self, cr, uid, context=None):
-        if context is None:
-            context = {}
-        res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form')
-        view_id = res and res[1] or False
-        context['view_id'] = view_id
-        return context
-
-    def invoice_print(self, cr, uid, ids, context=None):
-        '''
-        This function prints the invoice and mark it as sent, so that we can see more easily the next step of the workflow
-        '''
-        assert len(ids) == 1, 'This option should only be used for a single id at a time.'
-        self.write(cr, uid, ids, {'sent': True}, context=context)
-        datas = {
-             'ids': ids,
-             'model': 'account.invoice',
-             'form': self.read(cr, uid, ids[0], context=context)
-        }
-        return {
-            'type': 'ir.actions.report.xml',
-            'report_name': 'account.invoice',
-            'datas': datas,
-            'nodestroy' : True
-        }
+        res['arch'] = etree.tostring(doc)
+        return res
 
-    def action_invoice_sent(self, cr, uid, ids, context=None):
-        '''
-        This function opens a window to compose an email, with the edi invoice template message loaded by default
-        '''
-        assert len(ids) == 1, 'This option should only be used for a single id at a time.'
-        ir_model_data = self.pool.get('ir.model.data')
-        try:
-            template_id = ir_model_data.get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1]
-        except ValueError:
-            template_id = False
-        try:
-            compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
-        except ValueError:
-            compose_form_id = False
-        ctx = dict(context)
-        ctx.update({
-            'default_model': 'account.invoice',
-            'default_res_id': ids[0],
-            'default_use_template': bool(template_id),
-            'default_template_id': template_id,
-            'default_composition_mode': 'comment',
-            'mark_invoice_as_sent': True,
-            })
+    @api.multi
+    def invoice_print(self):
+        """ Print the invoice and mark it as sent, so that we can see more
+            easily the next step of the workflow
+        """
+        assert len(self) == 1, 'This option should only be used for a single id at a time.'
+        self.sent = True
+        return self.env['report'].get_action(self, 'account.report_invoice')
+
+    @api.multi
+    def action_invoice_sent(self):
+        """ Open a window to compose an email, with the edi invoice template
+            message loaded by default
+        """
+        assert len(self) == 1, 'This option should only be used for a single id at a time.'
+        template = self.env.ref('account.email_template_edi_invoice', False)
+        compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
+        ctx = dict(
+            default_model='account.invoice',
+            default_res_id=self.id,
+            default_use_template=bool(template),
+            default_template_id=template.id,
+            default_composition_mode='comment',
+            mark_invoice_as_sent=True,
+        )
         return {
             'name': _('Compose Email'),
             'type': 'ir.actions.act_window',
             'view_type': 'form',
             'view_mode': 'form',
             'res_model': 'mail.compose.message',
-            'views': [(compose_form_id, 'form')],
-            'view_id': compose_form_id,
+            'views': [(compose_form.id, 'form')],
+            'view_id': compose_form.id,
             'target': 'new',
             'context': ctx,
         }
 
-    def confirm_paid(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        self.write(cr, uid, ids, {'state':'paid'}, context=context)
-        return True
-
-    def unlink(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        invoices = self.read(cr, uid, ids, ['state','internal_number'], context=context)
-        unlink_ids = []
-
-        for t in invoices:
-            if t['state'] not in ('draft', 'cancel'):
-                raise openerp.exceptions.Warning(_('You cannot delete an invoice which is not draft or cancelled. You should refund it instead.'))
-            elif t['internal_number']:
-                raise openerp.exceptions.Warning(_('You cannot delete an invoice after it has been validated (and received a number).  You can set it back to "Draft" state and modify its content, then re-confirm it.'))
-            else:
-                unlink_ids.append(t['id'])
-
-        osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
-        return True
-
-    def onchange_partner_id(self, cr, uid, ids, type, partner_id,\
-            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
-        partner_payment_term = False
-        acc_id = False
-        bank_id = False
+    @api.multi
+    def confirm_paid(self):
+        return self.write({'state': 'paid'})
+
+    @api.multi
+    def unlink(self):
+        for invoice in self:
+            if invoice.state not in ('draft', 'cancel'):
+                raise Warning(_('You cannot delete an invoice which is not draft or cancelled. You should refund it instead.'))
+            elif invoice.internal_number:
+                raise Warning(_('You cannot delete an invoice after it has been validated (and received a number).  You can set it back to "Draft" state and modify its content, then re-confirm it.'))
+        return super(account_invoice, self).unlink()
+
+    @api.multi
+    def onchange_partner_id(self, type, partner_id, date_invoice=False,
+            payment_term=False, partner_bank_id=False, company_id=False):
+        account_id = False
+        payment_term_id = False
         fiscal_position = False
+        bank_id = False
 
-        opt = [('uid', str(uid))]
         if partner_id:
-
-            opt.insert(0, ('id', partner_id))
-            p = self.pool.get('res.partner').browse(cr, uid, partner_id)
+            p = self.env['res.partner'].browse(partner_id)
+            rec_account = p.property_account_receivable
+            pay_account = p.property_account_payable
             if company_id:
-                if (p.property_account_receivable.company_id and (p.property_account_receivable.company_id.id != company_id)) and (p.property_account_payable.company_id and (p.property_account_payable.company_id.id != company_id)):
-                    property_obj = self.pool.get('ir.property')
-                    rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
-                    pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
-                    if not rec_pro_id:
-                        rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)])
-                    if not pay_pro_id:
-                        pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)])
-                    rec_line_data = property_obj.read(cr,uid,rec_pro_id,['name','value_reference','res_id'])
-                    pay_line_data = property_obj.read(cr,uid,pay_pro_id,['name','value_reference','res_id'])
-                    rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False
-                    pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False
-                    if not rec_res_id and not pay_res_id:
-                        raise osv.except_osv(_('Configuration Error!'),
-                            _('Cannot find a chart of accounts for this company, you should create one.'))
-                    account_obj = self.pool.get('account.account')
-                    rec_obj_acc = account_obj.browse(cr, uid, [rec_res_id])
-                    pay_obj_acc = account_obj.browse(cr, uid, [pay_res_id])
-                    p.property_account_receivable = rec_obj_acc[0]
-                    p.property_account_payable = pay_obj_acc[0]
+                if p.property_account_receivable.company_id and \
+                        p.property_account_receivable.company_id.id != company_id and \
+                        p.property_account_payable.company_id and \
+                        p.property_account_payable.company_id.id != company_id:
+                    prop = self.env['ir.property']
+                    rec_dom = [('name', '=', 'property_account_receivable'), ('company_id', '=', company_id)]
+                    pay_dom = [('name', '=', 'property_account_payable'), ('company_id', '=', company_id)]
+                    res_dom = [('res_id', '=', 'res.partner,%s' % partner_id)]
+                    rec_prop = prop.search(rec_dom + res_dom) or prop.search(rec_dom)
+                    pay_prop = prop.search(pay_dom + res_dom) or prop.search(pay_dom)
+                    rec_account = rec_prop.get_by_record(rec_prop)
+                    pay_account = pay_prop.get_by_record(pay_prop)
+                    if not rec_account and not pay_account:
+                        action = self.env.ref('account.action_account_config')
+                        msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.')
+                        raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
 
             if type in ('out_invoice', 'out_refund'):
-                acc_id = p.property_account_receivable.id
-                partner_payment_term = p.property_payment_term and p.property_payment_term.id or False
+                account_id = rec_account.id
+                payment_term_id = p.property_payment_term.id
             else:
-                acc_id = p.property_account_payable.id
-                partner_payment_term = p.property_supplier_payment_term and p.property_supplier_payment_term.id or False
-            fiscal_position = p.property_account_position and p.property_account_position.id or False
-            if p.bank_ids:
-                bank_id = p.bank_ids[0].id
+                account_id = pay_account.id
+                payment_term_id = p.property_supplier_payment_term.id
+            fiscal_position = p.property_account_position.id
+            bank_id = p.bank_ids and p.bank_ids[0].id or False
 
         result = {'value': {
-            'account_id': acc_id,
-            'payment_term': partner_payment_term,
-            'fiscal_position': fiscal_position
-            }
-        }
+            'account_id': account_id,
+            'payment_term': payment_term_id,
+            'fiscal_position': fiscal_position,
+        }}
 
         if type in ('in_invoice', 'in_refund'):
             result['value']['partner_bank_id'] = bank_id
 
-        if payment_term != partner_payment_term:
-            if partner_payment_term:
-                to_update = self.onchange_payment_term_date_invoice(
-                    cr, uid, ids, partner_payment_term, date_invoice)
-                result['value'].update(to_update['value'])
+        if payment_term != payment_term_id:
+            if payment_term_id:
+                to_update = self.onchange_payment_term_date_invoice(payment_term_id, date_invoice)
+                result['value'].update(to_update.get('value', {}))
             else:
                 result['value']['date_due'] = False
 
         if partner_bank_id != bank_id:
-            to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
-            result['value'].update(to_update['value'])
+            to_update = self.onchange_partner_bank(bank_id)
+            result['value'].update(to_update.get('value', {}))
+
         return result
 
-    def onchange_journal_id(self, cr, uid, ids, journal_id=False, context=None):
-        result = {}
+    @api.multi
+    def onchange_journal_id(self, journal_id=False):
         if journal_id:
-            journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
-            currency_id = journal.currency and journal.currency.id or journal.company_id.currency_id.id
-            company_id = journal.company_id.id
-            result = {'value': {
-                    'currency_id': currency_id,
-                    'company_id': company_id,
-                    }
+            journal = self.env['account.journal'].browse(journal_id)
+            return {
+                'value': {
+                    'currency_id': journal.currency.id or journal.company_id.currency_id.id,
+                    'company_id': journal.company_id.id,
                 }
-        return result
+            }
+        return {}
 
-    def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice):
-        res = {}
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @api.multi
+    def onchange_payment_term_date_invoice(self, payment_term_id, date_invoice):
         if not date_invoice:
-            date_invoice = fields.date.context_today(self, cr, uid)
+            date_invoice = fields.Date.context_today(self)
         if not payment_term_id:
-            inv = self.browse(cr, uid, ids[0])
-            #To make sure the invoice due date should contain due date which is entered by user when there is no payment term defined
-            return {'value':{'date_due': inv.date_due and inv.date_due or date_invoice}}
-        pterm_list = self.pool.get('account.payment.term').compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
+            # To make sure the invoice due date should contain due date which is
+            # entered by user when there is no payment term defined
+            return {'value': {'date_due': self.date_due or date_invoice}}
+        pterm = self.env['account.payment.term'].browse(payment_term_id)
+        pterm_list = pterm.compute(value=1, date_ref=date_invoice)[0]
         if pterm_list:
-            pterm_list = [line[0] for line in pterm_list]
-            pterm_list.sort()
-            res = {'value':{'date_due': pterm_list[-1]}}
+            return {'value': {'date_due': max(line[0] for line in pterm_list)}}
         else:
-             raise osv.except_osv(_('Insufficient Data!'), _('The payment term of supplier does not have a payment term line.'))
-        return res
+            raise except_orm(_('Insufficient Data!'),
+                _('The payment term of supplier does not have a payment term line.'))
 
-    def onchange_invoice_line(self, cr, uid, ids, lines):
+    @api.multi
+    def onchange_invoice_line(self, lines):
         return {}
 
-    def onchange_partner_bank(self, cursor, user, ids, partner_bank_id=False):
+    @api.multi
+    def onchange_partner_bank(self, partner_bank_id=False):
         return {'value': {}}
 
-    def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line, currency_id, context=None):
-        #TODO: add the missing context parameter when forward-porting in trunk so we can remove
-        #      this hack!
-        context = self.pool['res.users'].context_get(cr, uid)
+    @api.multi
+    def onchange_company_id(self, company_id, part_id, type, invoice_line, currency_id):
+        # TODO: add the missing context parameter when forward-porting in trunk
+        # so we can remove this hack!
+        self = self.with_context(self.env['res.users'].context_get())
 
-        val = {}
-        dom = {}
-        obj_journal = self.pool.get('account.journal')
-        account_obj = self.pool.get('account.account')
-        inv_line_obj = self.pool.get('account.invoice.line')
+        values = {}
+        domain = {}
 
         if company_id and part_id and type:
-            acc_id = False
-            partner_obj = self.pool.get('res.partner').browse(cr, uid, part_id, context=context)
-
-            if partner_obj.property_account_payable and partner_obj.property_account_receivable:
-                if partner_obj.property_account_payable.company_id.id != company_id and partner_obj.property_account_receivable.company_id.id != company_id:
-                    property_obj = self.pool.get('ir.property')
-                    rec_pro_id = property_obj.search(cr, uid, [('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
-                    pay_pro_id = property_obj.search(cr, uid, [('name','=','property_account_payable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
-
-                    if not rec_pro_id:
-                        rec_pro_id = property_obj.search(cr, uid, [('name','=','property_account_receivable'),('company_id','=',company_id)])
-                    if not pay_pro_id:
-                        pay_pro_id = property_obj.search(cr, uid, [('name','=','property_account_payable'),('company_id','=',company_id)])
-
-                    rec_line_data = property_obj.read(cr, uid, rec_pro_id, ['name','value_reference','res_id'])
-                    pay_line_data = property_obj.read(cr, uid, pay_pro_id, ['name','value_reference','res_id'])
-                    rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False
-                    pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False
-
-                    if not rec_res_id and not pay_res_id:
-                        raise self.pool.get('res.config.settings').get_config_warning(cr, _('Cannot find any chart of account: you can create a new one from %(menu:account.menu_account_config)s.'), context=context)
-
-                    if type in ('out_invoice', 'out_refund'):
-                        acc_id = rec_res_id
-                    else:
-                        acc_id = pay_res_id
+            p = self.env['res.partner'].browse(part_id)
+            if p.property_account_payable and p.property_account_receivable and \
+                    p.property_account_payable.company_id.id != company_id and \
+                    p.property_account_receivable.company_id.id != company_id:
+                prop = self.env['ir.property']
+                rec_dom = [('name', '=', 'property_account_receivable'), ('company_id', '=', company_id)]
+                pay_dom = [('name', '=', 'property_account_payable'), ('company_id', '=', company_id)]
+                res_dom = [('res_id', '=', 'res.partner,%s' % part_id)]
+                rec_prop = prop.search(rec_dom + res_dom) or prop.search(rec_dom)
+                pay_prop = prop.search(pay_dom + res_dom) or prop.search(pay_dom)
+                rec_account = rec_prop.get_by_record(rec_prop)
+                pay_account = pay_prop.get_by_record(pay_prop)
+                if not rec_account and not pay_account:
+                    action = self.env.ref('account.action_account_config')
+                    msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.')
+                    raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
+
+                if type in ('out_invoice', 'out_refund'):
+                    acc_id = rec_account.id
+                else:
+                    acc_id = pay_account.id
+                values= {'account_id': acc_id}
 
-                    val= {'account_id': acc_id}
-            if ids:
+            if self:
                 if company_id:
-                    inv_obj = self.browse(cr,uid,ids)
-                    for line in inv_obj[0].invoice_line:
-                        if line.account_id:
-                            if line.account_id.company_id.id != company_id:
-                                result_id = account_obj.search(cr, uid, [('name','=',line.account_id.name),('company_id','=',company_id)])
-                                if not result_id:
-                                    raise osv.except_osv(_('Configuration Error!'),
-                                        _('Cannot find a chart of account, you should create one from Settings\Configuration\Accounting menu.'))
-                                inv_line_obj.write(cr, uid, [line.id], {'account_id': result_id[-1]})
-            else:
-                if invoice_line:
-                    for inv_line in invoice_line:
-                        obj_l = account_obj.browse(cr, uid, inv_line[2]['account_id'])
-                        if obj_l.company_id.id != company_id:
-                            raise osv.except_osv(_('Configuration Error!'),
-                                _('Invoice line account\'s company and invoice\'s company does not match.'))
-                        else:
+                    for line in self.invoice_line:
+                        if not line.account_id:
                             continue
-        if company_id and type:
-            journal_mapping = {
-               'out_invoice': 'sale',
-               'out_refund': 'sale_refund',
-               'in_refund': 'purchase_refund',
-               'in_invoice': 'purchase',
-            }
-            journal_type = journal_mapping[type]
-            journal_ids = obj_journal.search(cr, uid, [('company_id','=',company_id), ('type', '=', journal_type)])
-            if journal_ids:
-                val['journal_id'] = journal_ids[0]
-            ir_values_obj = self.pool.get('ir.values')
-            res_journal_default = ir_values_obj.get(cr, uid, 'default', 'type=%s' % (type), ['account.invoice'])
-            for r in res_journal_default:
-                if r[1] == 'journal_id' and r[2] in journal_ids:
-                    val['journal_id'] = r[2]
-            if not val.get('journal_id', False):
-                journal_type_map = dict(obj_journal._columns['type'].selection)
-                journal_type_label = self.pool['ir.translation']._get_source(cr, uid, None, ('code','selection'),
-                                                                             context.get('lang'),
-                                                                             journal_type_map.get(journal_type))
-                raise osv.except_osv(_('Configuration Error!'),
-                                     _('Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals.') % ('"%s"' % journal_type_label))
-            dom = {'journal_id':  [('id', 'in', journal_ids)]}
-        else:
-            journal_ids = obj_journal.search(cr, uid, [])
-
-        return {'value': val, 'domain': dom}
+                        if line.account_id.company_id.id == company_id:
+                            continue
+                        accounts = self.env['account.account'].search([('name', '=', line.account_id.name), ('company_id', '=', company_id)])
+                        if not accounts:
+                            action = self.env.ref('account.action_account_config')
+                            msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.')
+                            raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
+                        line.write({'account_id': accounts[-1].id})
+            else:
+                for line_cmd in invoice_line or []:
+                    if len(line_cmd) >= 3 and isinstance(line_cmd[2], dict):
+                        line = self.env['account.account'].browse(line_cmd[2]['account_id'])
+                        if line.company_id.id != company_id:
+                            raise except_orm(
+                                _('Configuration Error!'),
+                                _("Invoice line account's company and invoice's company does not match.")
+                            )
 
-    # go from canceled state to draft state
-    def action_cancel_draft(self, cr, uid, ids, *args):
-        self.write(cr, uid, ids, {'state':'draft'})
-        self.delete_workflow(cr, uid, ids)
-        self.create_workflow(cr, uid, ids)
+        if company_id and type:
+            journal_type = TYPE2JOURNAL[type]
+            journals = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company_id)])
+            if journals:
+                values['journal_id'] = journals[0].id
+            journal_defaults = self.env['ir.values'].get_defaults_dict('account.invoice', 'type=%s' % type)
+            if 'journal_id' in journal_defaults:
+                values['journal_id'] = journal_defaults['journal_id']
+            if not values.get('journal_id'):
+                field_desc = journals.fields_get(['type'])
+                type_label = next(t for t, label in field_desc['type']['selection'] if t == journal_type)
+                action = self.env.ref('account.action_account_journal_form')
+                msg = _('Cannot find any account journal of type "%s" for this company, You should create one.\n Please go to Journal Configuration') % type_label
+                raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
+            domain = {'journal_id':  [('id', 'in', journals.ids)]}
+
+        return {'value': values, 'domain': domain}
+
+    @api.multi
+    def action_cancel_draft(self):
+        # go from canceled state to draft state
+        self.write({'state': 'draft'})
+        self.delete_workflow()
+        self.create_workflow()
         return True
 
-    # ----------------------------------------
-    # Mail related methods
-    # ----------------------------------------
-
-    def _get_formview_action(self, cr, uid, id, context=None):
+    @api.one
+    @api.returns('ir.ui.view')
+    def get_formview_id(self):
         """ Update form view id of action to open the invoice """
-        action = super(account_invoice, self)._get_formview_action(cr, uid, id, context=context)
-        obj = self.browse(cr, uid, id, context=context)
-        if obj.type == 'in_invoice':
-            model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
-            action.update({
-                'views': [(view_id, 'form')],
-                })
+        if self.type == 'in_invoice':
+            return self.env.ref('account.invoice_supplier_form')
         else:
-            model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form')
-            action.update({
-                'views': [(view_id, 'form')],
-                })
-        return action
-
-    # Workflow stuff
-    #################
-
-    # return the ids of the move lines which has the same account than the invoice
-    # whose id is in ids
-    def move_line_id_payment_get(self, cr, uid, ids, *args):
-        if not ids: return []
-        result = self.move_line_id_payment_gets(cr, uid, ids, *args)
-        return result.get(ids[0], [])
-
-    def move_line_id_payment_gets(self, cr, uid, ids, *args):
-        res = {}
-        if not ids: return res
-        cr.execute('SELECT i.id, l.id '\
-                   'FROM account_move_line l '\
-                   'LEFT JOIN account_invoice i ON (i.move_id=l.move_id) '\
-                   'WHERE i.id IN %s '\
-                   'AND l.account_id=i.account_id',
-                   (tuple(ids),))
-        for r in cr.fetchall():
-            res.setdefault(r[0], [])
-            res[r[0]].append( r[1] )
-        return res
-
-    def copy(self, cr, uid, id, default=None, context=None):
-        default = default or {}
-        default.update({
-            'state':'draft',
-            'number':False,
-            'move_id':False,
-            'move_name':False,
-            'internal_number': False,
-            'period_id': False,
-            'sent': False,
-        })
-        if 'date_invoice' not in default:
-            default.update({
-                'date_invoice':False
-            })
-        if 'date_due' not in default:
-            default.update({
-                'date_due':False
-            })
-        return super(account_invoice, self).copy(cr, uid, id, default, context)
+            return self.env.ref('account.invoice_form')
 
-    def test_paid(self, cr, uid, ids, *args):
-        res = self.move_line_id_payment_get(cr, uid, ids)
-        if not res:
+    @api.multi
+    def move_line_id_payment_get(self):
+        # return the move line ids with the same account as the invoice self
+        if not self.id:
+            return []
+        query = """ SELECT l.id
+                    FROM account_move_line l, account_invoice i
+                    WHERE i.id = %s AND l.move_id = i.move_id AND l.account_id = i.account_id
+                """
+        self._cr.execute(query, (self.id,))
+        return [row[0] for row in self._cr.fetchall()]
+
+    @api.multi
+    def test_paid(self):
+        # check whether all corresponding account move lines are reconciled
+        line_ids = self.move_line_id_payment_get()
+        if not line_ids:
             return False
-        ok = True
-        for id in res:
-            cr.execute('select reconcile_id from account_move_line where id=%s', (id,))
-            ok = ok and  bool(cr.fetchone()[0])
-        return ok
-
-    def button_reset_taxes(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        ctx = context.copy()
-        ait_obj = self.pool.get('account.invoice.tax')
-        for id in ids:
-            cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (id,))
-            partner = self.browse(cr, uid, id, context=ctx).partner_id
+        query = "SELECT reconcile_id FROM account_move_line WHERE id IN %s"
+        self._cr.execute(query, (tuple(line_ids),))
+        return all(row[0] for row in self._cr.fetchall())
+
+    @api.multi
+    def button_reset_taxes(self):
+        account_invoice_tax = self.env['account.invoice.tax']
+        ctx = dict(self._context)
+        for invoice in self:
+            self._cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (invoice.id,))
+            self.invalidate_cache()
+            partner = invoice.partner_id
             if partner.lang:
-                ctx.update({'lang': partner.lang})
-            for taxe in ait_obj.compute(cr, uid, id, context=ctx).values():
-                ait_obj.create(cr, uid, taxe)
-        # Update the stored value (fields.function), so we write to trigger recompute
-        self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=ctx)
-        return True
-
-    def button_compute(self, cr, uid, ids, context=None, set_total=False):
-        self.button_reset_taxes(cr, uid, ids, context)
-        for inv in self.browse(cr, uid, ids, context=context):
+                ctx['lang'] = partner.lang
+            for taxe in account_invoice_tax.compute(invoice).values():
+                account_invoice_tax.create(taxe)
+        # dummy write on self to trigger recomputations
+        return self.with_context(ctx).write({'invoice_line': []})
+
+    @api.multi
+    def button_compute(self, set_total=False):
+        self.button_reset_taxes()
+        for invoice in self:
             if set_total:
-                self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
+                invoice.check_total = invoice.amount_total
         return True
 
-    def _convert_ref(self, cr, uid, ref):
-        return (ref or '').replace('/','')
-
-    def _get_analytic_lines(self, cr, uid, id, context=None):
-        if context is None:
-            context = {}
-        inv = self.browse(cr, uid, id)
-        cur_obj = self.pool.get('res.currency')
-
-        company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id
-        if inv.type in ('out_invoice', 'in_refund'):
-            sign = 1
-        else:
-            sign = -1
+    @api.multi
+    def _get_analytic_lines(self):
+        """ Return a list of dict for creating analytic lines for self[0] """
+        company_currency = self.company_id.currency_id
+        sign = 1 if self.type in ('out_invoice', 'in_refund') else -1
 
-        iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id, context=context)
+        iml = self.env['account.invoice.line'].move_line_get(self.id)
         for il in iml:
             if il['account_analytic_id']:
-                if inv.type in ('in_invoice', 'in_refund'):
-                    ref = inv.reference
+                if self.type in ('in_invoice', 'in_refund'):
+                    ref = self.reference
                 else:
-                    ref = self._convert_ref(cr, uid, inv.number)
-                if not inv.journal_id.analytic_journal_id:
-                    raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (inv.journal_id.name,))
+                    ref = self.number
+                if not self.journal_id.analytic_journal_id:
+                    raise except_orm(_('No Analytic Journal!'),
+                        _("You have to define an analytic journal on the '%s' journal!") % (self.journal_id.name,))
+                currency = self.currency_id.with_context(date=self.date_invoice)
                 il['analytic_lines'] = [(0,0, {
                     'name': il['name'],
-                    'date': inv['date_invoice'],
+                    'date': self.date_invoice,
                     'account_id': il['account_analytic_id'],
                     'unit_amount': il['quantity'],
-                    'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign,
+                    'amount': currency.compute(il['price'], company_currency) * sign,
                     'product_id': il['product_id'],
                     'product_uom_id': il['uos_id'],
                     'general_account_id': il['account_id'],
-                    'journal_id': inv.journal_id.analytic_journal_id.id,
+                    'journal_id': self.journal_id.analytic_journal_id.id,
                     'ref': ref,
                 })]
         return iml
 
-    def action_date_assign(self, cr, uid, ids, *args):
-        for inv in self.browse(cr, uid, ids):
-            res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
-            if res and res['value']:
-                self.write(cr, uid, [inv.id], res['value'])
+    @api.multi
+    def action_date_assign(self):
+        for inv in self:
+            res = inv.onchange_payment_term_date_invoice(inv.payment_term.id, inv.date_invoice)
+            if res and res.get('value'):
+                inv.write(res['value'])
         return True
 
-    def finalize_invoice_move_lines(self, cr, uid, invoice_browse, move_lines):
-        """finalize_invoice_move_lines(cr, uid, invoice, move_lines) -> move_lines
-        Hook method to be overridden in additional modules to verify and possibly alter the
-        move lines to be created by an invoice, for special cases.
-        :param invoice_browse: browsable record of the invoice that is generating the move lines
-        :param move_lines: list of dictionaries with the account.move.lines (as for create())
-        :return: the (possibly updated) final move_lines to create for this invoice
+    @api.multi
+    def finalize_invoice_move_lines(self, move_lines):
+        """ finalize_invoice_move_lines(move_lines) -> move_lines
+
+            Hook method to be overridden in additional modules to verify and
+            possibly alter the move lines to be created by an invoice, for
+            special cases.
+            :param move_lines: list of dictionaries with the account.move.lines (as for create())
+            :return: the (possibly updated) final move_lines to create for this invoice
         """
         return move_lines
 
-    def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj):
-        company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id
-        if not inv.tax_line:
+    @api.multi
+    def check_tax_lines(self, compute_taxes):
+        account_invoice_tax = self.env['account.invoice.tax']
+        company_currency = self.company_id.currency_id
+        if not self.tax_line:
             for tax in compute_taxes.values():
-                ait_obj.create(cr, uid, tax)
+                account_invoice_tax.create(tax)
         else:
             tax_key = []
-            for tax in inv.tax_line:
+            precision = self.env['decimal.precision'].precision_get('Account')
+            for tax in self.tax_line:
                 if tax.manual:
                     continue
                 key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
                 tax_key.append(key)
-                if not key in compute_taxes:
-                    raise osv.except_osv(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
+                if key not in compute_taxes:
+                    raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
                 base = compute_taxes[key]['base']
-                precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
                 if float_compare(abs(base - tax.base), company_currency.rounding, precision_digits=precision) == 1:
-                    raise osv.except_osv(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.'))
+                    raise except_orm(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.'))
             for key in compute_taxes:
-                if not key in tax_key:
-                    raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.'))
+                if key not in tax_key:
+                    raise except_orm(_('Warning!'), _('Taxes are missing!\nClick on compute button.'))
 
-    def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None):
-        if context is None:
-            context={}
+    @api.multi
+    def compute_invoice_totals(self, company_currency, ref, invoice_move_lines):
         total = 0
         total_currency = 0
-        cur_obj = self.pool.get('res.currency')
-        for i in invoice_move_lines:
-            if inv.currency_id.id != company_currency:
-                context.update({'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)})
-                i['currency_id'] = inv.currency_id.id
-                i['amount_currency'] = i['price']
-                i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
-                        company_currency, i['price'],
-                        context=context)
+        for line in invoice_move_lines:
+            if self.currency_id != company_currency:
+                currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
+                line['currency_id'] = currency.id
+                line['amount_currency'] = line['price']
+                line['price'] = currency.compute(line['price'], company_currency)
             else:
-                i['amount_currency'] = False
-                i['currency_id'] = False
-            i['ref'] = ref
-            if inv.type in ('out_invoice','in_refund'):
-                total += i['price']
-                total_currency += i['amount_currency'] or i['price']
-                i['price'] = - i['price']
+                line['currency_id'] = False
+                line['amount_currency'] = False
+            line['ref'] = ref
+            if self.type in ('out_invoice','in_refund'):
+                total += line['price']
+                total_currency += line['amount_currency'] or line['price']
+                line['price'] = - line['price']
             else:
-                total -= i['price']
-                total_currency -= i['amount_currency'] or i['price']
+                total -= line['price']
+                total_currency -= line['amount_currency'] or line['price']
         return total, total_currency, invoice_move_lines
 
-    def inv_line_characteristic_hashcode(self, invoice, invoice_line):
+    def inv_line_characteristic_hashcode(self, invoice_line):
         """Overridable hashcode generation for invoice lines. Lines having the same hashcode
         will be grouped together if the journal has the 'group line' option. Of course a module
         can add fields to invoice lines that would need to be tested too before merging lines
         or not."""
-        return "%s-%s-%s-%s-%s"%(
+        return "%s-%s-%s-%s-%s" % (
             invoice_line['account_id'],
-            invoice_line.get('tax_code_id',"False"),
-            invoice_line.get('product_id',"False"),
-            invoice_line.get('analytic_account_id',"False"),
-            invoice_line.get('date_maturity',"False"))
+            invoice_line.get('tax_code_id', 'False'),
+            invoice_line.get('product_id', 'False'),
+            invoice_line.get('analytic_account_id', 'False'),
+            invoice_line.get('date_maturity', 'False'),
+        )
 
-    def group_lines(self, cr, uid, iml, line, inv):
+    def group_lines(self, iml, line):
         """Merge account move lines (and hence analytic lines) if invoice line hashcodes are equals"""
-        if inv.journal_id.group_invoice_lines:
+        if self.journal_id.group_invoice_lines:
             line2 = {}
             for x, y, l in line:
-                tmp = self.inv_line_characteristic_hashcode(inv, l)
-
+                tmp = self.inv_line_characteristic_hashcode(l)
                 if tmp in line2:
                     am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
                     line2[tmp]['debit'] = (am > 0) and am or 0.0
@@ -928,41 +786,37 @@ class account_invoice(osv.osv):
                 line.append((0,0,val))
         return line
 
-    def action_move_create(self, cr, uid, ids, context=None):
-        """Creates invoice related analytics and financial move lines"""
-        ait_obj = self.pool.get('account.invoice.tax')
-        cur_obj = self.pool.get('res.currency')
-        period_obj = self.pool.get('account.period')
-        payment_term_obj = self.pool.get('account.payment.term')
-        journal_obj = self.pool.get('account.journal')
-        move_obj = self.pool.get('account.move')
-        if context is None:
-            context = {}
-        for inv in self.browse(cr, uid, ids, context=context):
+    @api.multi
+    def action_move_create(self):
+        """ Creates invoice related analytics and financial move lines """
+        account_invoice_tax = self.env['account.invoice.tax']
+        account_move = self.env['account.move']
+
+        for inv in self:
             if not inv.journal_id.sequence_id:
-                raise osv.except_osv(_('Error!'), _('Please define sequence on the journal related to this invoice.'))
+                raise except_orm(_('Error!'), _('Please define sequence on the journal related to this invoice.'))
             if not inv.invoice_line:
-                raise osv.except_osv(_('No Invoice Lines!'), _('Please create some invoice lines.'))
+                raise except_orm(_('No Invoice Lines!'), _('Please create some invoice lines.'))
             if inv.move_id:
                 continue
 
-            ctx = context.copy()
-            ctx.update({'lang': inv.partner_id.lang})
+            ctx = dict(self._context, lang=inv.partner_id.lang)
+
             if not inv.date_invoice:
-                self.write(cr, uid, [inv.id], {'date_invoice': fields.date.context_today(self, cr, uid, context=context)}, context=ctx)
-                inv.refresh()
-            company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id
-            # create the analytical lines
-            # one move line per invoice line
-            iml = self._get_analytic_lines(cr, uid, inv.id, context=ctx)
+                inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)})
+            date_invoice = inv.date_invoice
+
+            company_currency = inv.company_id.currency_id
+            # create the analytical lines, one move line per invoice line
+            iml = inv._get_analytic_lines()
             # check if taxes are all computed
-            compute_taxes = ait_obj.compute(cr, uid, inv.id, context=ctx)
-            self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
+            compute_taxes = account_invoice_tax.compute(inv)
+            inv.check_tax_lines(compute_taxes)
 
             # I disabled the check_total feature
-            if self.pool['res.users'].has_group(cr, uid, 'account.group_supplier_inv_check_total'):
-                if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)):
-                    raise osv.except_osv(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.'))
+            if self.env['res.users'].has_group('account.group_supplier_inv_check_total'):
+                if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding / 2.0):
+                    raise except_orm(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.'))
 
             if inv.payment_term:
                 total_fixed = total_percent = 0
@@ -973,61 +827,46 @@ class account_invoice(osv.osv):
                         total_percent += line.value_amount
                 total_fixed = (total_fixed * 100) / (inv.amount_total or 1.0)
                 if (total_fixed + total_percent) > 100:
-                    raise osv.except_osv(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'."))
+                    raise except_orm(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'."))
 
             # one move line per tax line
-            iml += ait_obj.move_line_get(cr, uid, inv.id)
+            iml += account_invoice_tax.move_line_get(inv.id)
 
-            entry_type = ''
             if inv.type in ('in_invoice', 'in_refund'):
                 ref = inv.reference
-                entry_type = 'journal_pur_voucher'
-                if inv.type == 'in_refund':
-                    entry_type = 'cont_voucher'
             else:
-                ref = self._convert_ref(cr, uid, inv.number)
-                entry_type = 'journal_sale_vou'
-                if inv.type == 'out_refund':
-                    entry_type = 'cont_voucher'
+                ref = inv.number
 
-            diff_currency_p = inv.currency_id.id <> company_currency
+            diff_currency = inv.currency_id != company_currency
             # create one move line for the total and possibly adjust the other lines amount
-            total = 0
-            total_currency = 0
-            total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx)
-            acc_id = inv.account_id.id
+            total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, ref, iml)
 
-            name = inv['name'] or inv['supplier_invoice_number'] or '/'
-            totlines = False
+            name = inv.name or inv.supplier_invoice_number or '/'
+            totlines = []
             if inv.payment_term:
-                totlines = payment_term_obj.compute(cr,
-                        uid, inv.payment_term.id, total, inv.date_invoice or False, context=ctx)
+                totlines = inv.with_context(ctx).payment_term.compute(total, date_invoice)[0]
             if totlines:
                 res_amount_currency = total_currency
-                i = 0
-                ctx.update({'date': inv.date_invoice})
-                for t in totlines:
-                    if inv.currency_id.id != company_currency:
-                        amount_currency = cur_obj.compute(cr, uid, company_currency, inv.currency_id.id, t[1], context=ctx)
+                ctx['date'] = date_invoice
+                for i, t in enumerate(totlines):
+                    if inv.currency_id != company_currency:
+                        amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id)
                     else:
                         amount_currency = False
 
-                    # last line add the diff
+                    # last line: add the diff
                     res_amount_currency -= amount_currency or 0
-                    i += 1
-                    if i == len(totlines):
+                    if i + 1 == len(totlines):
                         amount_currency += res_amount_currency
 
                     iml.append({
                         'type': 'dest',
                         'name': name,
                         'price': t[1],
-                        'account_id': acc_id,
+                        'account_id': inv.account_id.id,
                         'date_maturity': t[0],
-                        'amount_currency': diff_currency_p \
-                                and amount_currency or False,
-                        'currency_id': diff_currency_p \
-                                and inv.currency_id.id or False,
+                        'amount_currency': diff_currency and amount_currency,
+                        'currency_id': diff_currency and inv.currency_id.id,
                         'ref': ref,
                     })
             else:
@@ -1035,583 +874,543 @@ class account_invoice(osv.osv):
                     'type': 'dest',
                     'name': name,
                     'price': total,
-                    'account_id': acc_id,
-                    'date_maturity': inv.date_due or False,
-                    'amount_currency': diff_currency_p \
-                            and total_currency or False,
-                    'currency_id': diff_currency_p \
-                            and inv.currency_id.id or False,
+                    'account_id': inv.account_id.id,
+                    'date_maturity': inv.date_due,
+                    'amount_currency': diff_currency and total_currency,
+                    'currency_id': diff_currency and inv.currency_id.id,
                     'ref': ref
-            })
+                })
 
-            part = self.pool.get("res.partner")._find_accounting_partner(inv.partner_id)
+            date = date_invoice
 
-            line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part.id, inv.date_invoice, context=ctx)),iml)
+            part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
 
-            line = self.group_lines(cr, uid, iml, line, inv)
+            line = [(0, 0, self.line_get_convert(l, part.id, date)) for l in iml]
+            line = inv.group_lines(iml, line)
 
-            journal_id = inv.journal_id.id
-            journal = journal_obj.browse(cr, uid, journal_id, context=ctx)
+            journal = inv.journal_id.with_context(ctx)
             if journal.centralisation:
-                raise osv.except_osv(_('User Error!'),
+                raise except_orm(_('User Error!'),
                         _('You cannot create an invoice on a centralized journal. Uncheck the centralized counterpart box in the related journal from the configuration menu.'))
 
-            line = self.finalize_invoice_move_lines(cr, uid, inv, line)
+            line = inv.finalize_invoice_move_lines(line)
 
-            move = {
-                'ref': inv.reference and inv.reference or inv.name,
+            move_vals = {
+                'ref': inv.reference or inv.name,
                 'line_id': line,
-                'journal_id': journal_id,
+                'journal_id': journal.id,
                 'date': inv.date_invoice,
                 'narration': inv.comment,
                 'company_id': inv.company_id.id,
             }
-            period_id = inv.period_id and inv.period_id.id or False
-            ctx.update(company_id=inv.company_id.id)
-            if not period_id:
-                period_ids = period_obj.find(cr, uid, inv.date_invoice, context=ctx)
-                period_id = period_ids and period_ids[0] or False
-            if period_id:
-                move['period_id'] = period_id
+            ctx['company_id'] = inv.company_id.id
+            period = inv.period_id
+            if not period:
+                period = period.with_context(ctx).find(date_invoice)[:1]
+            if period:
+                move_vals['period_id'] = period.id
                 for i in line:
-                    i[2]['period_id'] = period_id
+                    i[2]['period_id'] = period.id
 
-            ctx.update(invoice=inv)
-            move_id = move_obj.create(cr, uid, move, context=ctx)
-            new_move_name = move_obj.browse(cr, uid, move_id, context=ctx).name
+            ctx['invoice'] = inv
+            move = account_move.with_context(ctx).create(move_vals)
             # make the invoice point to that move
-            self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name}, context=ctx)
+            vals = {
+                'move_id': move.id,
+                'period_id': period.id,
+                'move_name': move.name,
+            }
+            inv.with_context(ctx).write(vals)
             # Pass invoice in context in method post: used if you want to get the same
             # account move reference when creating the same invoice after a cancelled one:
-            move_obj.post(cr, uid, [move_id], context=ctx)
-        self._log_event(cr, uid, ids)
+            move.post()
+        self._log_event()
         return True
 
-    def invoice_validate(self, cr, uid, ids, context=None):
-        self.write(cr, uid, ids, {'state':'open'}, context=context)
-        return True
+    @api.multi
+    def invoice_validate(self):
+        return self.write({'state': 'open'})
 
-    def line_get_convert(self, cr, uid, x, part, date, context=None):
+    @api.model
+    def line_get_convert(self, line, part, date):
         return {
-            'date_maturity': x.get('date_maturity', False),
+            'date_maturity': line.get('date_maturity', False),
             'partner_id': part,
-            'name': x['name'][:64],
+            'name': line['name'][:64],
             'date': date,
-            'debit': x['price']>0 and x['price'],
-            'credit': x['price']<0 and -x['price'],
-            'account_id': x['account_id'],
-            'analytic_lines': x.get('analytic_lines', []),
-            'amount_currency': x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
-            'currency_id': x.get('currency_id', False),
-            'tax_code_id': x.get('tax_code_id', False),
-            'tax_amount': x.get('tax_amount', False),
-            'ref': x.get('ref', False),
-            'quantity': x.get('quantity',1.00),
-            'product_id': x.get('product_id', False),
-            'product_uom_id': x.get('uos_id', False),
-            'analytic_account_id': x.get('account_analytic_id', False),
+            'debit': line['price']>0 and line['price'],
+            'credit': line['price']<0 and -line['price'],
+            'account_id': line['account_id'],
+            'analytic_lines': line.get('analytic_lines', []),
+            'amount_currency': line['price']>0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)),
+            'currency_id': line.get('currency_id', False),
+            'tax_code_id': line.get('tax_code_id', False),
+            'tax_amount': line.get('tax_amount', False),
+            'ref': line.get('ref', False),
+            'quantity': line.get('quantity',1.00),
+            'product_id': line.get('product_id', False),
+            'product_uom_id': line.get('uos_id', False),
+            'analytic_account_id': line.get('account_analytic_id', False),
         }
 
-    def action_number(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        #TODO: not correct fix but required a frech values before reading it.
-        self.write(cr, uid, ids, {})
-
-        for obj_inv in self.browse(cr, uid, ids, context=context):
-            invtype = obj_inv.type
-            number = obj_inv.number
-            move_id = obj_inv.move_id and obj_inv.move_id.id or False
-            reference = obj_inv.reference or ''
+    @api.multi
+    def action_number(self):
+        #TODO: not correct fix but required a fresh values before reading it.
+        self.write({})
 
-            self.write(cr, uid, ids, {'internal_number': number})
+        for inv in self:
+            self.write({'internal_number': inv.number})
 
-            if invtype in ('in_invoice', 'in_refund'):
-                if not reference:
-                    ref = self._convert_ref(cr, uid, number)
+            if inv.type in ('in_invoice', 'in_refund'):
+                if not inv.reference:
+                    ref = inv.number
                 else:
-                    ref = reference
+                    ref = inv.reference
             else:
-                ref = self._convert_ref(cr, uid, number)
-
-            cr.execute('UPDATE account_move SET ref=%s ' \
-                    'WHERE id=%s AND (ref is null OR ref = \'\')',
-                    (ref, move_id))
-            cr.execute('UPDATE account_move_line SET ref=%s ' \
-                    'WHERE move_id=%s AND (ref is null OR ref = \'\')',
-                    (ref, move_id))
-            cr.execute('UPDATE account_analytic_line SET ref=%s ' \
-                    'FROM account_move_line ' \
-                    'WHERE account_move_line.move_id = %s ' \
-                        'AND account_analytic_line.move_id = account_move_line.id',
-                        (ref, move_id))
+                ref = inv.number
+
+            self._cr.execute(""" UPDATE account_move SET ref=%s
+                           WHERE id=%s AND (ref IS NULL OR ref = '')""",
+                        (ref, inv.move_id.id))
+            self._cr.execute(""" UPDATE account_move_line SET ref=%s
+                           WHERE move_id=%s AND (ref IS NULL OR ref = '')""",
+                        (ref, inv.move_id.id))
+            self._cr.execute(""" UPDATE account_analytic_line SET ref=%s
+                           FROM account_move_line
+                           WHERE account_move_line.move_id = %s AND
+                                 account_analytic_line.move_id = account_move_line.id""",
+                        (ref, inv.move_id.id))
+            self.invalidate_cache()
+
         return True
 
-    def action_cancel(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        account_move_obj = self.pool.get('account.move')
-        invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
-        move_ids = [] # ones that we will need to remove
-        for i in invoices:
-            if i['move_id']:
-                move_ids.append(i['move_id'][0])
-            if i['payment_ids']:
-                account_move_line_obj = self.pool.get('account.move.line')
-                pay_ids = account_move_line_obj.browse(cr, uid, i['payment_ids'])
-                for move_line in pay_ids:
-                    if move_line.reconcile_partial_id and move_line.reconcile_partial_id.line_partial_ids:
-                        raise osv.except_osv(_('Error!'), _('You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first.'))
+    @api.multi
+    def action_cancel(self):
+        moves = self.env['account.move']
+        for inv in self:
+            if inv.move_id:
+                moves += inv.move_id
+            if inv.payment_ids:
+                for move_line in inv.payment_ids:
+                    if move_line.reconcile_partial_id.line_partial_ids:
+                        raise except_orm(_('Error!'), _('You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first.'))
 
         # First, set the invoices as cancelled and detach the move ids
-        self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
-        if move_ids:
+        self.write({'state': 'cancel', 'move_id': False})
+        if moves:
             # second, invalidate the move(s)
-            account_move_obj.button_cancel(cr, uid, move_ids, context=context)
+            moves.button_cancel()
             # delete the move this invoice was pointing to
             # Note that the corresponding move_lines and move_reconciles
             # will be automatically deleted too
-            account_move_obj.unlink(cr, uid, move_ids, context=context)
-        self._log_event(cr, uid, ids, -1.0, 'Cancel Invoice')
+            moves.unlink()
+        self._log_event(-1.0, 'Cancel Invoice')
         return True
 
     ###################
 
-    def list_distinct_taxes(self, cr, uid, ids):
-        invoices = self.browse(cr, uid, ids)
-        taxes = {}
-        for inv in invoices:
-            for tax in inv.tax_line:
-                if not tax['name'] in taxes:
-                    taxes[tax['name']] = {'name': tax['name']}
-        return taxes.values()
-
-    def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'):
+    @api.multi
+    def _log_event(self, factor=1.0, name='Open Invoice'):
         #TODO: implement messages system
         return True
 
-    def name_get(self, cr, uid, ids, context=None):
-        if not ids:
-            return []
-        types = {
-                'out_invoice': _('Invoice'),
-                'in_invoice': _('Supplier Invoice'),
-                'out_refund': _('Refund'),
-                'in_refund': _('Supplier Refund'),
-                }
-        return [(r['id'], '%s %s' % (r['number'] or types[r['type']], r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
-
-    def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
-        if not args:
-            args = []
-        if context is None:
-            context = {}
-        ids = []
+    @api.multi
+    def name_get(self):
+        TYPES = {
+            'out_invoice': _('Invoice'),
+            'in_invoice': _('Supplier Invoice'),
+            'out_refund': _('Refund'),
+            'in_refund': _('Supplier Refund'),
+        }
+        result = []
+        for inv in self:
+            result.append((inv.id, "%s %s" % (inv.number or TYPES[inv.type], inv.name or '')))
+        return result
+
+    @api.model
+    def name_search(self, name, args=None, operator='ilike', limit=100):
+        args = args or []
+        recs = self.browse()
         if name:
-            ids = self.search(cr, user, [('number','=',name)] + args, limit=limit, context=context)
-        if not ids:
-            ids = self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context)
-        return self.name_get(cr, user, ids, context)
+            recs = self.search([('number', '=', name)] + args, limit=limit)
+        if not recs:
+            recs = self.search([('name', operator, name)] + args, limit=limit)
+        return recs.name_get()
 
-    def _refund_cleanup_lines(self, cr, uid, lines, context=None):
-        """Convert records to dict of values suitable for one2many line creation
+    @api.model
+    def _refund_cleanup_lines(self, lines):
+        """ Convert records to dict of values suitable for one2many line creation
 
-            :param list(browse_record) lines: records to convert
+            :param recordset lines: records to convert
             :return: list of command tuple for one2many line creation [(0, 0, dict of valueis), ...]
         """
-        clean_lines = []
+        result = []
         for line in lines:
-            clean_line = {}
-            for field in line._all_columns.keys():
-                if line._all_columns[field].column._type == 'many2one':
-                    clean_line[field] = line[field].id
-                elif line._all_columns[field].column._type not in ['many2many','one2many']:
-                    clean_line[field] = line[field]
-                elif field == 'invoice_line_tax_id':
-                    tax_list = []
-                    for tax in line[field]:
-                        tax_list.append(tax.id)
-                    clean_line[field] = [(6,0, tax_list)]
-            clean_lines.append(clean_line)
-        return map(lambda x: (0,0,x), clean_lines)
-
-    def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None, description=None, journal_id=None, context=None):
-        """Prepare the dict of values to create the new refund from the invoice.
+            values = {}
+            for name, field in line._fields.iteritems():
+                if name in MAGIC_COLUMNS:
+                    continue
+                elif field.type == 'many2one':
+                    values[name] = line[name].id
+                elif field.type not in ['many2many', 'one2many']:
+                    values[name] = line[name]
+                elif name == 'invoice_line_tax_id':
+                    values[name] = [(6, 0, line[name].ids)]
+            result.append((0, 0, values))
+        return result
+
+    @api.model
+    def _prepare_refund(self, invoice, date=None, period_id=None, description=None, journal_id=None):
+        """ Prepare the dict of values to create the new refund from the invoice.
             This method may be overridden to implement custom
             refund generation (making sure to call super() to establish
             a clean extension chain).
 
-            :param integer invoice_id: id of the invoice to refund
-            :param dict invoice: read of the invoice to refund
+            :param record invoice: invoice to refund
             :param string date: refund creation date from the wizard
             :param integer period_id: force account.period from the wizard
             :param string description: description of the refund from the wizard
             :param integer journal_id: account.journal from the wizard
             :return: dict of value to create() the refund
         """
-        obj_journal = self.pool.get('account.journal')
-
-        type_dict = {
-            'out_invoice': 'out_refund', # Customer Invoice
-            'in_invoice': 'in_refund',   # Supplier Invoice
-            'out_refund': 'out_invoice', # Customer Refund
-            'in_refund': 'in_invoice',   # Supplier Refund
-        }
-        invoice_data = {}
+        values = {}
         for field in ['name', 'reference', 'comment', 'date_due', 'partner_id', 'company_id',
                 'account_id', 'currency_id', 'payment_term', 'user_id', 'fiscal_position']:
-            if invoice._all_columns[field].column._type == 'many2one':
-                invoice_data[field] = invoice[field].id
+            if invoice._fields[field].type == 'many2one':
+                values[field] = invoice[field].id
             else:
-                invoice_data[field] = invoice[field] if invoice[field] else False
+                values[field] = invoice[field] or False
+
+        values['invoice_line'] = self._refund_cleanup_lines(invoice.invoice_line)
 
-        invoice_lines = self._refund_cleanup_lines(cr, uid, invoice.invoice_line, context=context)
+        tax_lines = filter(lambda l: l.manual, invoice.tax_line)
+        values['tax_line'] = self._refund_cleanup_lines(tax_lines)
 
-        tax_lines = filter(lambda l: l['manual'], invoice.tax_line)
-        tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines, context=context)
         if journal_id:
-            refund_journal_ids = [journal_id]
+            journal = self.env['account.journal'].browse(journal_id)
         elif invoice['type'] == 'in_invoice':
-            refund_journal_ids = obj_journal.search(cr, uid, [('type','=','purchase_refund')], context=context)
+            journal = self.env['account.journal'].search([('type', '=', 'purchase_refund')], limit=1)
         else:
-            refund_journal_ids = obj_journal.search(cr, uid, [('type','=','sale_refund')], context=context)
-
-        if not date:
-            date = fields.date.context_today(self, cr, uid, context=context)
-        invoice_data.update({
-            'type': type_dict[invoice['type']],
-            'date_invoice': date,
-            'state': 'draft',
-            'number': False,
-            'invoice_line': invoice_lines,
-            'tax_line': tax_lines,
-            'journal_id': refund_journal_ids and refund_journal_ids[0] or False,
-        })
+            journal = self.env['account.journal'].search([('type', '=', 'sale_refund')], limit=1)
+        values['journal_id'] = journal.id
+
+        values['type'] = TYPE2REFUND[invoice['type']]
+        values['date_invoice'] = date or fields.Date.context_today(invoice)
+        values['state'] = 'draft'
+        values['number'] = False
+
         if period_id:
-            invoice_data['period_id'] = period_id
+            values['period_id'] = period_id
         if description:
-            invoice_data['name'] = description
-        return invoice_data
-
-    def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None, context=None):
-        new_ids = []
-        for invoice in self.browse(cr, uid, ids, context=context):
-            invoice = self._prepare_refund(cr, uid, invoice,
-                                                date=date,
-                                                period_id=period_id,
-                                                description=description,
-                                                journal_id=journal_id,
-                                                context=context)
+            values['name'] = description
+        return values
+
+    @api.multi
+    @api.returns('self')
+    def refund(self, date=None, period_id=None, description=None, journal_id=None):
+        new_invoices = self.browse()
+        for invoice in self:
             # create the new invoice
-            new_ids.append(self.create(cr, uid, invoice, context=context))
-
-        return new_ids
-
-    def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
-        if context is None:
-            context = {}
-        #TODO check if we can use different period for payment and the writeoff line
-        assert len(ids)==1, "Can only pay one invoice at a time."
-        invoice = self.browse(cr, uid, ids[0], context=context)
-        src_account_id = invoice.account_id.id
+            values = self._prepare_refund(invoice, date=date, period_id=period_id,
+                                    description=description, journal_id=journal_id)
+            new_invoices += self.create(values)
+        return new_invoices
+
+    @api.v8
+    def pay_and_reconcile(self, pay_amount, pay_account_id, period_id, pay_journal_id,
+                          writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=''):
+        # TODO check if we can use different period for payment and the writeoff line
+        assert len(self)==1, "Can only pay one invoice at a time."
         # Take the seq as name for move
-        types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
-        direction = types[invoice.type]
-        #take the choosen date
-        if 'date_p' in context and context['date_p']:
-            date=context['date_p']
-        else:
-            date=time.strftime('%Y-%m-%d')
+        SIGN = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
+        direction = SIGN[self.type]
+        # take the chosen date
+        date = self._context.get('date_p') or fields.Date.context_today(self)
 
         # Take the amount in currency and the currency of the payment
-        if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
-            amount_currency = context['amount_currency']
-            currency_id = context['currency_id']
+        if self._context.get('amount_currency') and self._context.get('currency_id'):
+            amount_currency = self._context['amount_currency']
+            currency_id = self._context['currency_id']
         else:
             amount_currency = False
             currency_id = False
 
-        pay_journal = self.pool.get('account.journal').read(cr, uid, pay_journal_id, ['type'], context=context)
-        if invoice.type in ('in_invoice', 'out_invoice'):
-            if pay_journal['type'] == 'bank':
-                entry_type = 'bank_pay_voucher' # Bank payment
-            else:
-                entry_type = 'pay_voucher' # Cash payment
+        pay_journal = self.env['account.journal'].browse(pay_journal_id)
+        if self.type in ('in_invoice', 'in_refund'):
+            ref = self.reference
         else:
-            entry_type = 'cont_voucher'
-        if invoice.type in ('in_invoice', 'in_refund'):
-            ref = invoice.reference
-        else:
-            ref = self._convert_ref(cr, uid, invoice.number)
-        partner = self.pool['res.partner']._find_accounting_partner(invoice.partner_id)
+            ref = self.number
+        partner = self.partner_id._find_accounting_partner(self.partner_id)
+        name = name or self.invoice_line.name or self.number
         # Pay attention to the sign for both debit/credit AND amount_currency
         l1 = {
-            'debit': direction * pay_amount>0 and direction * pay_amount,
-            'credit': direction * pay_amount<0 and - direction * pay_amount,
-            'account_id': src_account_id,
+            'name': name,
+            'debit': direction * pay_amount > 0 and direction * pay_amount,
+            'credit': direction * pay_amount < 0 and -direction * pay_amount,
+            'account_id': self.account_id.id,
             'partner_id': partner.id,
-            'ref':ref,
+            'ref': ref,
             'date': date,
-            'currency_id':currency_id,
-            'amount_currency':amount_currency and direction * amount_currency or 0.0,
-            'company_id': invoice.company_id.id,
+            'currency_id': currency_id,
+            'amount_currency': direction * (amount_currency or 0.0),
+            'company_id': self.company_id.id,
         }
         l2 = {
-            'debit': direction * pay_amount<0 and - direction * pay_amount,
-            'credit': direction * pay_amount>0 and direction * pay_amount,
+            'name': name,
+            'debit': direction * pay_amount < 0 and -direction * pay_amount,
+            'credit': direction * pay_amount > 0 and direction * pay_amount,
             'account_id': pay_account_id,
             'partner_id': partner.id,
-            'ref':ref,
+            'ref': ref,
             'date': date,
-            'currency_id':currency_id,
-            'amount_currency':amount_currency and - direction * amount_currency or 0.0,
-            'company_id': invoice.company_id.id,
+            'currency_id': currency_id,
+            'amount_currency': -direction * (amount_currency or 0.0),
+            'company_id': self.company_id.id,
         }
+        move = self.env['account.move'].create({
+            'ref': ref,
+            'line_id': [(0, 0, l1), (0, 0, l2)],
+            'journal_id': pay_journal_id,
+            'period_id': period_id,
+            'date': date,
+        })
 
-        if not name:
-            name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
-        l1['name'] = name
-        l2['name'] = name
-
-        lines = [(0, 0, l1), (0, 0, l2)]
-        move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
-        move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
-
-        line_ids = []
+        move_ids = (move | self.move_id).ids
+        self._cr.execute("SELECT id FROM account_move_line WHERE move_id IN %s",
+                         (tuple(move_ids),))
+        lines = self.env['account.move.line'].browse([r[0] for r in self._cr.fetchall()])
+        lines2rec = lines.browse()
         total = 0.0
-        line = self.pool.get('account.move.line')
-        move_ids = [move_id,]
-        if invoice.move_id:
-            move_ids.append(invoice.move_id.id)
-        cr.execute('SELECT id FROM account_move_line '\
-                   'WHERE move_id IN %s',
-                   ((move_id, invoice.move_id.id),))
-        lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
-        for l in lines+invoice.payment_ids:
-            if l.account_id.id == src_account_id:
-                line_ids.append(l.id)
-                total += (l.debit or 0.0) - (l.credit or 0.0)
-
-        inv_id, name = self.name_get(cr, uid, [invoice.id], context=context)[0]
-        if (not round(total,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))) or writeoff_acc_id:
-            self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
+        for line in itertools.chain(lines, self.payment_ids):
+            if line.account_id == self.account_id:
+                lines2rec += line
+                total += (line.debit or 0.0) - (line.credit or 0.0)
+
+        inv_id, name = self.name_get()[0]
+        if not round(total, self.env['decimal.precision'].precision_get('Account')) or writeoff_acc_id:
+            lines2rec.reconcile('manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id)
         else:
-            code = invoice.currency_id.symbol
+            code = self.currency_id.symbol
             # TODO: use currency's formatting function
             msg = _("Invoice partially paid: %s%s of %s%s (%s%s remaining).") % \
-                    (pay_amount, code, invoice.amount_total, code, total, code)
-            self.message_post(cr, uid, [inv_id], body=msg, context=context)
-            self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
+                    (pay_amount, code, self.amount_total, code, total, code)
+            self.message_post(body=msg)
+            lines2rec.reconcile_partial('manual')
 
         # Update the stored value (fields.function), so we write to trigger recompute
-        self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
-        return True
-
-
-class account_invoice_line(osv.osv):
+        return self.write({})
 
-    def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict):
-        res = {}
-        tax_obj = self.pool.get('account.tax')
-        cur_obj = self.pool.get('res.currency')
-        for line in self.browse(cr, uid, ids):
-            price = line.price_unit * (1-(line.discount or 0.0)/100.0)
-            taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id)
-            res[line.id] = taxes['total']
-            if line.invoice_id:
-                cur = line.invoice_id.currency_id
-                res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
-        return res
-
-    def _price_unit_default(self, cr, uid, context=None):
-        if context is None:
-            context = {}
-        if context.get('check_total', False):
-            t = context['check_total']
-            for l in context.get('invoice_line', {}):
-                if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
-                    tax_obj = self.pool.get('account.tax')
-                    p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
-                    t = t - (p * l[2].get('quantity'))
-                    taxes = l[2].get('invoice_line_tax_id')
-                    if len(taxes[0]) >= 3 and taxes[0][2]:
-                        taxes = tax_obj.browse(cr, uid, list(taxes[0][2]))
-                        for tax in tax_obj.compute_all(cr, uid, taxes, p,l[2].get('quantity'), l[2].get('product_id', False), context.get('partner_id', False))['taxes']:
-                            t = t - tax['amount']
-            return t
-        return 0
+    @api.v7
+    def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id,
+                          writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
+        recs = self.browse(cr, uid, ids, context)
+        return recs.pay_and_reconcile(pay_amount, pay_account_id, period_id, pay_journal_id,
+                    writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=name)
 
+class account_invoice_line(models.Model):
     _name = "account.invoice.line"
     _description = "Invoice Line"
     _order = "invoice_id,sequence,id"
-    _columns = {
-        'name': fields.text('Description', required=True),
-        'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."),
-        'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."),
-        'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True),
-        'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True),
-        'product_id': fields.many2one('product.product', 'Product', ondelete='set null', select=True),
-        'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
-        'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
-        'price_subtotal': fields.function(_amount_line, string='Amount', type="float",
-            digits_compute= dp.get_precision('Account'), store=True),
-        'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True),
-        'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
-        'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
-        'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
-        'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
-        'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True)
-    }
 
-    def _default_account_id(self, cr, uid, context=None):
+    @api.one
+    @api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
+        'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id')
+    def _compute_price(self):
+        price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
+        taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
+        self.price_subtotal = taxes['total']
+        if self.invoice_id:
+            self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal)
+
+    @api.model
+    def _default_price_unit(self):
+        if not self._context.get('check_total'):
+            return 0
+        total = self._context['check_total']
+        for l in self._context.get('invoice_line', []):
+            if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
+                vals = l[2]
+                price = vals.get('price_unit', 0) * (1 - vals.get('discount', 0) / 100.0)
+                total = total - (price * vals.get('quantity'))
+                taxes = vals.get('invoice_line_tax_id')
+                if taxes and len(taxes[0]) >= 3 and taxes[0][2]:
+                    taxes = self.env['account.tax'].browse(taxes[0][2])
+                    tax_res = taxes.compute_all(price, vals.get('quantity'),
+                        product=vals.get('product_id'), partner=self._context.get('partner_id'))
+                    for tax in tax_res['taxes']:
+                        total = total - tax['amount']
+        return total
+
+    @api.model
+    def _default_account(self):
         # XXX this gets the default account for the user's company,
         # it should get the default account for the invoice's company
         # however, the invoice's company does not reach this point
-        if context is None:
-            context = {}
-        if context.get('type') in ('out_invoice','out_refund'):
-            prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context)
+        if self._context.get('type') in ('out_invoice', 'out_refund'):
+            return self.env['ir.property'].get('property_account_income_categ', 'product.category')
         else:
-            prop = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category', context=context)
-        return prop and prop.id or False
-
-    _defaults = {
-        'quantity': 1,
-        'discount': 0.0,
-        'price_unit': _price_unit_default,
-        'account_id': _default_account_id,
-        'sequence': 10,
-    }
-
-    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
-        if context is None:
-            context = {}
-        res = super(account_invoice_line,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
-        if context.get('type', False):
+            return self.env['ir.property'].get('property_account_expense_categ', 'product.category')
+
+    name = fields.Text(string='Description', required=True)
+    origin = fields.Char(string='Source Document',
+        help="Reference of the document that produced this invoice.")
+    sequence = fields.Integer(string='Sequence', default=10,
+        help="Gives the sequence of this line when displaying the invoice.")
+    invoice_id = fields.Many2one('account.invoice', string='Invoice Reference',
+        ondelete='cascade', index=True)
+    uos_id = fields.Many2one('product.uom', string='Unit of Measure',
+        ondelete='set null', index=True)
+    product_id = fields.Many2one('product.product', string='Product',
+        ondelete='set null', index=True)
+    account_id = fields.Many2one('account.account', string='Account',
+        required=True, domain=[('type', 'not in', ['view', 'closed'])],
+        default=_default_account,
+        help="The income or expense account related to the selected product.")
+    price_unit = fields.Float(string='Unit Price', required=True,
+        digits= dp.get_precision('Product Price'),
+        default=_default_price_unit)
+    price_subtotal = fields.Float(string='Amount', digits= dp.get_precision('Account'),
+        store=True, readonly=True, compute='_compute_price')
+    quantity = fields.Float(string='Quantity', digits= dp.get_precision('Product Unit of Measure'),
+        required=True, default=1)
+    discount = fields.Float(string='Discount (%)', digits= dp.get_precision('Discount'),
+        default=0.0)
+    invoice_line_tax_id = fields.Many2many('account.tax',
+        'account_invoice_line_tax', 'invoice_line_id', 'tax_id',
+        string='Taxes', domain=[('parent_id', '=', False)])
+    account_analytic_id = fields.Many2one('account.analytic.account',
+        string='Analytic Account')
+    company_id = fields.Many2one('res.company', string='Company',
+        related='invoice_id.company_id', store=True, readonly=True)
+    partner_id = fields.Many2one('res.partner', string='Partner',
+        related='invoice_id.partner_id', store=True, readonly=True)
+
+    @api.model
+    def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
+        res = super(account_invoice_line, self).fields_view_get(
+            view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
+        if self._context.get('type'):
             doc = etree.XML(res['arch'])
             for node in doc.xpath("//field[@name='product_id']"):
-                if context['type'] in ('in_invoice', 'in_refund'):
+                if self._context['type'] in ('in_invoice', 'in_refund'):
                     node.set('domain', "[('purchase_ok', '=', True)]")
                 else:
                     node.set('domain', "[('sale_ok', '=', True)]")
             res['arch'] = etree.tostring(doc)
         return res
 
-    def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
-        if context is None:
-            context = {}
-        company_id = company_id if company_id != None else context.get('company_id',False)
-        context = dict(context)
-        context.update({'company_id': company_id, 'force_company': company_id})
+    @api.multi
+    def product_id_change(self, product, uom_id, qty=0, name='', type='out_invoice',
+            partner_id=False, fposition_id=False, price_unit=False, currency_id=False,
+            company_id=None):
+        context = self._context
+        company_id = company_id if company_id is not None else context.get('company_id', False)
+        self = self.with_context(company_id=company_id, force_company=company_id)
+
         if not partner_id:
-            raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") )
+            raise except_orm(_('No Partner Defined!'), _("You must first select a partner!"))
         if not product:
             if type in ('in_invoice', 'in_refund'):
-                return {'value': {}, 'domain':{'product_uom':[]}}
+                return {'value': {}, 'domain': {'product_uom': []}}
             else:
-                return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
-        part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
-        fpos_obj = self.pool.get('account.fiscal.position')
-        fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False
+                return {'value': {'price_unit': 0.0}, 'domain': {'product_uom': []}}
+
+        values = {}
+
+        part = self.env['res.partner'].browse(partner_id)
+        fpos = self.env['account.fiscal.position'].browse(fposition_id)
 
         if part.lang:
-            context.update({'lang': part.lang})
-        result = {}
-        res = self.pool.get('product.product').browse(cr, uid, product, context=context)
-
-        result['name'] = res.partner_ref
-        if type in ('out_invoice','out_refund'):
-            a = res.property_account_income.id
-            if not a:
-                a = res.categ_id.property_account_income_categ.id
+            self = self.with_context(lang=part.lang)
+        product = self.env['product.product'].browse(product)
+
+        values['name'] = product.partner_ref
+        if type in ('out_invoice', 'out_refund'):
+            account = product.property_account_income or product.categ_id.property_account_income_categ
         else:
-            a = res.property_account_expense.id
-            if not a:
-                a = res.categ_id.property_account_expense_categ.id
-        a = fpos_obj.map_account(cr, uid, fpos, a)
-        if a:
-            result['account_id'] = a
+            account = product.property_account_expense or product.categ_id.property_account_expense_categ
+        account = fpos.map_account(account)
+        if account:
+            values['account_id'] = account.id
 
         if type in ('out_invoice', 'out_refund'):
-            taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False)
-            if res.description_sale:
-                result['name'] += '\n'+res.description_sale
+            taxes = product.taxes_id or account.tax_ids
+            if product.description_sale:
+                values['name'] += '\n' + product.description_sale
         else:
-            taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False)
-            if res.description_purchase:
-                result['name'] += '\n'+res.description_purchase
+            taxes = product.supplier_taxes_id or account.tax_ids
+            if product.description_purchase:
+                values['name'] += '\n' + product.description_purchase
 
-        tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
+        taxes = fpos.map_tax(taxes)
+        values['invoice_line_tax_id'] = taxes.ids
 
         if type in ('in_invoice', 'in_refund'):
-            result.update( {'price_unit': price_unit or res.standard_price,'invoice_line_tax_id': tax_id} )
+            values['price_unit'] = price_unit or product.standard_price
         else:
-            result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
+            values['price_unit'] = product.list_price
 
-        result['uos_id'] = uom_id or res.uom_id.id
+        values['uos_id'] = uom_id or product.uom_id.id
+        domain = {'uos_id': [('category_id', '=', product.uom_id.category_id.id)]}
 
-        domain = {'uos_id':[('category_id','=',res.uom_id.category_id.id)]}
+        company = self.env['res.company'].browse(company_id)
+        currency = self.env['res.currency'].browse(currency_id)
 
-        res_final = {'value':result, 'domain':domain}
+        if company and currency:
+            if company.currency_id != currency:
+                if type in ('in_invoice', 'in_refund'):
+                    values['price_unit'] = product.standard_price
+                values['price_unit'] = values['price_unit'] * currency.rate
 
-        if not company_id or not currency_id:
-            return res_final
+            if values['uos_id'] and values['uos_id'] != product.uom_id.id:
+                values['price_unit'] = self.env['product.uom']._compute_price(
+                    product.uom_id.id, values['price_unit'], values['uos_id'])
 
-        company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
-        currency = self.pool.get('res.currency').browse(cr, uid, currency_id, context=context)
+        return {'value': values, 'domain': domain}
 
-        if company.currency_id.id != currency.id:
-            if type in ('in_invoice', 'in_refund'):
-                res_final['value']['price_unit'] = res.standard_price
-            new_price = res_final['value']['price_unit'] * currency.rate
-            res_final['value']['price_unit'] = new_price
-
-        if result['uos_id'] and result['uos_id'] != res.uom_id.id:
-            selected_uom = self.pool.get('product.uom').browse(cr, uid, result['uos_id'], context=context)
-            new_price = self.pool.get('product.uom')._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uos_id'])
-            res_final['value']['price_unit'] = new_price
-        return res_final
-
-    def uos_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None):
-        if context is None:
-            context = {}
-        company_id = company_id if company_id != None else context.get('company_id',False)
-        context = dict(context)
-        context.update({'company_id': company_id})
+    @api.multi
+    def uos_id_change(self, product, uom, qty=0, name='', type='out_invoice', partner_id=False,
+            fposition_id=False, price_unit=False, currency_id=False, company_id=None):
+        context = self._context
+        company_id = company_id if company_id != None else context.get('company_id', False)
+        self = self.with_context(company_id=company_id)
+
+        result = self.product_id_change(
+            product, uom, qty, name, type, partner_id, fposition_id, price_unit,
+            currency_id, company_id=company_id,
+        )
         warning = {}
-        res = self.product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context=context)
         if not uom:
-            res['value']['price_unit'] = 0.0
+            result['value']['price_unit'] = 0.0
         if product and uom:
-            prod = self.pool.get('product.product').browse(cr, uid, product, context=context)
-            prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context)
-            if prod.uom_id.category_id.id != prod_uom.category_id.id:
+            prod = self.env['product.product'].browse(product)
+            prod_uom = self.env['product.uom'].browse(uom)
+            if prod.uom_id.category_id != prod_uom.category_id:
                 warning = {
                     'title': _('Warning!'),
-                    'message': _('The selected unit of measure is not compatible with the unit of measure of the product.')
+                    'message': _('The selected unit of measure is not compatible with the unit of measure of the product.'),
                 }
-                res['value'].update({'uos_id': prod.uom_id.id})
-            return {'value': res['value'], 'warning': warning}
-        return res
+                result['value']['uos_id'] = prod.uom_id.id
+        if warning:
+            result['warning'] = warning
+        return result
+
+    @api.model
+    def move_line_get(self, invoice_id):
+        inv = self.env['account.invoice'].browse(invoice_id)
+        currency = inv.currency_id.with_context(date=inv.date_invoice)
+        company_currency = inv.company_id.currency_id
 
-    def move_line_get(self, cr, uid, invoice_id, context=None):
         res = []
-        tax_obj = self.pool.get('account.tax')
-        cur_obj = self.pool.get('res.currency')
-        if context is None:
-            context = {}
-        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context)
-        company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id
         for line in inv.invoice_line:
-            mres = self.move_line_get_item(cr, uid, line, context)
+            mres = self.move_line_get_item(line)
             mres['invl_id'] = line.id
             res.append(mres)
-            tax_code_found= False
-            for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id,
-                    (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)),
-                    line.quantity, line.product_id,
-                    inv.partner_id)['taxes']:
-
+            tax_code_found = False
+            taxes = line.invoice_line_tax_id.compute_all(
+                (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0)),
+                line.quantity, line.product_id, inv.partner_id)['taxes']
+            for tax in taxes:
                 if inv.type in ('out_invoice', 'in_invoice'):
                     tax_code_id = tax['base_code_id']
                     tax_amount = line.price_subtotal * tax['base_sign']
@@ -1622,7 +1421,7 @@ class account_invoice_line(osv.osv):
                 if tax_code_found:
                     if not tax_code_id:
                         continue
-                    res.append(self.move_line_get_item(cr, uid, line, context))
+                    res.append(dict(mres))
                     res[-1]['price'] = 0.0
                     res[-1]['account_analytic_id'] = False
                 elif not tax_code_id:
@@ -1630,143 +1429,134 @@ class account_invoice_line(osv.osv):
                 tax_code_found = True
 
                 res[-1]['tax_code_id'] = tax_code_id
-                res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice})
+                res[-1]['tax_amount'] = currency.compute(tax_amount, company_currency)
+
         return res
 
-    def move_line_get_item(self, cr, uid, line, context=None):
+    @api.model
+    def move_line_get_item(self, line):
         return {
-            'type':'src',
+            'type': 'src',
             'name': line.name.split('\n')[0][:64],
-            'price_unit':line.price_unit,
-            'quantity':line.quantity,
-            'price':line.price_subtotal,
-            'account_id':line.account_id.id,
-            'product_id':line.product_id.id,
-            'uos_id':line.uos_id.id,
-            'account_analytic_id':line.account_analytic_id.id,
-            'taxes':line.invoice_line_tax_id,
+            'price_unit': line.price_unit,
+            'quantity': line.quantity,
+            'price': line.price_subtotal,
+            'account_id': line.account_id.id,
+            'product_id': line.product_id.id,
+            'uos_id': line.uos_id.id,
+            'account_analytic_id': line.account_analytic_id.id,
+            'taxes': line.invoice_line_tax_id,
         }
+
     #
     # Set the tax field according to the account and the fiscal position
     #
-    def onchange_account_id(self, cr, uid, ids, product_id, partner_id, inv_type, fposition_id, account_id):
+    @api.multi
+    def onchange_account_id(self, product_id, partner_id, inv_type, fposition_id, account_id):
         if not account_id:
             return {}
         unique_tax_ids = []
-        fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
-        account = self.pool.get('account.account').browse(cr, uid, account_id)
+        account = self.env['account.account'].browse(account_id)
         if not product_id:
-            taxes = account.tax_ids
-            unique_tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
+            fpos = self.env['account.fiscal.position'].browse(fposition_id)
+            unique_tax_ids = fpos.map_tax(account.tax_ids).ids
         else:
-            product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type,
-                partner_id=partner_id, fposition_id=fposition_id,
-                company_id=account.company_id.id)
-            if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']:
+            product_change_result = self.product_id_change(product_id, False, type=inv_type,
+                partner_id=partner_id, fposition_id=fposition_id, company_id=account.company_id.id)
+            if 'invoice_line_tax_id' in product_change_result.get('value', {}):
                 unique_tax_ids = product_change_result['value']['invoice_line_tax_id']
-        return {'value':{'invoice_line_tax_id': unique_tax_ids}}
+        return {'value': {'invoice_line_tax_id': unique_tax_ids}}
 
 
-class account_invoice_tax(osv.osv):
+class account_invoice_tax(models.Model):
     _name = "account.invoice.tax"
     _description = "Invoice Tax"
+    _order = 'sequence'
 
-    def _count_factor(self, cr, uid, ids, name, args, context=None):
-        res = {}
-        for invoice_tax in self.browse(cr, uid, ids, context=context):
-            res[invoice_tax.id] = {
-                'factor_base': 1.0,
-                'factor_tax': 1.0,
-            }
-            if invoice_tax.amount <> 0.0:
-                factor_tax = invoice_tax.tax_amount / invoice_tax.amount
-                res[invoice_tax.id]['factor_tax'] = factor_tax
-
-            if invoice_tax.base <> 0.0:
-                factor_base = invoice_tax.base_amount / invoice_tax.base
-                res[invoice_tax.id]['factor_base'] = factor_base
-
-        return res
-
-    _columns = {
-        'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
-        'name': fields.char('Tax Description', size=64, required=True),
-        'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
-        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
-        'base': fields.float('Base', digits_compute=dp.get_precision('Account')),
-        'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')),
-        'manual': fields.boolean('Manual'),
-        'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."),
-        'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
-        'base_amount': fields.float('Base Code Amount', digits_compute=dp.get_precision('Account')),
-        'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
-        'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Account')),
-        'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
-        'factor_base': fields.function(_count_factor, string='Multipication factor for Base code', type='float', multi="all"),
-        'factor_tax': fields.function(_count_factor, string='Multipication factor Tax code', type='float', multi="all")
-    }
-
-    def base_change(self, cr, uid, ids, base, currency_id=False, company_id=False, date_invoice=False):
-        cur_obj = self.pool.get('res.currency')
-        company_obj = self.pool.get('res.company')
-        company_currency = False
-        factor = 1
-        if ids:
-            factor = self.read(cr, uid, ids[0], ['factor_base'])['factor_base']
-        if company_id:
-            company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0]
-        if currency_id and company_currency:
-            base = cur_obj.compute(cr, uid, currency_id, company_currency, base*factor, context={'date': date_invoice or fields.date.context_today(self, cr, uid)}, round=False)
-        return {'value': {'base_amount':base}}
-
-    def amount_change(self, cr, uid, ids, amount, currency_id=False, company_id=False, date_invoice=False):
-        cur_obj = self.pool.get('res.currency')
-        company_obj = self.pool.get('res.company')
-        company_currency = False
-        factor = 1
-        if ids:
-            factor = self.read(cr, uid, ids[0], ['factor_tax'])['factor_tax']
-        if company_id:
-            company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0]
-        if currency_id and company_currency:
-            amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount*factor, context={'date': date_invoice or fields.date.context_today(self, cr, uid)}, round=False)
+    @api.one
+    @api.depends('base', 'base_amount', 'amount', 'tax_amount')
+    def _compute_factors(self):
+        self.factor_base = self.base_amount / self.base if self.base else 1.0
+        self.factor_tax = self.tax_amount / self.amount if self.amount else 1.0
+
+    invoice_id = fields.Many2one('account.invoice', string='Invoice Line',
+        ondelete='cascade', index=True)
+    name = fields.Char(string='Tax Description',
+        required=True)
+    account_id = fields.Many2one('account.account', string='Tax Account',
+        required=True, domain=[('type', 'not in', ['view', 'income', 'closed'])])
+    account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic account')
+    base = fields.Float(string='Base', digits=dp.get_precision('Account'))
+    amount = fields.Float(string='Amount', digits=dp.get_precision('Account'))
+    manual = fields.Boolean(string='Manual', default=True)
+    sequence = fields.Integer(string='Sequence',
+        help="Gives the sequence order when displaying a list of invoice tax.")
+    base_code_id = fields.Many2one('account.tax.code', string='Base Code',
+        help="The account basis of the tax declaration.")
+    base_amount = fields.Float(string='Base Code Amount', digits=dp.get_precision('Account'),
+        default=0.0)
+    tax_code_id = fields.Many2one('account.tax.code', string='Tax Code',
+        help="The tax basis of the tax declaration.")
+    tax_amount = fields.Float(string='Tax Code Amount', digits=dp.get_precision('Account'),
+        default=0.0)
+
+    company_id = fields.Many2one('res.company', string='Company',
+        related='account_id.company_id', store=True, readonly=True)
+    factor_base = fields.Float(string='Multipication factor for Base code',
+        compute='_compute_factors')
+    factor_tax = fields.Float(string='Multipication factor Tax code',
+        compute='_compute_factors')
+
+    @api.multi
+    def base_change(self, base, currency_id=False, company_id=False, date_invoice=False):
+        factor = self.factor_base if self else 1
+        company = self.env['res.company'].browse(company_id)
+        if currency_id and company.currency_id:
+            currency = self.env['res.currency'].browse(currency_id)
+            currency = currency.with_context(date=date_invoice or fields.Date.context_today(self))
+            base = currency.compute(base * factor, company.currency_id, round=False)
+        return {'value': {'base_amount': base}}
+
+    @api.multi
+    def amount_change(self, amount, currency_id=False, company_id=False, date_invoice=False):
+        factor = self.factor_tax if self else 1
+        company = self.env['res.company'].browse(company_id)
+        if currency_id and company.currency_id:
+            currency = self.env['res.currency'].browse(currency_id)
+            currency = currency.with_context(date=date_invoice or fields.Date.context_today(self))
+            amount = currency.compute(amount * factor, company.currency_id, round=False)
         return {'value': {'tax_amount': amount}}
 
-    _order = 'sequence'
-    _defaults = {
-        'manual': 1,
-        'base_amount': 0.0,
-        'tax_amount': 0.0,
-    }
-    def compute(self, cr, uid, invoice_id, context=None):
+    @api.v8
+    def compute(self, invoice):
         tax_grouped = {}
-        tax_obj = self.pool.get('account.tax')
-        cur_obj = self.pool.get('res.currency')
-        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context)
-        cur = inv.currency_id
-        company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id
-        for line in inv.invoice_line:
-            for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, line.product_id, inv.partner_id)['taxes']:
-                val={}
-                val['invoice_id'] = inv.id
-                val['name'] = tax['name']
-                val['amount'] = tax['amount']
-                val['manual'] = False
-                val['sequence'] = tax['sequence']
-                val['base'] = cur_obj.round(cr, uid, cur, tax['price_unit'] * line['quantity'])
-
-                if inv.type in ('out_invoice','in_invoice'):
+        currency = invoice.currency_id.with_context(date=invoice.date_invoice or fields.Date.context_today(invoice))
+        company_currency = invoice.company_id.currency_id
+        for line in invoice.invoice_line:
+            taxes = line.invoice_line_tax_id.compute_all(
+                (line.price_unit * (1 - (line.discount or 0.0) / 100.0)),
+                line.quantity, line.product_id, invoice.partner_id)['taxes']
+            for tax in taxes:
+                val = {
+                    'invoice_id': invoice.id,
+                    'name': tax['name'],
+                    'amount': tax['amount'],
+                    'manual': False,
+                    'sequence': tax['sequence'],
+                    'base': currency.round(tax['price_unit'] * line['quantity']),
+                }
+                if invoice.type in ('out_invoice','in_invoice'):
                     val['base_code_id'] = tax['base_code_id']
                     val['tax_code_id'] = tax['tax_code_id']
-                    val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False)
-                    val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False)
+                    val['base_amount'] = currency.compute(val['base'] * tax['base_sign'], company_currency, round=False)
+                    val['tax_amount'] = currency.compute(val['amount'] * tax['tax_sign'], company_currency, round=False)
                     val['account_id'] = tax['account_collected_id'] or line.account_id.id
                     val['account_analytic_id'] = tax['account_analytic_collected_id']
                 else:
                     val['base_code_id'] = tax['ref_base_code_id']
                     val['tax_code_id'] = tax['ref_tax_code_id']
-                    val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False)
-                    val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False)
+                    val['base_amount'] = currency.compute(val['base'] * tax['ref_base_sign'], company_currency, round=False)
+                    val['tax_amount'] = currency.compute(val['amount'] * tax['ref_tax_sign'], company_currency, round=False)
                     val['account_id'] = tax['account_paid_id'] or line.account_id.id
                     val['account_analytic_id'] = tax['account_analytic_paid_id']
 
@@ -1782,46 +1572,55 @@ class account_invoice_tax(osv.osv):
                 if not key in tax_grouped:
                     tax_grouped[key] = val
                 else:
-                    tax_grouped[key]['amount'] += val['amount']
                     tax_grouped[key]['base'] += val['base']
+                    tax_grouped[key]['amount'] += val['amount']
                     tax_grouped[key]['base_amount'] += val['base_amount']
                     tax_grouped[key]['tax_amount'] += val['tax_amount']
 
         for t in tax_grouped.values():
-            t['base'] = cur_obj.round(cr, uid, cur, t['base'])
-            t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
-            t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
-            t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
+            t['base'] = currency.round(t['base'])
+            t['amount'] = currency.round(t['amount'])
+            t['base_amount'] = currency.round(t['base_amount'])
+            t['tax_amount'] = currency.round(t['tax_amount'])
+
         return tax_grouped
 
-    def move_line_get(self, cr, uid, invoice_id):
+    @api.v7
+    def compute(self, cr, uid, invoice_id, context=None):
+        recs = self.browse(cr, uid, [], context)
+        invoice = recs.env['account.invoice'].browse(invoice_id)
+        return recs.compute(invoice)
+
+    @api.model
+    def move_line_get(self, invoice_id):
         res = []
-        cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%s', (invoice_id,))
-        for t in cr.dictfetchall():
-            if not t['amount'] \
-                    and not t['tax_code_id'] \
-                    and not t['tax_amount']:
+        self._cr.execute(
+            'SELECT * FROM account_invoice_tax WHERE invoice_id = %s',
+            (invoice_id,)
+        )
+        for row in self._cr.dictfetchall():
+            if not (row['amount'] or row['tax_code_id'] or row['tax_amount']):
                 continue
             res.append({
-                'type':'tax',
-                'name':t['name'],
-                'price_unit': t['amount'],
+                'type': 'tax',
+                'name': row['name'],
+                'price_unit': row['amount'],
                 'quantity': 1,
-                'price': t['amount'] or 0.0,
-                'account_id': t['account_id'],
-                'tax_code_id': t['tax_code_id'],
-                'tax_amount': t['tax_amount'],
-                'account_analytic_id': t['account_analytic_id'],
+                'price': row['amount'] or 0.0,
+                'account_id': row['account_id'],
+                'tax_code_id': row['tax_code_id'],
+                'tax_amount': row['tax_amount'],
+                'account_analytic_id': row['account_analytic_id'],
             })
         return res
 
 
-class res_partner(osv.osv):
-    """ Inherits partner and adds invoice information in the partner form """
+class res_partner(models.Model):
+    # Inherits partner and adds invoice information in the partner form
     _inherit = 'res.partner'
-    _columns = {
-        'invoice_ids': fields.one2many('account.invoice.line', 'partner_id', 'Invoices', readonly=True),
-    }
+
+    invoice_ids = fields.One2many('account.invoice', 'partner_id', string='Invoices',
+        readonly=True, copy=False)
 
     def _find_accounting_partner(self, partner):
         '''
@@ -1829,21 +1628,18 @@ class res_partner(osv.osv):
         '''
         return partner.commercial_partner_id
 
-    def copy_data(self, cr, uid, id, default=None, context=None):
-        default = default or {}
-        default.update({'invoice_ids' : []})
-        return super(res_partner, self).copy_data(cr, uid, id, default=default, context=context)
-
-
-class mail_compose_message(osv.Model):
+class mail_compose_message(models.Model):
     _inherit = 'mail.compose.message'
 
-    def send_mail(self, cr, uid, ids, context=None):
-        context = context or {}
-        if context.get('default_model') == 'account.invoice' and context.get('default_res_id') and context.get('mark_invoice_as_sent'):
-            context = dict(context, mail_post_autofollow=True)
-            self.pool.get('account.invoice').write(cr, uid, [context['default_res_id']], {'sent': True}, context=context)
-            self.pool.get('account.invoice').message_post(cr, uid, [context['default_res_id']], body=_("Invoice sent"), context=context)
-        return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
+    @api.multi
+    def send_mail(self):
+        context = self._context
+        if context.get('default_model') == 'account.invoice' and \
+                context.get('default_res_id') and context.get('mark_invoice_as_sent'):
+            invoice = self.env['account.invoice'].browse(context['default_res_id'])
+            invoice = invoice.with_context(mail_post_autofollow=True)
+            invoice.write({'sent': True})
+            invoice.message_post(body=_("Invoice sent"))
+        return super(mail_compose_message, self).send_mail()
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: