From 94716a3f14d929574c46ef165e4364d12a5903ed Mon Sep 17 00:00:00 2001
From: Nicolas Martinelli <nim@odoo.com>
Date: Thu, 27 Aug 2015 14:20:02 +0200
Subject: [PATCH] [IMP] sale: adaptation due to the new Sale module

Invoicing is now completely handled from the SO.
cf. documentation

Reason: complete rewrite of the Sale module.

Responsible: fp, dbo, nim
---
 addons/sale/__init__.py                       |    1 +
 addons/sale/__openerp__.py                    |   16 +-
 addons/sale/report/invoice_report.py          |   18 +-
 addons/sale/report/sale_report.py             |   42 +-
 addons/sale/report/sale_report_view.xml       |    3 +-
 addons/sale/res_config.py                     |   23 +-
 addons/sale/res_config_view.xml               |    4 +-
 addons/sale/res_partner_view.xml              |    2 +-
 addons/sale/sale.py                           | 1756 ++++++-----------
 addons/sale/sale_analytic.py                  |  108 +
 addons/sale/sale_demo.xml                     |   33 +-
 addons/sale/sale_product_demo.xml             |  273 +++
 addons/sale/sale_unit_test.xml                |    2 -
 addons/sale/sale_view.xml                     |  353 ++--
 addons/sale/sale_workflow.xml                 |  303 ---
 addons/sale/test/cancel_order.yml             |   82 -
 addons/sale/test/canceled_lines_order.yml     |   60 -
 addons/sale/test/create_sale_users.yml        |   28 -
 addons/sale/test/delete_order.yml             |   19 -
 addons/sale/test/manual_order_policy.yml      |   66 -
 addons/sale/test/sale_order_demo.yml          |   46 -
 addons/sale/tests/__init__.py                 |    4 +-
 addons/sale/tests/test_sale_common.py         |   38 +
 addons/sale/tests/test_sale_order.py          |  124 ++
 addons/sale/tests/test_sale_to_invoice.py     |   11 +-
 addons/sale/views/report_saleorder.xml        |   46 +-
 addons/sale/wizard/__init__.py                |    2 -
 addons/sale/wizard/sale_line_invoice.py       |  119 --
 addons/sale/wizard/sale_line_invoice.xml      |   42 -
 addons/sale/wizard/sale_make_invoice.py       |   52 -
 addons/sale/wizard/sale_make_invoice.xml      |   42 -
 .../sale/wizard/sale_make_invoice_advance.py  |  306 ++-
 .../sale/wizard/sale_make_invoice_advance.xml |   44 +-
 33 files changed, 1545 insertions(+), 2523 deletions(-)
 create mode 100644 addons/sale/sale_analytic.py
 create mode 100644 addons/sale/sale_product_demo.xml
 delete mode 100644 addons/sale/sale_workflow.xml
 delete mode 100644 addons/sale/test/cancel_order.yml
 delete mode 100644 addons/sale/test/canceled_lines_order.yml
 delete mode 100644 addons/sale/test/create_sale_users.yml
 delete mode 100644 addons/sale/test/delete_order.yml
 delete mode 100644 addons/sale/test/manual_order_policy.yml
 delete mode 100644 addons/sale/test/sale_order_demo.yml
 create mode 100644 addons/sale/tests/test_sale_common.py
 create mode 100644 addons/sale/tests/test_sale_order.py
 delete mode 100644 addons/sale/wizard/sale_line_invoice.py
 delete mode 100644 addons/sale/wizard/sale_line_invoice.xml
 delete mode 100644 addons/sale/wizard/sale_make_invoice.py
 delete mode 100644 addons/sale/wizard/sale_make_invoice.xml

diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py
index 3c0854c48bc1..2ec8f5b6b2f9 100644
--- a/addons/sale/__init__.py
+++ b/addons/sale/__init__.py
@@ -2,6 +2,7 @@
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
 import sale
+import sale_analytic
 import sales_team
 import res_partner
 import wizard
diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py
index 9efb1c43615f..3cb0f397a663 100644
--- a/addons/sale/__openerp__.py
+++ b/addons/sale/__openerp__.py
@@ -42,11 +42,8 @@ The Dashboard for the Sales Manager will include
     'depends': ['sales_team','account', 'procurement', 'report'],
     'data': [
         'wizard/sale_make_invoice_advance.xml',
-        'wizard/sale_line_invoice.xml',
-        'wizard/sale_make_invoice.xml',
         'security/sale_security.xml',
         'security/ir.model.access.csv',
-        'sale_workflow.xml',
         'sale_sequence.xml',
         'sale_report.xml',
         'sale_data.xml',
@@ -61,16 +58,9 @@ The Dashboard for the Sales Manager will include
         'sales_team_dashboard.xml',
         'sale_tip_data.xml',
     ],
-    'demo': ['sale_demo.xml'],
-    'test': [
-        '../account/test/account_minimal_test.xml',
-        'test/create_sale_users.yml',
-        'test/sale_order_demo.yml',
-        'test/manual_order_policy.yml',
-        'test/cancel_order.yml',
-        'test/delete_order.yml',
-        'test/canceled_lines_order.yml',
-    ],
+    'demo': ['sale_demo.xml',
+             'sale_product_demo.xml',
+             ],
     'css': ['static/src/css/sale.css'],
     'installable': True,
     'auto_install': False,
diff --git a/addons/sale/report/invoice_report.py b/addons/sale/report/invoice_report.py
index 315896b2dc92..d38931789367 100644
--- a/addons/sale/report/invoice_report.py
+++ b/addons/sale/report/invoice_report.py
@@ -1,21 +1,17 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.osv import fields,osv
 
-class account_invoice_report(osv.osv):
+from openerp import api, fields, models, _
+
+class AccountInvoiceReport(models.Model):
     _inherit = 'account.invoice.report'
-    _columns = {
-        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id'),
-    }
-    _depends = {
-        'account.invoice': ['team_id'],
-    }
+    team_id = fields.Many2one('crm.team', string='Sales Team')
 
     def _select(self):
-        return  super(account_invoice_report, self)._select() + ", sub.team_id as team_id"
+        return  super(AccountInvoiceReport, self)._select() + ", sub.team_id as team_id"
 
     def _sub_select(self):
-        return  super(account_invoice_report, self)._sub_select() + ", ai.team_id as team_id"
+        return  super(AccountInvoiceReport, self)._sub_select() + ", ai.team_id as team_id"
 
     def _group_by(self):
-        return super(account_invoice_report, self)._group_by() + ", ai.team_id"
+        return super(AccountInvoiceReport, self)._group_by() + ", ai.team_id"
diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py
index 3359e339efd7..5ee8d33c6a98 100644
--- a/addons/sale/report/sale_report.py
+++ b/addons/sale/report/sale_report.py
@@ -11,33 +11,32 @@ class sale_report(osv.osv):
     _rec_name = 'date'
 
     _columns = {
-        'date': fields.datetime('Date Order', readonly=True),  # TDE FIXME master: rename into date_order
-        'date_confirm': fields.date('Date Confirm', readonly=True),
+        'date': fields.datetime('Date Order', readonly=True),
         'product_id': fields.many2one('product.product', 'Product', readonly=True),
         'product_uom': fields.many2one('product.uom', 'Unit of Measure', readonly=True),
         'product_uom_qty': fields.float('# of Qty', readonly=True),
-
+        'qty_delivered': fields.float('Qty Delivered', readonly=True),
+        'qty_to_invoice': fields.float('Qty To Invoice', readonly=True),
+        'qty_invoiced': fields.float('Qty Invoiced', readonly=True),
         'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
         'company_id': fields.many2one('res.company', 'Company', readonly=True),
         'user_id': fields.many2one('res.users', 'Salesperson', readonly=True),
         'price_total': fields.float('Total Price', readonly=True),
-        'delay': fields.float('Commitment Delay', digits=(16,2), readonly=True),
         'product_tmpl_id': fields.many2one('product.template', 'Product Template', readonly=True),
         'categ_id': fields.many2one('product.category','Product Category', readonly=True),
-        'nbr': fields.integer('# of Lines', readonly=True),  # TDE FIXME master: rename into nbr_lines
-        'state': fields.selection([
-            ('cancel', 'Cancelled'),
-            ('draft', 'Draft'),
-            ('confirmed', 'Confirmed'),
-            ('exception', 'Exception'),
-            ('done', 'Done')], 'Order Status', readonly=True),
+        'nbr': fields.integer('# of Lines', readonly=True),
         'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True),
         'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
-        'invoiced': fields.boolean('Paid', readonly=True),
-        'nbr_paid': fields.integer('# of Paid Lines', readonly=True),
         'team_id': fields.many2one('crm.team', 'Sales Team', readonly=True, oldname='section_id'),
         'country_id': fields.many2one('res.country', 'Partner Country', readonly=True),
         'commercial_partner_id': fields.many2one('res.partner', 'Commercial Entity', readonly=True),
+        'state': fields.selection([
+                ('draft', 'Draft Quotation'),
+                ('sent', 'Quotation Sent'),
+                ('sale', 'Sales Order'),
+                ('done', 'Sales Done'),
+                ('cancel', 'Cancelled'),
+            ], string='Status', readonly=True),
     }
     _order = 'date desc'
 
@@ -56,22 +55,23 @@ class sale_report(osv.osv):
                     l.product_id as product_id,
                     t.uom_id as product_uom,
                     sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty,
-                    sum(l.product_uom_qty * cr.rate * l.price_unit * (100.0-l.discount) / 100.0) as price_total,
+                    sum(l.qty_delivered / u.factor * u2.factor) as qty_delivered,
+                    sum(l.qty_invoiced / u.factor * u2.factor) as qty_invoiced,
+                    sum(l.qty_to_invoice / u.factor * u2.factor) as qty_to_invoice,
+                    sum(l.price_total * cr.rate) as price_total,
+                    sum(l.price_subtotal * cr.rate) as price_subtotal,
                     count(*) as nbr,
                     s.date_order as date,
-                    s.date_confirm as date_confirm,
+                    s.state as state,
                     s.partner_id as partner_id,
                     s.user_id as user_id,
                     s.company_id as company_id,
-                    extract(epoch from avg(date_trunc('day',s.date_confirm)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
-                    l.state,
+                    extract(epoch from avg(date_trunc('day',s.date_order)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
                     t.categ_id as categ_id,
                     s.pricelist_id as pricelist_id,
                     s.project_id as analytic_account_id,
                     s.team_id as team_id,
                     p.product_tmpl_id,
-                    l.invoiced::integer as nbr_paid,
-                    l.invoiced,
                     partner.country_id as country_id,
                     partner.commercial_partner_id as commercial_partner_id
         """
@@ -100,16 +100,14 @@ class sale_report(osv.osv):
                     t.uom_id,
                     t.categ_id,
                     s.date_order,
-                    s.date_confirm,
                     s.partner_id,
                     s.user_id,
+                    s.state,
                     s.company_id,
-                    l.state,
                     s.pricelist_id,
                     s.project_id,
                     s.team_id,
                     p.product_tmpl_id,
-                    l.invoiced,
                     partner.country_id,
                     partner.commercial_partner_id
         """
diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml
index f05b25e1f3f0..092c37ba451b 100644
--- a/addons/sale/report/sale_report_view.xml
+++ b/addons/sale/report/sale_report_view.xml
@@ -58,10 +58,9 @@
         <field name="arch" type="xml">
             <search string="Sales Analysis">
                 <field name="date"/>
-                <field name="date_confirm"/>
                 <filter string="This Year" name="year" invisible="1" domain="[('date','&lt;=', time.strftime('%%Y-12-31')),('date','&gt;=',time.strftime('%%Y-01-01'))]"/>
                 <filter name="Quotations" domain="[('state','in',('draft'))]"/>
-                <filter name="Sales" string="Sales" domain="[('state','not in',('draft', 'cancel'))]"/>
+                <filter name="Sales" string="Sales" domain="[('state','not in',('draft', 'cancel', 'sent'))]"/>
                 <separator/>
                 <field name="partner_id"/>
                 <field name="product_id"/>
diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py
index abd574d1938d..d053f38a936a 100644
--- a/addons/sale/res_config.py
+++ b/addons/sale/res_config.py
@@ -4,7 +4,6 @@
 import logging
 
 from openerp.osv import fields, osv
-from openerp.tools.translate import _
 
 _logger = logging.getLogger(__name__)
 
@@ -19,15 +18,6 @@ class sale_configuration(osv.TransientModel):
             ], "Product Variants",
             help='Work with product variant allows you to define some variant of the same products, an ease the product management in the ecommerce for example',
             implied_group='product.group_product_variant'),
-        'module_sale_contract': fields.selection([
-            (0, 'Sell based on sales order only'),
-            (1, 'Activate contract management to track costs and revenues')
-            ], "Contracts",
-            help='Allows to define your customer contracts conditions: invoicing '
-                 'method (fixed price, on timesheet, advance invoice), the exact pricing '
-                 '(650€/day for a developer), the duration (one year support contract).\n'
-                 'You will be able to follow the progress of the contract and invoice automatically.\n'
-                 '-It installs the sale_contract module.'),
         'group_sale_pricelist':fields.selection([
             (0, 'Set a fixed sale price on each product'),
             (1, 'Use pricelists to adapt your price per customers or products')
@@ -67,10 +57,19 @@ class sale_configuration(osv.TransientModel):
         'group_sale_delivery_address': fields.selection([
             (0, "Invoicing and shipping addresses are always the same (Example: services companies)"),
             (1, 'Have 3 fields on sales orders: customer, invoice address, delivery address')
-            ], "Customer Addresses",
-            implied_group='sale.group_delivery_invoice_address'),
+            ], "Customer Addresses", implied_group='sale.group_delivery_invoice_address'),
+        'default_invoice_policy': fields.selection([
+            ('order', 'Invoice ordered quantities'),
+            ('delivery', 'Invoice delivered quantities'),
+            ('cost', 'Invoice based on costs (time and material, expenses)')
+            ], 'Default Invoicing', default_model='product.template')
     }
 
+    _defaults = {
+        'default_invoice_policy': 'order',
+    }
+
+
     def set_sale_defaults(self, cr, uid, ids, context=None):
         return {}
 
diff --git a/addons/sale/res_config_view.xml b/addons/sale/res_config_view.xml
index 097b6aaa8fc8..91a68c6bab8a 100644
--- a/addons/sale/res_config_view.xml
+++ b/addons/sale/res_config_view.xml
@@ -11,6 +11,7 @@
                         <field name="group_product_variant" widget="radio"/>
                         <field name="module_website_sale_digital" widget="radio"/>
                         <field name="group_uom" widget="radio"/>
+                        <field name="default_invoice_policy" widget="radio"/>
                     </group>
                     <group string="Quotations &amp; Sales" id="sale">
                         <field name="group_sale_pricelist" widget="radio"/>
@@ -20,9 +21,6 @@
                         <field name="module_website_quote" widget="radio"/>
                         <field name="module_sale_margin" widget="radio"/>
                     </group>
-                    <group string="Contracts" id="contract">
-                        <field name="module_sale_contract" widget="radio"/>
-                    </group>
                 </div>
             </field>
         </record>
diff --git a/addons/sale/res_partner_view.xml b/addons/sale/res_partner_view.xml
index 643373953592..a877cf803109 100644
--- a/addons/sale/res_partner_view.xml
+++ b/addons/sale/res_partner_view.xml
@@ -6,7 +6,7 @@
             <field name="res_model">sale.order</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form,graph</field>
-            <field name="context">{'search_default_partner_id': active_id}</field>
+            <field name="context">{'search_default_partner_id': active_id, 'show_sale': True}</field>
             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
             <field name="help" type="html">
               <p class="oe_view_nocontent_create">
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 32ac3d8e1af2..b8a54a6faa26 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -2,561 +2,307 @@
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
 from datetime import datetime, timedelta
-import time
 from openerp import SUPERUSER_ID
-from openerp.addons.analytic.models import analytic
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
+from openerp import api, fields, models, _
 import openerp.addons.decimal_precision as dp
-from openerp import workflow
 from openerp.exceptions import UserError
+from openerp.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
 
-class res_company(osv.Model):
+
+class res_company(models.Model):
     _inherit = "res.company"
-    _columns = {
-        'sale_note': fields.text('Default Terms and Conditions', translate=True, help="Default terms and conditions for quotations."),
-    }
+    sale_note = fields.Text(string='Default Terms and Conditions', translate=True)
+
 
-class sale_order(osv.osv):
+class SaleOrder(models.Model):
     _name = "sale.order"
     _inherit = ['mail.thread', 'ir.needaction_mixin']
     _description = "Sales Order"
-
-    def _amount_line_tax(self, cr, uid, line, context=None):
-        val = 0.0
-        if line.tax_id.ids:
-            for c in self.pool.get('account.tax').compute_all(cr, uid, line.tax_id.ids, line.price_unit * (1-(line.discount or 0.0)/100.0), line.order_id.currency_id.id, line.product_uom_qty, line.product_id.id, line.order_id.partner_id.id)['taxes']:
-                val += c.get('amount', 0.0)
-        return val
-
-    def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None):
-        """ Wrapper because of direct method passing as parameter for function fields """
-        return self._amount_all(cr, uid, ids, field_name, arg, context=context)
-
-    def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
-        cur_obj = self.pool.get('res.currency')
-        res = {}
-        for order in self.browse(cr, uid, ids, context=context):
-            res[order.id] = {
-                'amount_untaxed': 0.0,
-                'amount_tax': 0.0,
-                'amount_total': 0.0,
-            }
-            val = val1 = 0.0
-            cur = order.pricelist_id.currency_id
-            for line in order.order_line:
-                val1 += line.price_subtotal
-                val += self._amount_line_tax(cr, uid, line, context=context)
-            res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val)
-            res[order.id]['amount_untaxed'] = cur_obj.round(cr, uid, cur, val1)
-            res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
-        return res
-
-    def _search_invoiced(self, cursor, user, obj, name, args, context=None):
-        if not len(args):
-            return []
-        clause = ''
-        sale_clause = ''
-        no_invoiced = False
-        for arg in args:
-            if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
-                clause += 'AND inv.state = \'paid\''
-            else:
-                clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\'  AND rel.order_id = sale.id '
-                sale_clause = ',  sale_order AS sale '
-                no_invoiced = True
-
-        cursor.execute('SELECT rel.order_id ' \
-                'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \
-                'WHERE rel.invoice_id = inv.id ' + clause)
-        res = cursor.fetchall()
-        if no_invoiced:
-            cursor.execute('SELECT sale.id ' \
-                    'FROM sale_order AS sale ' \
-                    'WHERE sale.id NOT IN ' \
-                        '(SELECT rel.order_id ' \
-                        'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'')
-            res.extend(cursor.fetchall())
-        if not res:
-            return [('id', '=', 0)]
-        return [('id', 'in', [x[0] for x in res])]
-
-    def _get_order(self, cr, uid, ids, context=None):
-        result = {}
-        for line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
-            result[line.order_id.id] = True
-        return result.keys()
-
-    def _get_default_company(self, cr, uid, context=None):
-        company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
-        if not company_id:
-            raise UserError(_('There is no default company for the current user!'))
-        return company_id
-
-    def _get_invoiced(self, cr, uid, ids, field_name, arg, context=None):
-        res = {}
-        for order in self.browse(cr, uid, ids, context=context):
-            res[order.id] = {
-                'invoice_count': len(order.invoice_ids),
-                'invoiced': False,
-            }
-            if order.state != 'manual' and any(invoice.state == 'paid' for invoice in order.invoice_ids):
-                res[order.id]['invoiced'] = True
-        return res
-
-    _columns = {
-        'name': fields.char('Order Reference', required=True, copy=False,
-            readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
-        'origin': fields.char('Source Document', help="Reference of the document that generated this sales order request."),
-        'client_order_ref': fields.char('Customer Reference', copy=False),
-        'state': fields.selection([
-            ('draft', 'Draft Quotation'),
-            ('sent', 'Quotation Sent'),
-            ('cancel', 'Cancelled'),
-            ('waiting_date', 'Waiting Schedule'),
-            ('progress', 'Sales Order'),
-            ('manual', 'Sale to Invoice'),
-            ('shipping_except', 'Shipping Exception'),
-            ('invoice_except', 'Invoice Exception'),
-            ('done', 'Done'),
-            ], 'Status', readonly=True, copy=False, help="Gives the status of the quotation or sales order.\
-              \nThe exception status is automatically set when a cancel operation occurs \
-              in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\
-               but waiting for the scheduler to run on the order date.", select=True),
-        'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False),
-        'validity_date': fields.date('Expiration Date', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
-        'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."),
-        'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed.", copy=False),
-        'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True, track_visibility='onchange'),
-        'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True, track_visibility='always'),
-        'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."),
-        'partner_shipping_id': fields.many2one('res.partner', 'Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order."),
-        'order_policy': fields.selection([
-                ('manual', 'On Demand'),
-            ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
-            help="""This field controls how invoice and delivery operations are synchronized."""),
-        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Pricelist for current sales order."),
-        'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", readonly=True, required=True),
-        'project_id': fields.many2one('account.analytic.account', 'Contract / Analytic', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order."),
-        'contract_state': fields.related('project_id', 'state', string='Contract Status', type='selection', selection=analytic.ANALYTIC_ACCOUNT_STATE),
-
-        'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=True),
-        'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoices', readonly=True, copy=False, help="This is the list of invoices that have been generated for this sales order. The same sales order may have been invoiced in several times (by line for example)."),
-        'invoice_count': fields.function(_get_invoiced, type='integer', string='Invoices', multi="counts"),
-        'invoiced': fields.function(_get_invoiced, fnct_search=_search_invoiced, type='boolean', string='Paid', multi="counts"),
-        'note': fields.text('Terms and conditions'),
-        'amount_untaxed': fields.function(_amount_all_wrapper, digits=0, string='Untaxed Amount',
-            store={
-                'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
-                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
-            },
-            multi='sums', help="The amount without tax.", track_visibility='always'),
-        'amount_tax': fields.function(_amount_all_wrapper, digits=0, string='Taxes',
-            store={
-                'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
-                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
-            },
-            multi='sums', help="The tax amount."),
-        'amount_total': fields.function(_amount_all_wrapper, digits=0, string='Total',
-            store={
-                'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
-                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
-            },
-            multi='sums', help="The total amount."),
-
-        'payment_term_id': fields.many2one('account.payment.term', string='Payment Term', oldname='payment_term'),
-        'fiscal_position_id': fields.many2one('account.fiscal.position', oldname='fiscal_position', string='Fiscal Position'),
-        'company_id': fields.many2one('res.company', 'Company'),
-        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id', change_default=True),
-        'procurement_group_id': fields.many2one('procurement.group', 'Procurement group', copy=False),
-        'product_id': fields.related('order_line', 'product_id', type='many2one', relation='product.product', string='Product'),
-    }
-
-    _defaults = {
-        'date_order': fields.datetime.now,
-        'order_policy': 'manual',
-        'company_id': _get_default_company,
-        'state': 'draft',
-        'user_id': lambda obj, cr, uid, context: uid,
-        'name': lambda obj, cr, uid, context: '/',
-        'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'],
-        'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'],
-        'note': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.sale_note,
-        'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(cr, uid, context=c),
-    }
-    _sql_constraints = [
-        ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
-    ]
     _order = 'date_order desc, id desc'
 
-    # Form filling
-    def unlink(self, cr, uid, ids, context=None):
-        sale_orders = self.read(cr, uid, ids, ['state'], context=context)
-        unlink_ids = []
-        for s in sale_orders:
-            if s['state'] in ['draft', 'cancel']:
-                unlink_ids.append(s['id'])
-            elif s['state'] == 'sent':
-                raise UserError(_('In order to delete already sent quotation(s), you must cancel it before!'))
-            else:
-                raise UserError(_('In order to delete a confirmed sales order, you must cancel it before!'))
+    @api.depends('order_line.product_uom_qty', 'order_line.discount', 'order_line.price_unit', 'order_line.tax_id')
+    def _amount_all(self):
+        amount_untaxed = amount_tax = 0.0
+        for line in self.order_line:
+            amount_untaxed += line.price_subtotal
+            amount_tax += line.price_tax
+        self.update({
+            'amount_untaxed': self.pricelist_id.currency_id.round(amount_untaxed),
+            'amount_tax': self.pricelist_id.currency_id.round(amount_tax),
+            'amount_total': amount_untaxed + amount_tax,
+        })
 
-        return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
+    @api.depends('state', 'order_line.invoice_status')
+    def _get_invoiced(self):
+        for order in self:
+            invoice_ids = order.order_line.mapped('invoice_lines').mapped('invoice_id').ids
+
+            if order.state not in ('sale', 'done'):
+                invoice_status = 'no'
+            elif any(line.invoice_status == 'to invoice' for line in order.order_line):
+                invoice_status = 'to invoice'
+            elif all(line.invoice_status == 'invoiced' for line in order.order_line):
+                invoice_status = 'invoiced'
+            elif all(line.invoice_status in ['invoiced', 'upselling'] for line in order.order_line):
+                invoice_status = 'upselling'
+            else:
+                invoice_status = 'no'
 
-    def copy_quotation(self, cr, uid, ids, context=None):
-        id = self.copy(cr, uid, ids[0], context=context)
-        view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'view_order_form')
-        view_id = view_ref and view_ref[1] or False,
-        return {
-            'type': 'ir.actions.act_window',
-            'name': _('Sales Order'),
-            'res_model': 'sale.order',
-            'res_id': id,
-            'view_type': 'form',
-            'view_mode': 'form',
-            'view_id': view_id,
-            'target': 'current',
-        }
+            order.update({
+                'invoice_count': len(set(invoice_ids)),
+                'invoice_ids': invoice_ids,
+                'invoice_status': invoice_status
+            })
 
-    def _track_subtype(self, cr, uid, ids, init_values, context=None):
-        record = self.browse(cr, uid, ids[0], context=context)
-        if 'state' in init_values and record.state in ['manual', 'progress']:
+    @api.model
+    def _default_note(self):
+        return self.env.user.company_id.sale_note
+
+    @api.model
+    def _get_default_team(self):
+        default_team_id = self.env['crm.team']._get_default_team_id()
+        return self.env['crm.team'].browse(default_team_id)
+
+    @api.onchange('fiscal_position_id')
+    def _compute_tax_id(self):
+        for order in self:
+            order.order_line._compute_tax_id()
+
+    name = fields.Char(string='Order Reference', required=True, copy=False, readonly=True, index=True, default='New')
+    origin = fields.Char(string='Source Document', help="Reference of the document that generated this sales order request.")
+    client_order_ref = fields.Char(string='Customer Reference', copy=False)
+
+    state = fields.Selection([
+        ('draft', 'Quotation'),
+        ('sent', 'Quotation Sent'),
+        ('sale', 'Sale Order'),
+        ('done', 'Done'),
+        ('cancel', 'Cancelled'),
+        ], string='Status', readonly=True, copy=False, index=True, default='draft')
+    date_order = fields.Datetime(string='Order Date', required=True, readonly=True, index=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False, default=fields.Date.context_today)
+    validity_date = fields.Date(string='Expiration Date', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]})
+    create_date = fields.Datetime(string='Creation Date', readonly=True, index=True, help="Date on which sales order is created.")
+
+    user_id = fields.Many2one('res.users', string='Salesperson', states={
+        'draft': [('readonly', False)],
+        'sent': [('readonly', False)]
+        }, index=True, track_visibility='onchange', default=lambda self: self.env.user)
+    partner_id = fields.Many2one('res.partner', string='Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, index=True, track_visibility='always')
+    partner_invoice_id = fields.Many2one('res.partner', string='Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order.")
+    partner_shipping_id = fields.Many2one('res.partner', string='Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order.")
+
+    pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Pricelist for current sales order.")
+    currency_id = fields.Many2one("res.currency", related='pricelist_id.currency_id', string="Currency", readonly=True, required=True)
+    project_id = fields.Many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order.", copy=False)
+
+    order_line = fields.One2many('sale.order.line', 'order_id', string='Order Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True)
+
+    invoice_count = fields.Integer(string='# of Invoices', compute='_get_invoiced', store=True, readonly=True)
+    invoice_ids = fields.Many2many("account.invoice", string='Invoices', compute="_get_invoiced", readonly=True, copy=False)
+    invoice_status = fields.Selection([
+        ('upselling', 'Upselling Opportunity'),
+        ('invoiced', 'Fully Invoiced'),
+        ('to invoice', 'To Invoice'),
+        ('no', 'Nothing to Invoice')
+        ], string='Invoice Status', compute='_get_invoiced', store=True, readonly=True, default='no')
+
+    note = fields.Text('Terms and conditions', default=_default_note)
+
+    amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', track_visibility='always')
+    amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all', track_visibility='always')
+    amount_total = fields.Monetary(string='Total', store=True, readonly=True, compute='_amount_all', track_visibility='always')
+
+    payment_term_id = fields.Many2one('account.payment.term', string='Payment Term', oldname='payment_term')
+    fiscal_position_id = fields.Many2one('account.fiscal.position', oldname='fiscal_position', string='Fiscal Position')
+    company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('sale.order'))
+    team_id = fields.Many2one('crm.team', 'Sales Team', change_default=True, default=_get_default_team)
+    procurement_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False)
+
+    product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product')
+
+    @api.multi
+    def unlink(self):
+        for order in self:
+            if order.state != 'draft':
+                raise UserError(_('You can only delete draft quotations!'))
+        return super(SaleOrder, self).unlink()
+
+    @api.multi
+    def _track_subtype(self, init_values):
+        self.ensure_one()
+        if 'state' in init_values and self.state == 'sale':
             return 'sale.mt_order_confirmed'
-        elif 'state' in init_values and record.state == 'sent':
+        elif 'state' in init_values and self.state == 'sent':
             return 'sale.mt_order_sent'
-        return super(sale_order, self)._track_subtype(cr, uid, ids, init_values, context=context)
-
-    def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context=None):
-        if not pricelist_id:
-            return {}
-        pricelist = self.pool['product.pricelist'].browse(cr, uid, pricelist_id, context=context)
-        return {'value': {'currency_id': pricelist.currency_id.id}}
-
-    def get_salenote(self, cr, uid, ids, partner_id, context=None):
-        context_lang = context.copy()
-        if partner_id:
-            partner_lang = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context).lang
-            context_lang.update({'lang': partner_lang})
-        return self.pool.get('res.users').browse(cr, uid, uid, context=context_lang).company_id.sale_note
-
-    def onchange_delivery_id(self, cr, uid, ids, company_id, partner_id, delivery_id, fiscal_position_id, context=None):
-        r = {'value': {}}
-        if not company_id:
-            company_id = self._get_default_company(cr, uid, context=context)
-        fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, partner_id, delivery_id, context=context)
+        return super(SaleOrder, self)._track_subtype(init_values)
+
+
+    @api.onchange('partner_shipping_id')
+    def onchange_partner_shipping_id(self):
+        fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, self.partner_shipping_id.id)
         if fiscal_position:
-            r['value']['fiscal_position_id'] = fiscal_position
-        return r
+            self.fiscal_position_id = fiscal_position
+        return {}
 
-    def onchange_partner_id(self, cr, uid, ids, part, context=None):
-        if not part:
-            return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False,  'payment_term_id': False, 'fiscal_position_id': False}}
-
-        part = self.pool.get('res.partner').browse(cr, uid, part, context=context)
-        addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact'], context=context)
-        pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
-        invoice_part = self.pool.get('res.partner').browse(cr, uid, addr['invoice'], context=context)
-        payment_term = invoice_part.property_payment_term_id and invoice_part.property_payment_term_id.id or False
-        dedicated_salesman = part.user_id and part.user_id.id or uid
-        val = {
+    @api.multi
+    @api.onchange('partner_id')
+    def onchange_partner_id(self):
+        if not self.partner_id:
+            self.update({
+                'partner_invoice_id': False,
+                'partner_shipping_id': False,
+                'payment_term_id': False,
+                'fiscal_position_id': False,
+            })
+            return
+
+        addr = self.partner_id.address_get(['delivery', 'invoice'])
+        values = {
+            'pricelist_id': self.partner_id.property_product_pricelist and self.partner_id.property_product_pricelist.id or False,
+            'payment_term_id': self.partner_id.property_payment_term_id and self.partner_id.property_payment_term_id.id or False,
             'partner_invoice_id': addr['invoice'],
             'partner_shipping_id': addr['delivery'],
-            'payment_term_id': payment_term,
-            'user_id': dedicated_salesman,
         }
-        delivery_onchange = self.onchange_delivery_id(cr, uid, ids, False, part.id, addr['delivery'], False,  context=context)
-        val.update(delivery_onchange['value'])
-        if pricelist:
-            val['pricelist_id'] = pricelist
-        if not self.pool['crm.team']._get_default_team_id(cr, uid, context=context) and part.team_id:
-            val['team_id'] = part.team_id.id
-        sale_note = self.get_salenote(cr, uid, ids, part.id, context=context)
-        if sale_note: val.update({'note': sale_note})
-        return {'value': val}
-
-    def create(self, cr, uid, vals, context=None):
-        if context is None:
-            context = {}
-        if vals.get('name', '/') == '/':
-            vals['name'] = self.pool.get('ir.sequence').next_by_code(cr, uid, 'sale.order', context=context) or '/'
-        if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id', 'fiscal_position_id']):
-            defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context=context)['value']
-            if not vals.get('fiscal_position_id') and vals.get('partner_shipping_id'):
-                delivery_onchange = self.onchange_delivery_id(cr, uid, [], vals.get('company_id'), None, vals['partner_id'], vals.get('partner_shipping_id'), context=context)
-                defaults.update(delivery_onchange['value'])
-            vals = dict(defaults, **vals)
-        ctx = dict(context or {}, mail_create_nolog=True)
-        new_id = super(sale_order, self).create(cr, uid, vals, context=ctx)
-        self.message_post(cr, uid, [new_id], body=_("Quotation created"), context=ctx)
-        return new_id
-
-    def button_dummy(self, cr, uid, ids, context=None):
-        return True
 
-    # FIXME: deprecated method, overriders should be using _prepare_invoice() instead.
-    #        can be removed after 6.1.
-    def _inv_get(self, cr, uid, order, context=None):
-        return {}
+        if self.partner_id.user_id:
+            values['user_id'] = self.partner_id.user_id.id
+        if self.partner_id.team_id:
+            values['team_id'] = self.partner_id.team_id.id
+        self.update(values)
+
+    @api.model
+    def create(self, vals):
+        if vals.get('name', 'New') == 'New':
+            vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or 'New'
+        if any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']):
+            partner = self.env['res.partner'].browse(vals.get('partner_id'))
+            addr = partner.address_get(['delivery', 'invoice'])
+            vals['partner_invoice_id'] = vals.setdefault('partner_invoice_id', addr['invoice'])
+            vals['partner_shipping_id'] = vals.setdefault('partner_shipping_id', addr['delivery'])
+            vals['pricelist_id'] = vals.setdefault('pricelist_id', partner.property_product_pricelist and partner.property_product_pricelist.id)
+        result = super(SaleOrder, self).create(vals)
+        self.message_post(body=_("Quotation created"))
+        return result
 
-    def _prepare_invoice(self, cr, uid, order, lines, context=None):
+    @api.multi
+    def _prepare_invoice(self):
         """Prepare the dict of values to create the new invoice for a
            sales order. This method may be overridden to implement custom
            invoice generation (making sure to call super() to establish
            a clean extension chain).
-
-           :param browse_record order: sale.order record to invoice
-           :param list(int) line: list of invoice line IDs that must be
-                                  attached to the invoice
-           :return: dict of value to create() the invoice
         """
-        if context is None:
-            context = {}
-        journal_ids = self.pool.get('account.journal').search(cr, uid,
-            [('type', '=', 'sale'), ('company_id', '=', order.company_id.id)],
-            limit=1)
+        self.ensure_one()
+        journal_ids = self.env['account.journal'].search([('type', '=', 'sale'), ('company_id', '=', self.company_id.id)], limit=1)
         if not journal_ids:
-            raise UserError(_('Please define sales journal for this company: "%s" (id:%d).') % (order.company_id.name, order.company_id.id))
+            raise UserError(_('Please define an accounting sale journal for this company.'))
         invoice_vals = {
-            'name': order.client_order_ref or '',
-            'origin': order.name,
+            'name': self.client_order_ref or '',
+            'origin': self.name,
             'type': 'out_invoice',
-            'reference': order.client_order_ref or order.name,
-            'account_id': order.partner_invoice_id.property_account_receivable_id.id,
-            'partner_id': order.partner_invoice_id.id,
-            'journal_id': journal_ids[0],
-            'invoice_line_ids': [(6, 0, lines)],
-            'currency_id': order.pricelist_id.currency_id.id,
-            'comment': order.note,
-            'payment_term_id': order.payment_term_id.id,
-            'fiscal_position_id': order.fiscal_position_id.id or order.partner_invoice_id.property_account_position_id.id,
-            'date_invoice': context.get('date_invoice', False),
-            'company_id': order.company_id.id,
-            'user_id': order.user_id and order.user_id.id or False,
-            'team_id' : order.team_id.id
+            'reference': self.client_order_ref or self.name,
+            'account_id': self.partner_invoice_id.property_account_receivable_id.id,
+            'partner_id': self.partner_invoice_id.id,
+            'journal_id': journal_ids[0].id,
+            'currency_id': self.pricelist_id.currency_id.id,
+            'comment': self.note,
+            'payment_term_id': self.payment_term_id.id,
+            'fiscal_position_id': self.fiscal_position_id.id or self.partner_invoice_id.property_account_position_id.id,
+            'company_id': self.company_id.id,
+            'user_id': self.user_id and self.user_id.id,
+            'team_id': self.team_id.id
         }
-
-        # Care for deprecated _inv_get() hook - FIXME: to be removed after 6.1
-        invoice_vals.update(self._inv_get(cr, uid, order, context=context))
         return invoice_vals
 
-    def _make_invoice(self, cr, uid, order, lines, context=None):
-        inv_obj = self.pool.get('account.invoice')
-        obj_invoice_line = self.pool.get('account.invoice.line')
-        if context is None:
-            context = {}
-        invoiced_sale_line_ids = self.pool.get('sale.order.line').search(cr, uid, [('order_id', '=', order.id), ('invoiced', '=', True)], context=context)
-        from_line_invoice_ids = []
-        for invoiced_sale_line_id in self.pool.get('sale.order.line').browse(cr, uid, invoiced_sale_line_ids, context=context):
-            for invoice_line_id in invoiced_sale_line_id.invoice_lines:
-                if invoice_line_id.invoice_id.id not in from_line_invoice_ids:
-                    from_line_invoice_ids.append(invoice_line_id.invoice_id.id)
-        for preinv in order.invoice_ids:
-            if preinv.state not in ('cancel',) and preinv.id not in from_line_invoice_ids:
-                for preline in preinv.invoice_line_ids:
-                    inv_line_id = obj_invoice_line.copy(cr, uid, preline.id, {'invoice_id': False, 'price_unit': -preline.price_unit})
-                    lines.append(inv_line_id)
-        inv = self._prepare_invoice(cr, uid, order, lines, context=context)
-        inv_id = inv_obj.create(cr, uid, inv, context=context)
-        invoice = self.pool['account.invoice'].browse(cr, uid, inv_id, context=context)
-        invoice._onchange_payment_term_date_invoice()
-        invoice.compute_taxes()
-        return inv_id
-
-    def print_quotation(self, cr, uid, ids, context=None):
-        '''
-        This function prints the sales order 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.signal_workflow(cr, uid, ids, 'quotation_sent')
-        return self.pool['report'].get_action(cr, uid, ids, 'sale.report_saleorder', context=context)
-
-    def manual_invoice(self, cr, uid, ids, context=None):
-        """ create invoices for the given sales orders (ids), and open the form
-            view of one of the newly created invoices
-        """
-        mod_obj = self.pool.get('ir.model.data')
-
-        # create invoices through the sales orders' workflow
-        inv_ids0 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
-        self.signal_workflow(cr, uid, ids, 'manual_invoice')
-        inv_ids1 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
-        # determine newly created invoices
-        new_inv_ids = list(inv_ids1 - inv_ids0)
-
-        res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
-        res_id = res and res[1] or False,
-
-        return {
-            'name': _('Customer Invoices'),
-            'view_type': 'form',
-            'view_mode': 'form',
-            'view_id': [res_id],
-            'res_model': 'account.invoice',
-            'context': "{'type':'out_invoice'}",
-            'type': 'ir.actions.act_window',
-            'target': 'current',
-            'res_id': new_inv_ids and new_inv_ids[0] or False,
+    @api.multi
+    def print_quotation(self):
+        self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'})
+        return self.env['report'].get_action(self, 'sale.report_saleorder')
+
+    @api.multi
+    def action_view_invoice(self):
+        self.ensure_one()
+        imd = self.env['ir.model.data']
+        action = imd.xmlid_to_object('account.action_invoice_tree1')
+        list_view_id = imd.xmlid_to_res_id('account.invoice_tree')
+        form_view_id = imd.xmlid_to_res_id('account.invoice_form')
+
+        result = {
+            'name': action.name,
+            'help': action.help,
+            'type': action.type,
+            'views': [[list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'kanban'], [False, 'calendar'], [False, 'pivot']],
+            'target': action.target,
+            'context': action.context,
+            'res_model': action.res_model,
         }
-
-    def action_view_invoice(self, cr, uid, ids, context=None):
-        '''
-        This function returns an action that display existing invoices of given sales order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
-        '''
-        mod_obj = self.pool.get('ir.model.data')
-        act_obj = self.pool.get('ir.actions.act_window')
-
-        result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
-        id = result and result[1] or False
-        result = act_obj.read(cr, uid, [id], context=context)[0]
-        #compute the number of invoices to display
-        inv_ids = []
-        for so in self.browse(cr, uid, ids, context=context):
-            inv_ids += [invoice.id for invoice in so.invoice_ids]
-        #choose the view_mode accordingly
-        if len(inv_ids)>1:
-            result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
+        if len(self.invoice_ids) > 1:
+            result['domain'] = "[('id','in',%s)]" % self.invoice_ids.ids
+        elif len(self.invoice_ids) == 1:
+            result['views'] = [(form_view_id, 'form')]
+            result['res_id'] = self.invoice_ids.id
         else:
-            res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
-            result['views'] = [(res and res[1] or False, 'form')]
-            result['res_id'] = inv_ids and inv_ids[0] or False
+            result = {'type': 'ir.actions.act_window_close'}
         return result
 
-    def test_no_product(self, cr, uid, order, context):
-        for line in order.order_line:
-            if line.state == 'cancel':
-                continue
-            if line.product_id and (line.product_id.type in ['consu', 'product']):
-                return False
-        return True
-
-    def action_invoice_create(self, cr, uid, ids, grouped=False, states=None, date_invoice = False, context=None):
-        if states is None:
-            states = ['confirmed', 'done', 'exception']
-        res = False
+    @api.multi
+    def action_invoice_create(self, grouped=False, final=False):
+        inv_obj = self.env['account.invoice']
+        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
         invoices = {}
-        invoice_ids = []
-        invoice = self.pool.get('account.invoice')
-        obj_sale_order_line = self.pool.get('sale.order.line')
-        partner_currency = {}
-        # If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
-        # last day of the last month as invoice date
-        if date_invoice:
-            context = dict(context or {}, date_invoice=date_invoice)
-        for o in self.browse(cr, uid, ids, context=context):
-            currency_id = o.pricelist_id.currency_id.id
-            if (o.partner_id.id in partner_currency) and (partner_currency[o.partner_id.id] <> currency_id):
-                raise UserError(_('You cannot group sales having different currencies for the same partner.'))
-
-            partner_currency[o.partner_id.id] = currency_id
-            lines = []
-            for line in o.order_line:
-                if line.invoiced:
-                    continue
-                elif (line.state in states):
-                    lines.append(line.id)
-            created_lines = obj_sale_order_line.invoice_line_create(cr, uid, lines)
-            if created_lines:
-                invoices.setdefault(o.partner_invoice_id.id or o.partner_id.id, []).append((o, created_lines))
-        if not invoices:
-            for o in self.browse(cr, uid, ids, context=context):
-                for i in o.invoice_ids:
-                    if i.state == 'draft':
-                        return i.id
-        for val in invoices.values():
-            if grouped:
-                res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x, y: x + y, [l for o, l in val], []), context=context)
-                invoice_ref = ''
-                origin_ref = ''
-                for o, l in val:
-                    invoice_ref += (o.client_order_ref or o.name) + '|'
-                    origin_ref += (o.origin or o.name) + '|'
-                    self.write(cr, uid, [o.id], {'state': 'progress'})
-                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
-                    self.invalidate_cache(cr, uid, ['invoice_ids'], [o.id], context=context)
-                #remove last '|' in invoice_ref
-                if len(invoice_ref) >= 1:
-                    invoice_ref = invoice_ref[:-1]
-                if len(origin_ref) >= 1:
-                    origin_ref = origin_ref[:-1]
-                invoice.write(cr, uid, [res], {'origin': origin_ref, 'name': invoice_ref})
-            else:
-                for order, il in val:
-                    res = self._make_invoice(cr, uid, order, il, context=context)
-                    invoice_ids.append(res)
-                    self.write(cr, uid, [order.id], {'state': 'progress'})
-                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
-                    self.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
-        return res
-
-    def action_invoice_cancel(self, cr, uid, ids, context=None):
-        self.write(cr, uid, ids, {'state': 'invoice_except'}, context=context)
-        return True
-
-    def action_invoice_end(self, cr, uid, ids, context=None):
-        for this in self.browse(cr, uid, ids, context=context):
-            for line in this.order_line:
-                if line.state == 'exception':
-                    line.write({'state': 'confirmed'})
-            if this.state == 'invoice_except':
-                this.write({'state': 'progress'})
-        return True
-
-    def action_cancel(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        sale_order_line_obj = self.pool.get('sale.order.line')
-        account_invoice_obj = self.pool.get('account.invoice')
-        for sale in self.browse(cr, uid, ids, context=context):
-            for inv in sale.invoice_ids:
-                if inv.state not in ('draft', 'cancel'):
-                    raise UserError(_('Cannot cancel this sales order!') + ':' + _('First cancel all invoices attached to this sales order.'))
-                inv.signal_workflow('invoice_cancel')
-            line_ids = [l.id for l in sale.order_line if l.state != 'cancel']
-            sale_order_line_obj.button_cancel(cr, uid, line_ids, context=context)
-        self.write(cr, uid, ids, {'state': 'cancel'})
-        return True
 
-    def action_button_confirm(self, cr, uid, ids, context=None):
-        if not context:
-            context = {}
-        assert len(ids) == 1, 'This option should only be used for a single id at a time.'
-        self.signal_workflow(cr, uid, ids, 'order_confirm')
-        if context.get('send_email'):
-            self.force_quotation_send(cr, uid, ids, context=context)
-        return True
-
-    def action_wait(self, cr, uid, ids, context=None):
-        context = context or {}
-        for o in self.browse(cr, uid, ids):
-            if not any(line.state != 'cancel' for line in o.order_line):
-                raise UserError(_('You cannot confirm a sales order which has no line.'))
-            noprod = self.test_no_product(cr, uid, o, context)
-            if (o.order_policy == 'manual') or noprod:
-                self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
-            else:
-                self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
-            self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line if x.state != 'cancel'])
-        return True
-
-    def action_quotation_send(self, cr, uid, ids, context=None):
+        for order in self:
+            group_key = order.id if grouped else (order.partner_id.id, order.currency_id.id)
+            for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0):
+                if float_is_zero(line.qty_to_invoice, precision_digits=precision):
+                    continue
+                if group_key not in invoices:
+                    inv_data = order._prepare_invoice()
+                    invoice = inv_obj.create(inv_data)
+                    invoices[group_key] = invoice
+                if line.qty_to_invoice > 0:
+                    line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice)
+                elif line.qty_to_invoice < 0 and (final or invoices[group_key].amount_untaxed > abs(line.qty_to_invoice * line.price_unit)):
+                    line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice)
+
+        for invoice in invoices.values():
+            # If invoice is negative, do a refund invoice instead
+            if invoice.amount_untaxed < 0:
+                invoice.type = 'out_refund'
+                for line in invoice.invoice_line_ids:
+                    line.quantity = -line.quantity
+            # Necessary to force computation of taxes. In account_invoice, they are triggered
+            # by onchanges, which are not triggered when doing a create.
+            invoice.compute_taxes()
+
+        return [inv.id for inv in invoices.values()]
+
+    @api.multi
+    def action_draft(self):
+        self.filtered(lambda s: s.state in ['cancel', 'sent']).write({'state': 'draft'})
+
+    @api.multi
+    def action_cancel(self):
+        self.write({'state': 'cancel'})
+
+    @api.multi
+    def action_quotation_send(self):
         '''
         This function opens a window to compose an email, with the edi sale 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')
+        self.ensure_one()
+        ir_model_data = self.env['ir.model.data']
         try:
-            template_id = ir_model_data.get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1]
+            template_id = ir_model_data.get_object_reference('sale', 'email_template_edi_sale')[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]
+            compose_form_id = ir_model_data.get_object_reference('mail', 'email_compose_message_wizard_form')[1]
         except ValueError:
             compose_form_id = False
         ctx = dict()
         ctx.update({
             'default_model': 'sale.order',
-            'default_res_id': ids[0],
+            'default_res_id': self.ids[0],
             'default_use_template': bool(template_id),
             'default_template_id': template_id,
             'default_composition_mode': 'comment',
@@ -573,12 +319,12 @@ class sale_order(osv.osv):
             'context': ctx,
         }
 
-    def force_quotation_send(self, cr, uid, ids, context=None):
-        for order_id in ids:
-            email_act = self.action_quotation_send(cr, uid, [order_id], context=context)
+    @api.multi
+    def force_quotation_send(self):
+        for order in self:
+            email_act = order.action_quotation_send()
             if email_act and email_act.get('context'):
-                composer_obj = self.pool['mail.compose.message']
-                composer_values = {}
+                composer_obj = self.env['mail.compose.message']
                 email_ctx = email_act['context']
                 template_values = [
                     email_ctx.get('default_template_id'),
@@ -586,672 +332,442 @@ class sale_order(osv.osv):
                     email_ctx.get('default_model'),
                     email_ctx.get('default_res_id'),
                 ]
-                composer_values.update(composer_obj.onchange_template_id(cr, uid, None, *template_values, context=context).get('value', {}))
+                composer_values = composer_obj.onchange_template_id(*template_values).get('value', {})
                 if not composer_values.get('email_from'):
-                    composer_values['email_from'] = self.browse(cr, uid, order_id, context=context).company_id.email
+                    composer_values['email_from'] = order.company_id.email
                 for key in ['attachment_ids', 'partner_ids']:
                     if composer_values.get(key):
                         composer_values[key] = [(6, 0, composer_values[key])]
-                composer_id = composer_obj.create(cr, uid, composer_values, context=email_ctx)
-                composer_obj.send_mail(cr, uid, [composer_id], context=email_ctx)
+                composer_id = composer_obj.with_context(email_ctx).create(composer_values)
+                composer_id.with_context(email_ctx).send_mail()
         return True
 
-    def action_done(self, cr, uid, ids, context=None):
-        for order in self.browse(cr, uid, ids, context=context):
-            self.pool.get('sale.order.line').write(cr, uid, [line.id for line in order.order_line if line.state != 'cancel'], {'state': 'done'}, context=context)
-        return self.write(cr, uid, ids, {'state': 'done'}, context=context)
-
-    def _prepare_order_line_procurement(self, cr, uid, order, line, group_id=False, context=None):
-        date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
-        return {
-            'name': line.name,
-            'origin': order.name,
-            'date_planned': date_planned,
-            'product_id': line.product_id.id,
-            'product_qty': line.product_uom_qty,
-            'product_uom': line.product_uom.id,
-            'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty,
-            'product_uos': (line.product_uos and line.product_uos.id) or line.product_uom.id,
-            'company_id': order.company_id.id,
-            'group_id': group_id,
-            'sale_line_id': line.id
-        }
-
-    def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
-        date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=line.delay or 0.0)
-        return date_planned
-
-    def _prepare_procurement_group(self, cr, uid, order, context=None):
-        return {'name': order.name, 'partner_id': order.partner_shipping_id.id}
-
-    def procurement_needed(self, cr, uid, ids, context=None):
-        #when sale is installed only, there is no need to create procurements, that's only
-        #further installed modules (sale_service, sale_stock) that will change this.
-        sale_line_obj = self.pool.get('sale.order.line')
-        res = []
-        for order in self.browse(cr, uid, ids, context=context):
-            res.append(sale_line_obj.need_procurement(cr, uid, [line.id for line in order.order_line if line.state != 'cancel'], context=context))
-        return any(res)
-
-    def action_ignore_delivery_exception(self, cr, uid, ids, context=None):
-        for sale_order in self.browse(cr, uid, ids, context=context):
-            self.write(cr, uid, ids, {'state': 'progress' if sale_order.invoice_count else 'manual'}, context=context)
-        return True
-
-    def action_ship_create(self, cr, uid, ids, context=None):
-        """Create the required procurements to supply sales order lines, also connecting
-        the procurements to appropriate stock moves in order to bring the goods to the
-        sales order's requested location.
+    @api.multi
+    def action_done(self):
+        self.write({'state': 'done'})
+
+    @api.model
+    def _prepare_procurement_group(self):
+        return {'name': self.name}
+
+    @api.multi
+    def action_confirm(self):
+        for order in self:
+            order.state = 'sale'
+            order.order_line._action_procurement_create()
+            if not order.project_id:
+                for line in order.order_line:
+                    if line.product_id.invoice_policy == 'cost':
+                        order._create_analytic_account()
+                        break
+
+    @api.multi
+    def _create_analytic_account(self, prefix=None):
+        for order in self:
+            name = order.name
+            if prefix:
+                name = prefix + ": " + order.name
+            analytic = self.env['account.analytic.account'].create({
+                'name': name,
+                'code': order.client_order_ref,
+                'company_id': order.company_id.id,
+                'partner_id': order.partner_id.id
+            })
+            order.project_id = analytic
 
-        :return: True
-        """
-        context = context or {}
-        context['lang'] = self.pool['res.users'].browse(cr, uid, uid).lang
-        procurement_obj = self.pool.get('procurement.order')
-        sale_line_obj = self.pool.get('sale.order.line')
-        for order in self.browse(cr, uid, ids, context=context):
-            proc_ids = []
-            vals = self._prepare_procurement_group(cr, uid, order, context=context)
-            if not order.procurement_group_id:
-                group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context)
-                order.write({'procurement_group_id': group_id})
-
-            for line in order.order_line:
-                if line.state == 'cancel':
-                    continue
-                #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate)
-                if line.procurement_ids:
-                    #first check them to see if they are in exception or not (one of the related moves is cancelled)
-                    procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']])
-                    line.refresh()
-                    #run again procurement that are in exception in order to trigger another move
-                    except_proc_ids = [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')]
-                    procurement_obj.reset_to_confirmed(cr, uid, except_proc_ids, context=context)
-                    proc_ids += except_proc_ids
-                elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context):
-                    if (line.state == 'done') or not line.product_id:
-                        continue
-                    vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context)
-                    ctx = context.copy()
-                    ctx['procurement_autorun_defer'] = True
-                    proc_id = procurement_obj.create(cr, uid, vals, context=ctx)
-                    proc_ids.append(proc_id)
-            #Confirm procurement order such that rules will be applied on it
-            #note that the workflow normally ensure proc_ids isn't an empty list
-            procurement_obj.run(cr, uid, proc_ids, context=context)
-
-            #if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO
-            if order.state == 'shipping_except':
-                val = {'state': 'progress', 'shipped': False}
-
-                if (order.order_policy == 'manual'):
-                    for line in order.order_line:
-                        if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
-                            val['state'] = 'manual'
-                            break
-                order.write(val)
-        return True
 
+class SaleOrderLine(models.Model):
+    _name = 'sale.order.line'
+    _description = 'Sales Order Line'
+    _order = 'order_id desc, sequence, id'
 
+    @api.depends('state', 'product_uom_qty', 'qty_delivered', 'qty_to_invoice', 'qty_invoiced')
+    def _compute_invoice_status(self):
+        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+        for line in self:
+            if line.state not in ('sale', 'done'):
+                line.invoice_status = 'no'
+            elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
+                line.invoice_status = 'to invoice'
+            elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) == 1 or\
+                    float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0 and\
+                    float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
+                line.invoice_status = 'upselling'
+            elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) == 0:
+                line.invoice_status = 'invoiced'
+            else:
+                line.invoice_status = 'no'
 
-    def onchange_fiscal_position(self, cr, uid, ids, fiscal_position_id, order_lines, context=None):
-        '''Update taxes of order lines for each line where a product is defined
+    @api.depends('product_uom_qty', 'discount', 'price_unit', 'tax_id')
+    def _compute_amount(self):
+        for line in self:
+            price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
+            taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_id)
+            line.update({
+                'price_tax': taxes['total_included'] - taxes['total_excluded'],
+                'price_total': taxes['total_included'],
+                'price_subtotal': taxes['total_excluded'],
+            })
 
-        :param list ids: not used
-        :param int fiscal_position_id: sale order fiscal position
-        :param list order_lines: command list for one2many write method
-        '''
-        order_line = []
-        fiscal_obj = self.pool.get('account.fiscal.position')
-        product_obj = self.pool.get('product.product')
-        line_obj = self.pool.get('sale.order.line')
-
-        fpos = False
-        if fiscal_position_id:
-            fpos = fiscal_obj.browse(cr, uid, fiscal_position_id, context=context)
-
-        for line in order_lines:
-            # create    (0, 0,  { fields })
-            # update    (1, ID, { fields })
-            if line[0] in [0, 1]:
-                prod = None
-                if line[2].get('product_id'):
-                    prod = product_obj.browse(cr, uid, line[2]['product_id'], context=context)
-                elif line[1]:
-                    prod =  line_obj.browse(cr, uid, line[1], context=context).product_id
-                if prod and prod.taxes_id:
-                    line[2]['tax_id'] = [[6, 0, fiscal_obj.map_tax(cr, uid, fpos, prod.taxes_id)]]
-                order_line.append(line)
-
-            # link      (4, ID)
-            # link all  (6, 0, IDS)
-            elif line[0] in [4, 6]:
-                line_ids = line[0] == 4 and [line[1]] or line[2]
-                for line_id in line_ids:
-                    prod = line_obj.browse(cr, uid, line_id, context=context).product_id
-                    if prod and prod.taxes_id:
-                        order_line.append([1, line_id, {'tax_id': [[6, 0, fiscal_obj.map_tax(cr, uid, fpos, prod.taxes_id)]]}])
-                    else:
-                        order_line.append([4, line_id])
+    @api.depends('product_id.invoice_policy', 'order_id.state')
+    def _compute_qty_delivered_updateable(self):
+        for line in self:
+            line.qty_delivered_updateable = line.product_id.invoice_policy in ('order', 'delivery') and line.order_id.state == 'sale'
+
+    @api.depends('qty_invoiced', 'qty_delivered', 'product_uom_qty', 'order_id.state')
+    def _get_to_invoice_qty(self):
+        for line in self:
+            if line.order_id.state in ['sale', 'done']:
+                if line.product_id.invoice_policy == 'order':
+                    line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced
+                else:
+                    line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
             else:
-                order_line.append(line)
-        return {'value': {'order_line': order_line, 'amount_untaxed': False, 'amount_tax': False, 'amount_total': False}}
+                line.qty_to_invoice = 0
+
+    @api.depends('invoice_lines.invoice_id.state', 'invoice_lines.quantity')
+    def _get_invoice_qty(self):
+        for line in self:
+            qty_invoiced = 0.0
+            for invoice_line in line.invoice_lines:
+                if invoice_line.invoice_id.state != 'cancel':
+                    if invoice_line.invoice_id.type == 'out_invoice':
+                        qty_invoiced += invoice_line.quantity
+                    elif invoice_line.invoice_id.type == 'out_refund':
+                        qty_invoiced -= invoice_line.quantity
+            line.qty_invoiced = qty_invoiced
+
+    @api.depends('price_subtotal', 'product_uom_qty')
+    def _get_price_reduce(self):
+        for line in self:
+            line.price_reduce = line.price_subtotal / line.product_uom_qty if line.product_uom_qty else 0.0
+
+    @api.onchange('order_id', 'product_id')
+    def _compute_tax_id(self):
+        for line in self:
+            fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id
+            if fpos:
+                # The superuser is used by website_sale in order to create a sale order. We need to make
+                # sure we only select the taxes related to the company of the partner. This should only
+                # apply if the partner is linked to a company.
+                if self.env.uid == SUPERUSER_ID and line.order_id.company_id:
+                    taxes = fpos.map_tax(line.product_id.taxes_id).filtered(lambda r: r.company_id == line.order_id.company_id)
+                else:
+                    taxes = fpos.map_tax(line.product_id.taxes_id)
+                line.tax_id = taxes
+            else:
+                line.tax_id = False
 
-    def test_procurements_done(self, cr, uid, ids, context=None):
-        for sale in self.browse(cr, uid, ids, context=context):
-            for line in sale.order_line:
-                if line.state == 'cancel':
-                    continue
-                if not all([x.state == 'done' for x in line.procurement_ids]):
-                    return False
-        return True
+    @api.multi
+    def _prepare_order_line_procurement(self, group_id=False):
+        self.ensure_one()
+        return {
+            'name': self.name,
+            'origin': self.order_id.name,
+            'date_planned': datetime.strptime(self.order_id.date_order, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=self.customer_lead),
+            'product_id': self.product_id.id,
+            'product_qty': self.product_uom_qty,
+            'product_uom': self.product_uom.id,
+            'company_id': self.order_id.company_id.id,
+            'group_id': group_id,
+            'sale_line_id': self.id
+        }
 
-    def test_procurements_except(self, cr, uid, ids, context=None):
-        for sale in self.browse(cr, uid, ids, context=context):
-            for line in sale.order_line:
-                if line.state == 'cancel':
-                    continue
-                if any([x.state == 'cancel' for x in line.procurement_ids]):
-                    return True
-        return False
-
-
-# TODO add a field price_unit_uos
-# - update it on change product and unit price
-# - use it in report if there is a uos
-class sale_order_line(osv.osv):
-
-    def need_procurement(self, cr, uid, ids, context=None):
-        #when sale is installed only, there is no need to create procurements, that's only
-        #further installed modules (sale_service, sale_stock) that will change this.
-        prod_obj = self.pool.get('product.product')
-        for line in self.browse(cr, uid, ids, context=context):
-            if prod_obj.need_procurement(cr, uid, [line.product_id.id], context=context):
-                return True
-        return False
-
-    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
-        tax_obj = self.pool.get('account.tax')
-        cur_obj = self.pool.get('res.currency')
-        res = {}
-        if context is None:
-            context = {}
-        for line in self.browse(cr, uid, ids, context=context):
-            price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
-            cur_id = line.order_id.pricelist_id.currency_id.id
-            if line.tax_id.ids:
-                res[line.id] = tax_obj.compute_all(cr, uid, line.tax_id.ids, price, cur_id, line.product_uom_qty, line.product_id.id, line.order_id.partner_id.id)['total_excluded']
-            else:
-                res[line.id] = price * line.product_uom_qty
-        return res
+    @api.multi
+    def _action_procurement_create(self):
+        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+        for line in self:
+            if line.state != 'sale':
+                continue
+            qty = 0.0
+            for proc in line.procurement_ids:
+                qty += proc.product_qty
+            if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
+                return False
 
-    def _get_uom_id(self, cr, uid, *args):
-        try:
-            proxy = self.pool.get('ir.model.data')
-            result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit')
-            return result[1]
-        except Exception, ex:
-            return False
-
-    def _fnct_line_invoiced(self, cr, uid, ids, field_name, args, context=None):
-        res = dict.fromkeys(ids, False)
-        for this in self.browse(cr, uid, ids, context=context):
-            res[this.id] = this.invoice_lines and \
-                all(iline.invoice_id.state != 'cancel' for iline in this.invoice_lines)
-        return res
+            if not line.order_id.procurement_group_id:
+                vals = line.order_id._prepare_procurement_group()
+                line.order_id.procurement_group_id = self.env["procurement.group"].create(vals)
 
-    def _order_lines_from_invoice(self, cr, uid, ids, context=None):
-        # direct access to the m2m table is the less convoluted way to achieve this (and is ok ACL-wise)
-        cr.execute("""SELECT DISTINCT sol.id FROM sale_order_invoice_rel rel JOIN
-                                                  sale_order_line sol ON (sol.order_id = rel.order_id)
-                                    WHERE rel.invoice_id = ANY(%s)""", (list(ids),))
-        return [i[0] for i in cr.fetchall()]
-
-    def _get_price_reduce(self, cr, uid, ids, field_name, arg, context=None):
-        res = dict.fromkeys(ids, 0.0)
-        for line in self.browse(cr, uid, ids, context=context):
-            res[line.id] = line.price_subtotal / line.product_uom_qty
-        return res
+            vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id)
+            vals['product_qty'] = line.product_uom_qty - qty
+            new_proc = self.env["procurement.order"].create(vals)
+            new_proc.run()
+        return True
 
-    _name = 'sale.order.line'
-    _description = 'Sales Order Line'
-    _columns = {
-        'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
-        'name': fields.text('Description', required=True, readonly=True, states={'draft': [('readonly', False)]}),
-        'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
-        'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True, readonly=True, states={'draft': [('readonly', False)]}, ondelete='restrict'),
-        'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True, copy=False),
-        'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean',
-            store={
-                'account.invoice': (_order_lines_from_invoice, ['state'], 10),
-                'sale.order.line': (lambda self,cr,uid,ids,ctx=None: ids, ['invoice_lines'], 10)
-            }),
-        'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}),
-        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits=0),
-        'price_reduce': fields.function(_get_price_reduce, type='float', string='Price Reduce', digits_compute=dp.get_precision('Product Price')),
-        'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}),
-        'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
-        'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}),
-        'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}),
-        'product_uos': fields.many2one('product.uom', 'Product UoS'),
-        'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}),
-        'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}),
-        'state': fields.selection(
-                [('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')],
-                'Status', required=True, readonly=True, copy=False,
-                help='* The \'Draft\' status is set when the related sales order in draft status. \
-                    \n* The \'Confirmed\' status is set when the related sales order is confirmed. \
-                    \n* The \'Exception\' status is set when the related sales order is set as exception. \
-                    \n* The \'Done\' status is set when the sales order line has been picked. \
-                    \n* The \'Cancelled\' status is set when a user cancel the sales order related.'),
-        'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', store=True, string='Customer'),
-        'salesman_id':fields.related('order_id', 'user_id', type='many2one', relation='res.users', store=True, string='Salesperson'),
-        'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
-        'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation and the shipping of the products to the customer", readonly=True, states={'draft': [('readonly', False)]}),
-        'procurement_ids': fields.one2many('procurement.order', 'sale_line_id', 'Procurements'),
-    }
-    _order = 'order_id desc, sequence, id'
-    _defaults = {
-        'product_uom' : _get_uom_id,
-        'discount': 0.0,
-        'product_uom_qty': 1,
-        'product_uos_qty': 1,
-        'sequence': 10,
-        'state': 'draft',
-        'price_unit': 0.0,
-        'delay': 0.0,
-    }
-
-
-
-    def _get_line_qty(self, cr, uid, line, context=None):
-        if line.product_uos:
-            return line.product_uos_qty or 0.0
-        return line.product_uom_qty
-
-    def _get_line_uom(self, cr, uid, line, context=None):
-        if line.product_uos:
-            return line.product_uos.id
-        return line.product_uom.id
-
-    def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None):
+    # Create new procurements if quantities purchased changes
+    @api.model
+    def create(self, values):
+        line = super(SaleOrderLine, self).create(values)
+        if line.state == 'sale':
+            line._action_procurement_create()
+        return line
+
+    # Create new procurements if quantities purchased changes
+    @api.multi
+    def write(self, values):
+        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+        lines = False
+        if 'product_uom_qty' in values:
+            lines = self.filtered(
+                lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1)
+            lines._action_procurement_create()
+        return super(SaleOrderLine, self).write(values)
+
+    order_id = fields.Many2one('sale.order', string='Order Reference', required=True, ondelete='cascade', index=True, copy=False)
+    name = fields.Text(string='Description', required=True)
+    sequence = fields.Integer(string='Sequence', default=10)
+
+    invoice_lines = fields.Many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_line_id', string='Invoice Lines', copy=False)
+    invoice_status = fields.Selection([
+        ('upselling', 'Upselling Opportunity'),
+        ('invoiced', 'Fully Invoiced'),
+        ('to invoice', 'To Invoice'),
+        ('no', 'Nothing to Invoice')
+        ], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no')
+    price_unit = fields.Float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price'), default=0.0)
+
+    price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True)
+    price_tax = fields.Monetary(compute='_compute_amount', string='Taxes', readonly=True, store=True)
+    price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True)
+
+    price_reduce = fields.Monetary(compute='_get_price_reduce', string='Price Reduce', readonly=True, store=True)
+    tax_id = fields.Many2many('account.tax', string='Taxes', readonly=True, states={'draft': [('readonly', False)]})
+
+    discount = fields.Float(string='Discount (%)', digits_compute=dp.get_precision('Discount'), default=0.0)
+
+    product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], change_default=True, ondelete='restrict', required=True)
+    product_uom_qty = fields.Float(string='Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
+    product_uom = fields.Many2one('product.uom', string='Unit of Measure', required=True)
+
+    qty_delivered_updateable = fields.Boolean(compute='_compute_qty_delivered_updateable', string='Can Edit Delivered', readonly=True, default=True)
+    qty_delivered = fields.Float(string='Delivered', copy=False, digits_compute=dp.get_precision('Product Unit of Measure'), default=0.0)
+    qty_to_invoice = fields.Float(
+        compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True,
+        digits_compute=dp.get_precision('Product Unit of Measure'), default=0.0)
+    qty_invoiced = fields.Float(
+        compute='_get_invoice_qty', string='Invoiced', store=True, readonly=True,
+        digits_compute=dp.get_precision('Product Unit of Measure'), default=0.0)
+
+    salesman_id = fields.Many2one(related='order_id.user_id', store=True, string='Salesperson', readonly=True)
+    currency_id = fields.Many2one(related='order_id.currency_id', store=True, string='Currency', readonly=True)
+    company_id = fields.Many2one(related='order_id.company_id', string='Company', store=True, readonly=True)
+    order_partner_id = fields.Many2one(related='order_id.partner_id', store=True, string='Customer')
+
+    state = fields.Selection([
+        ('draft', 'Quotation'),
+        ('sent', 'Quotation Sent'),
+        ('sale', 'Sale Order'),
+        ('done', 'Done'),
+        ('cancel', 'Cancelled'),
+    ], related='order_id.state', string='Order Status', readonly=True, copy=False, store=True, default='draft')
+
+    customer_lead = fields.Float(
+        'Delivery Lead Time', required=True, default=0.0,
+        help="Number of days between the order confirmation and the shipping of the products to the customer", oldname="delay")
+    procurement_ids = fields.One2many('procurement.order', 'sale_line_id', string='Procurements')
+
+    @api.multi
+    def _prepare_invoice_line(self, qty):
         """Prepare the dict of values to create the new invoice line for a
            sales order line. This method may be overridden to implement custom
            invoice generation (making sure to call super() to establish
            a clean extension chain).
 
-           :param browse_record line: sale.order.line record to invoice
-           :param int account_id: optional ID of a G/L account to force
-               (this is used for returning products including service)
-           :return: dict of values to create() the invoice line
+           :param qty : float quantity to invoice
         """
+        self.ensure_one()
         res = {}
-        if not line.invoiced:
-            if not account_id:
-                if line.product_id:
-                    account_id = line.product_id.property_account_income_id.id
-                    if not account_id:
-                        account_id = line.product_id.categ_id.property_account_income_categ_id.id
-                    if not account_id:
-                        raise UserError(
-                                _('Please define income account for this product: "%s" (id:%d).') % \
-                                    (line.product_id.name, line.product_id.id,))
-                else:
-                    prop = self.pool.get('ir.property').get(cr, uid,
-                            'property_account_income_categ_id', 'product.category',
-                            context=context)
-                    account_id = prop and prop.id or False
-            uosqty = self._get_line_qty(cr, uid, line, context=context)
-            uos_id = self._get_line_uom(cr, uid, line, context=context)
-            pu = 0.0
-            if uosqty:
-                pu = round(line.price_unit * line.product_uom_qty / uosqty,
-                        self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price'))
-            fpos = line.order_id.fiscal_position_id or False
-            account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, account_id)
-            if not account_id:
-                raise UserError(_('There is no Fiscal Position defined or Income category account defined for default properties of Product categories.'))
-            res = {
-                'name': line.name,
-                'sequence': line.sequence,
-                'origin': line.order_id.name,
-                'account_id': account_id,
-                'price_unit': pu,
-                'quantity': uosqty,
-                'discount': line.discount,
-                'uos_id': uos_id,
-                'product_id': line.product_id.id or False,
-                'invoice_line_tax_ids': [(6, 0, [x.id for x in line.tax_id])],
-                'account_analytic_id': line.order_id.project_id and line.order_id.project_id.id or False,
-            }
-
+        account_id = self.product_id.property_account_income_id.id or self.product_id.categ_id.property_account_income_categ_id.id
+        if not account_id:
+            raise UserError(_('Please define income account for this product: "%s" (id:%d) - or for its category: "%s".') % \
+                            (self.product_id.name, self.product_id.id, self.product_id.categ_id.name))
+
+        fpos = self.order_id.fiscal_position_id or self.order_id.partner_id.property_account_position_id
+        if fpos:
+            account_id = self.order_id.fiscal_position_id.map_account(account_id)
+
+        res = {
+            'name': self.name,
+            'sequence': self.sequence,
+            'origin': self.order_id.name,
+            'account_id': account_id,
+            'price_unit': self.price_unit,
+            'quantity': qty,
+            'discount': self.discount,
+            'uom_id': self.product_uom.id,
+            'product_id': self.product_id.id or False,
+            'invoice_line_tax_ids': [(6, 0, self.tax_id.ids)],
+            'account_analytic_id': self.order_id.project_id.id,
+        }
         return res
 
-    def invoice_line_create(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-
-        create_ids = []
-        sales = set()
-        for line in self.browse(cr, uid, ids, context=context):
-            vals = self._prepare_order_line_invoice_line(cr, uid, line, False, context)
-            if vals:
-                inv_id = self.pool.get('account.invoice.line').create(cr, uid, vals, context=context)
-                self.write(cr, uid, [line.id], {'invoice_lines': [(4, inv_id)]}, context=context)
-                sales.add(line.order_id.id)
-                create_ids.append(inv_id)
-        # Trigger workflow events
-        for sale_id in sales:
-            workflow.trg_write(uid, 'sale.order', sale_id, cr)
-        return create_ids
-
-    def button_cancel(self, cr, uid, ids, context=None):
-        lines = self.browse(cr, uid, ids, context=context)
-        for line in lines:
-            if line.invoiced:
-                raise UserError(_('You cannot cancel a sales order line that has already been invoiced.'))
-        procurement_obj = self.pool['procurement.order']
-        procurement_obj.cancel(cr, uid, sum([l.procurement_ids.ids for l in lines], []), context=context)
-        return self.write(cr, uid, ids, {'state': 'cancel'})
-
-    def button_confirm(self, cr, uid, ids, context=None):
-        return self.write(cr, uid, ids, {'state': 'confirmed'})
-
-    def button_done(self, cr, uid, ids, context=None):
-        res = self.write(cr, uid, ids, {'state': 'done'})
-        for line in self.browse(cr, uid, ids, context=context):
-            workflow.trg_write(uid, 'sale.order', line.order_id.id, cr)
-        return res
+    @api.multi
+    def invoice_line_create(self, invoice_id, qty):
+        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
+        for line in self:
+            if not float_is_zero(qty, precision_digits=precision):
+                vals = line._prepare_invoice_line(qty=qty)
+                vals.update({'invoice_id': invoice_id, 'sale_line_ids': [(6, 0, [line.id])]})
+                self.env['account.invoice.line'].create(vals)
+
+    @api.multi
+    @api.onchange('product_id')
+    def product_id_change(self):
+        if not self.product_id:
+            return {'domain': {'product_uom': []}}
+
+        vals = {}
+        domain = {'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
+        if not (self.product_uom and (self.product_id.uom_id.category_id.id == self.product_uom.category_id.id)):
+            vals['product_uom'] = self.product_id.uom_id
+
+        product = self.product_id.with_context(
+            lang=self.order_id.partner_id.lang,
+            partner=self.order_id.partner_id.id,
+            quantity=self.product_uom_qty,
+            date=self.order_id.date_order,
+            pricelist=self.order_id.pricelist_id.id,
+            uom=self.product_uom.id
+        )
+
+        name = product.name_get()[0][1]
+        if product.description_sale:
+            name += '\n' + product.description_sale
+        vals['name'] = name
+
+        if self.order_id.pricelist_id and self.order_id.partner_id:
+            vals['price_unit'] = product.price
+        self.update(vals)
+        return {'domain': domain}
+
+    @api.onchange('product_uom')
+    def product_uom_change(self):
+        if not self.product_uom:
+            self.price_unit = 0.0
+            return
+        if self.order_id.pricelist_id and self.order_id.partner_id:
+            product = self.product_id.with_context(
+                lang=self.order_id.partner_id.lang,
+                partner=self.order_id.partner_id.id,
+                quantity=self.product_uom_qty,
+                date_order=self.order_id.date_order,
+                pricelist=self.order_id.pricelist_id.id,
+                uom=self.product_uom.id
+            )
+            self.price_unit = product.price
 
-    def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None):
-        product_obj = self.pool.get('product.product')
-        if not product_id:
-            return {'value': {'product_uom': product_uos,
-                'product_uom_qty': product_uos_qty}, 'domain': {}}
+    @api.multi
+    def unlink(self):
+        if self.filtered(lambda x: x.state in ('sale', 'done')):
+            raise UserError(_('You can not remove a sale order line.\nDiscard changes and try setting the quantity to 0.'))
+        return super(SaleOrderLine, self).unlink()
 
-        product = product_obj.browse(cr, uid, product_id)
-        value = {
-            'product_uom': product.uom_id.id,
-        }
-        # FIXME must depend on uos/uom of the product and not only of the coeff.
-        try:
-            value.update({
-                'product_uom_qty': product_uos_qty / product.uos_coeff,
-                'th_weight': product_uos_qty / product.uos_coeff * product.weight
-            })
-        except ZeroDivisionError:
-            pass
-        return {'value': value}
-
-    def create(self, cr, uid, values, context=None):
-        if values.get('order_id') and values.get('product_id') and  any(f not in values for f in ['name', 'price_unit', 'type', 'product_uom_qty', 'product_uom']):
-            order = self.pool['sale.order'].read(cr, uid, values['order_id'], ['pricelist_id', 'partner_id', 'date_order', 'fiscal_position_id'], context=context)
-            defaults = self.product_id_change(cr, uid, [], order['pricelist_id'][0], values['product_id'],
-                qty=float(values.get('product_uom_qty', False)),
-                uom=values.get('product_uom', False),
-                qty_uos=float(values.get('product_uos_qty', False)),
-                uos=values.get('product_uos', False),
-                name=values.get('name', False),
-                partner_id=order['partner_id'][0],
-                date_order=order['date_order'],
-                fiscal_position_id=order['fiscal_position_id'][0] if order['fiscal_position_id'] else False,
-                flag=False,  # Force name update
-                context=context
-            )['value']
-            if defaults.get('tax_id'):
-                defaults['tax_id'] = [[6, 0, defaults['tax_id']]]
-            values = dict(defaults, **values)
-        return super(sale_order_line, self).create(cr, uid, values, context=context)
-
-    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
-            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
-            lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position_id=False, flag=False, context=None):
-        if context is None:
-            context = {}
-        Partner = self.pool['res.partner']
-        ProductUom = self.pool['product.uom']
-        Product = self.pool['product.product']
-        ctx_product = dict(context)
-        partner = False
-        if partner_id:
-            partner = Partner.browse(cr, uid, partner_id, context=context)
-            ctx_product['lang'] = partner.lang
-            ctx_product['partner_id'] = partner_id
-        elif lang:
-            ctx_product['lang'] = lang
-
-        if not product:
-            return {'value': {'th_weight': 0,
-                    'product_uos_qty': qty}, 'domain': {'product_uom': [],
-                    'product_uos': []}}
-        if not date_order:
-            date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
-
-        result = {}
-        product_obj = Product.browse(cr, uid, product, context=ctx_product)
-
-        uom2 = False
-        if uom:
-            uom2 = ProductUom.browse(cr, uid, uom, context=context)
-            if product_obj.uom_id.category_id.id != uom2.category_id.id:
-                uom = False
-        if uos:
-            if product_obj.uos_id:
-                uos2 = ProductUom.browse(cr, uid, uos, context=context)
-                if product_obj.uos_id.category_id.id != uos2.category_id.id:
-                    uos = False
-            else:
-                uos = False
+    @api.multi
+    def _get_delivered_qty(self):
+        '''
+        Intended to be overridden in sale_stock and sale_mrp
+        :return: the quantity delivered
+        :rtype: float
+        '''
+        return 0.0
 
-        fpos = False
-        if not fiscal_position_id:
-            fpos = partner and partner.property_account_position_id or False
-        else:
-            fpos = self.pool['account.fiscal.position'].browse(cr, uid, fiscal_position_id)
-        if update_tax:  # The quantity only have changed
-            # The superuser is used by website_sale in order to create a sale order. We need to make
-            # sure we only select the taxes related to the company of the partner. This should only
-            # apply if the partner is linked to a company.
-            if uid == SUPERUSER_ID and partner.company_id:
-                taxes = product_obj.taxes_id.filtered(lambda r: r.company_id == partner.company_id)
-            else:
-                taxes = product_obj.taxes_id
-            result['tax_id'] = self.pool['account.fiscal.position'].map_tax(cr, uid, fpos, taxes)
-
-        if not flag:
-            result['name'] = Product.name_get(cr, uid, [product_obj.id], context=ctx_product)[0][1]
-            if product_obj.description_sale:
-                result['name'] += '\n'+product_obj.description_sale
-        domain = {}
-        if (not uom) and (not uos):
-            result['product_uom'] = product_obj.uom_id.id
-            if product_obj.uos_id:
-                result['product_uos'] = product_obj.uos_id.id
-                result['product_uos_qty'] = qty * product_obj.uos_coeff
-                uos_category_id = product_obj.uos_id.category_id.id
-            else:
-                result['product_uos'] = False
-                result['product_uos_qty'] = qty
-                uos_category_id = False
-            result['th_weight'] = qty * product_obj.weight
-            domain = {'product_uom':
-                        [('category_id', '=', product_obj.uom_id.category_id.id)],
-                        'product_uos':
-                        [('category_id', '=', uos_category_id)]}
-        elif uos and not uom:  # only happens if uom is False
-            result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id
-            result['product_uom_qty'] = qty_uos / product_obj.uos_coeff
-            result['th_weight'] = result['product_uom_qty'] * product_obj.weight
-        elif uom:  # whether uos is set or not
-            default_uom = product_obj.uom_id and product_obj.uom_id.id
-            q = ProductUom._compute_qty(cr, uid, uom, qty, default_uom)
-            if product_obj.uos_id:
-                result['product_uos'] = product_obj.uos_id.id
-                result['product_uos_qty'] = qty * product_obj.uos_coeff
-            else:
-                result['product_uos'] = False
-                result['product_uos_qty'] = qty
-            result['th_weight'] = q * product_obj.weight        # Round the quantity up
-
-        if not uom2:
-            uom2 = product_obj.uom_id
-
-        if pricelist and partner_id:
-            ctx = dict(
-                context,
-                uom=uom or result.get('product_uom'),
-                date=date_order,
-            )
-            price = self.pool['product.pricelist'].price_get(cr, uid, [pricelist],
-                    product, qty or 1.0, partner_id, ctx)[pricelist]
-        else:
-            price = Product.price_get(cr, uid, [product], ptype='list_price', context=ctx_product)[product] or False
-        if context.get('uom_qty_change', False):
-            result, domain = {}, {}
-        result.update({'price_unit': price})
-        return {'value': result, 'domain': domain}
-
-    def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0,
-            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
-            lang=False, update_tax=True, date_order=False, context=None):
-        context = context or {}
-        lang = lang or ('lang' in context and context['lang'])
-        if not uom:
-            return {'value': {'price_unit': 0.0, 'product_uom' : uom or False}}
-        return self.product_id_change(cursor, user, ids, pricelist, product,
-                qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name,
-                partner_id=partner_id, lang=lang, update_tax=update_tax,
-                date_order=date_order, context=context)
-
-    def unlink(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        """Allows to delete sales order lines in draft,cancel states"""
-        for rec in self.browse(cr, uid, ids, context=context):
-            if rec.state not in ['draft', 'cancel']:
-                raise UserError(_('Cannot delete a sales order line which is in state \'%s\'.') % (rec.state,))
-        return super(sale_order_line, self).unlink(cr, uid, ids, context=context)
-
-
-class mail_compose_message(osv.Model):
+
+class MailComposeMessage(models.TransientModel):
     _inherit = 'mail.compose.message'
 
-    def send_mail(self, cr, uid, ids, auto_commit=False, context=None):
-        context = context or {}
-        if context.get('default_model') == 'sale.order' and context.get('default_res_id') and context.get('mark_so_as_sent'):
-            context = dict(context, mail_post_autofollow=True)
-            self.pool.get('sale.order').signal_workflow(cr, uid, [context['default_res_id']], 'quotation_sent')
-        return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
+    @api.multi
+    def send_mail(self, auto_commit=False):
+        if self._context.get('default_model') == 'sale.order' and self._context.get('default_res_id') and self._context.get('mark_so_as_sent'):
+            order = self.env['sale.order'].browse([self._context['default_res_id']])
+            if order.state == 'draft':
+                order.state = 'sent'
+        return super(MailComposeMessage, self.with_context(mail_post_autofollow=True)).send_mail()
 
 
-class account_invoice(osv.Model):
+class AccountInvoice(models.Model):
     _inherit = 'account.invoice'
 
-    _columns = {
-        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id'),
-        'sale_ids': fields.many2many('sale.order', 'sale_order_invoice_rel', 'invoice_id', 'order_id', 'Sale Orders',
-                             readonly=True, copy=False, help="This is the list of sale orders related to this invoice. One invoice may have multiple sale orders related. "),
-    }
-
-    _defaults = {
-        'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(cr, uid, context=c),
-    }
-
-    def confirm_paid(self, cr, uid, ids, context=None):
-        sale_order_obj = self.pool.get('sale.order')
-        res = super(account_invoice, self).confirm_paid(cr, uid, ids, context=context)
-        so_ids = sale_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context)
-        for so_id in so_ids:
-            sale_order_obj.message_post(cr, uid, so_id, body=_("Invoice paid"), context=context)
+    @api.model
+    def _get_default_team(self):
+        default_team_id = self.env['crm.team']._get_default_team_id()
+        return self.env['crm.team'].browse(default_team_id)
+
+    team_id = fields.Many2one('crm.team', string='Sales Team', default=_get_default_team)
+
+    @api.multi
+    def confirm_paid(self):
+        res = super(AccountInvoice, self).confirm_paid()
+        todo = set()
+        for invoice in self:
+            for line in invoice.invoice_line_ids:
+                for sale_line in line.sale_line_ids:
+                    todo.add((sale_line.order_id, invoice.number))
+        for (order, name) in todo:
+            order.message_post(body=_("Invoice %s paid") % (name))
         return res
 
-    def unlink(self, cr, uid, ids, context=None):
-        """ Overwrite unlink method of account invoice to send a trigger to the sale workflow upon invoice deletion """
-        invoice_ids = self.search(cr, uid, [('id', 'in', ids), ('state', 'in', ['draft', 'cancel'])], context=context)
-        #if we can't cancel all invoices, do nothing
-        if len(invoice_ids) == len(ids):
-            #Cancel invoice(s) first before deleting them so that if any sale order is associated with them
-            #it will trigger the workflow to put the sale order in an 'invoice exception' state
-            for id in ids:
-                workflow.trg_validate(uid, 'account.invoice', id, 'invoice_cancel', cr)
-        return super(account_invoice, self).unlink(cr, uid, ids, context=context)
 
-
-class account_invoice_line(osv.Model):
+class AccountInvoiceLine(models.Model):
     _inherit = 'account.invoice.line'
-
-    _columns= {
-        'sale_line_ids': fields.many2many('sale.order.line', 'sale_order_line_invoice_rel', 'invoice_id', 'order_line_id',
-                                          'Sale Order Lines', readonly=True, copy=False)
-    }
+    sale_line_ids = fields.Many2many('sale.order.line', 'sale_order_line_invoice_rel', 'invoice_line_id', 'order_line_id', string='Sale Order Lines', readonly=True, copy=False)
 
 
-class procurement_order(osv.osv):
+class ProcurementOrder(models.Model):
     _inherit = 'procurement.order'
-    _columns = {
-        'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'),
-    }
-
-    def write(self, cr, uid, ids, vals, context=None):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        res = super(procurement_order, self).write(cr, uid, ids, vals, context=context)
-        from openerp import workflow
-        if vals.get('state') in ['done', 'cancel', 'exception']:
-            for proc in self.browse(cr, uid, ids, context=context):
-                if proc.sale_line_id and proc.sale_line_id.order_id:
-                    order_id = proc.sale_line_id.order_id.id
-                    if self.pool.get('sale.order').test_procurements_done(cr, uid, [order_id], context=context):
-                        workflow.trg_validate(uid, 'sale.order', order_id, 'ship_end', cr)
-                    if self.pool.get('sale.order').test_procurements_except(cr, uid, [order_id], context=context):
-                        workflow.trg_validate(uid, 'sale.order', order_id, 'ship_except', cr)
-        return res
+    sale_line_id = fields.Many2one('sale.order.line', string='Sale Order Line')
+
 
-class product_product(osv.Model):
+class ProductProduct(models.Model):
     _inherit = 'product.product'
 
-    def _sales_count(self, cr, uid, ids, field_name, arg, context=None):
-        r = dict.fromkeys(ids, 0)
+    @api.multi
+    def _sales_count(self):
+        r = {}
         domain = [
-            ('state', 'in', ['confirmed', 'done']),
-            ('product_id', 'in', ids),
+            ('state', 'in', ['sale', 'done']),
+            ('product_id', 'in', self.ids),
         ]
-        for group in self.pool['sale.report'].read_group(cr, uid, domain, ['product_id', 'product_uom_qty'], ['product_id'], context=context):
+        for group in self.env['sale.report'].read_group(domain, ['product_id', 'product_uom_qty'], ['product_id']):
             r[group['product_id'][0]] = group['product_uom_qty']
+        for product in self:
+            product.sales_count = r.get(product.id, 0)
         return r
 
-    def action_view_sales(self, cr, uid, ids, context=None):
-        result = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'sale.action_order_line_product_tree', raise_if_not_found=True)
-        result = self.pool['ir.actions.act_window'].read(cr, uid, [result], context=context)[0]
-        result['domain'] = "[('product_id','in',[" + ','.join(map(str, ids)) + "])]"
-        return result
+    sales_count = fields.Integer(compute='_sales_count', string='# Sales')
 
-    _columns = {
-        'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),
-    }
 
-class product_template(osv.Model):
+class ProductTemplate(models.Model):
     _inherit = 'product.template'
+    track_service = fields.Selection([('manual', 'Manually set quantities on order')], string='Track Service', default='manual')
 
-    def _sales_count(self, cr, uid, ids, field_name, arg, context=None):
-        res = dict.fromkeys(ids, 0)
-        for template in self.browse(cr, uid, ids, context=context):
-            res[template.id] = sum([p.sales_count for p in template.product_variant_ids])
-        return res
-
-    def action_view_sales(self, cr, uid, ids, context=None):
-        act_obj = self.pool.get('ir.actions.act_window')
-        mod_obj = self.pool.get('ir.model.data')
-        product_ids = []
-        for template in self.browse(cr, uid, ids, context=context):
-            product_ids += [x.id for x in template.product_variant_ids]
-        result = mod_obj.xmlid_to_res_id(cr, uid, 'sale.action_order_line_product_tree',raise_if_not_found=True)
-        result = act_obj.read(cr, uid, [result], context=context)[0]
-        result['domain'] = "[('product_id','in',[" + ','.join(map(str, product_ids)) + "])]"
-        return result
+    @api.multi
+    @api.depends('product_variant_ids.sales_count')
+    def _sales_count(self):
+        for product in self:
+            product.sales_count = sum([p.sales_count for p in product.product_variant_ids])
 
+    @api.multi
+    def action_view_sales(self):
+        self.ensure_one()
+        action = self.env.ref('sale.action_product_sale_list')
+        product_ids = self.product_variant_ids.ids
 
-    _columns = {
-        'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),
+        return {
+            'name': action.name,
+            'help': action.help,
+            'type': action.type,
+            'view_type': action.view_type,
+            'view_mode': action.view_mode,
+            'target': action.target,
+            'context': "{'search_default_product_id': " + ','.join(map(str, product_ids)) + ", 'default_product_id': " + str(product_ids[0]) + "}",
+            'res_model': action.res_model,
+            'domain': action.domain,
+        }
 
-    }
+    sales_count = fields.Integer(compute='_sales_count', string='# Sales')
+    invoice_policy = fields.Selection(
+        [('order', 'Ordered quantities'),
+         ('delivery', 'Delivered quantities'),
+         ('cost', 'Invoice at cost (time and material, expenses)')],
+        string='Invoicing Policy', default='order')
diff --git a/addons/sale/sale_analytic.py b/addons/sale/sale_analytic.py
new file mode 100644
index 000000000000..1910320ca8fe
--- /dev/null
+++ b/addons/sale/sale_analytic.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from openerp import api, fields, models
+
+
+class SaleOrderLine(models.Model):
+    _inherit = "sale.order.line"
+
+    @api.multi
+    def _compute_analytic(self, domain=None):
+        lines = {}
+        if not domain:
+            domain = [('so_line', 'in', self.ids), ('amount', '<', 0.0)]
+        data = self.env['account.analytic.line'].read_group(
+            domain,
+            ['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
+        )
+        for d in data:
+            if not d['product_uom_id']:
+                continue
+            line = self.browse(d['so_line'][0])
+            lines.setdefault(line, 0.0)
+            uom = self.env['product.uom'].browse(d['product_uom_id'][0])
+
+            qty = self.env['product.uom']._compute_qty_obj(uom, d['unit_amount'], line.product_uom)
+            lines[line] += qty
+
+        for line, qty in lines.items():
+            line.qty_delivered = qty
+        return True
+
+
+class AccountAnalyticLine(models.Model):
+    _inherit = "account.analytic.line"
+    so_line = fields.Many2one('sale.order.line', string='Sale Order Line')
+
+    def _get_invoice_price(self, order):
+        return abs(self.amount / self.unit_amount)
+
+    def _get_sale_order_line_vals(self):
+        order = self.env['sale.order'].search([('project_id', '=', self.account_id.id), ('state', '=', 'sale')], limit=1)
+
+        last_so_line = self.env['sale.order.line'].search([('order_id', '=', order.id)], order='sequence desc', limit=1)
+        last_sequence = last_so_line.sequence + 1 if last_so_line else 100
+
+        fpos = order.fiscal_position_id or order.partner_id.property_account_position_id
+        taxes = fpos.map_tax(self.product_id.taxes_id)
+        price = self._get_invoice_price(order)
+
+        return {
+            'order_id': order.id,
+            'name': self.name,
+            'sequence': last_sequence,
+            'price_unit': price,
+            'tax_id': [x.id for x in taxes],
+            'discount': 0.0,
+            'product_id': self.product_id.id,
+            'product_uom': self.product_uom_id.id,
+            'product_uom_qty': 0.0,
+            'qty_delivered': self.unit_amount,
+        }
+
+    def _get_sale_order_line(self, vals=None):
+        result = dict(vals or {})
+        sol = result.get('so_line', False) or self.so_line
+        if not sol and self.account_id and self.product_id and self.product_id.invoice_policy in ('cost', 'order'):
+            sol = self.env['sale.order.line'].search([
+                ('order_id.project_id', '=', self.account_id.id),
+                ('state', '=', 'sale'),
+                ('product_id', '=', self.product_id.id)],
+                limit=1)
+            result.update({'so_line': sol.id})
+
+        if not sol and self.account_id and self.product_id and self.product_id.invoice_policy == 'cost':
+            order_line_vals = self._get_sale_order_line_vals()
+            sol = self.env['sale.order.line'].create(order_line_vals)
+            sol._compute_tax_id()
+            result.update({'so_line': sol.id})
+
+        return result
+
+    @api.multi
+    def write(self, values):
+        if self._context.get('create', False):
+            return super(AccountAnalyticLine, self).write(values)
+
+        todo = self.mapped('so_line')
+        result = super(AccountAnalyticLine, self).write(values)
+        if 'so_line' in values:
+            todo |= self.mapped('so_line')
+
+        for line in self:
+            res = self._get_sale_order_line(vals=values)
+            super(AccountAnalyticLine, line).write(res)
+            if 'so_line' in res:
+                todo |= line.mapped('so_line')
+
+        todo._compute_analytic()
+        return result
+
+    @api.model
+    def create(self, values):
+        line = super(AccountAnalyticLine, self).create(values)
+        res = line._get_sale_order_line(vals=values)
+        line.with_context(create=True).write(res)
+        line.mapped('so_line')._compute_analytic()
+        return line
diff --git a/addons/sale/sale_demo.xml b/addons/sale/sale_demo.xml
index 9857cfe6ca04..2fabc306fcb3 100644
--- a/addons/sale/sale_demo.xml
+++ b/addons/sale/sale_demo.xml
@@ -25,7 +25,6 @@
             <field name="name">Laptop E5023</field>
             <field name="product_id" ref="product.product_product_25"/>
             <field name="product_uom_qty">3</field>
-            <field name="product_uos_qty">3</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">2950.00</field>
         </record>
@@ -35,7 +34,6 @@
             <field name="name">Pen drive, 16GB</field>
             <field name="product_id" ref="product.product_product_30"/>
             <field name="product_uom_qty">5</field>
-            <field name="product_uos_qty">5</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">145.00</field>
         </record>
@@ -45,7 +43,6 @@
             <field name="name">Headset USB</field>
             <field name="product_id" ref="product.product_product_33"/>
             <field name="product_uom_qty">2</field>
-            <field name="product_uos_qty">2</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">65.00</field>
         </record>
@@ -56,7 +53,6 @@
             <field name="partner_shipping_id" ref="base.res_partner_address_13"/>
             <field name="user_id" ref="base.user_root"/>
             <field name="pricelist_id" ref="product.list0"/>
-            <field name="order_policy">manual</field>
             <field name="team_id" ref="sales_team.team_sales_department"/>
             <field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
         </record>
@@ -66,7 +62,6 @@
             <field name="name">Service on demand</field>
             <field name="product_id" ref="product.product_product_1"/>
             <field name="product_uom_qty">24</field>
-            <field name="product_uos_qty">24</field>
             <field name="product_uom" ref="product.product_uom_hour"/>
             <field name="price_unit">75.00</field>
         </record>
@@ -76,7 +71,6 @@
             <field name="name">On Site Assistance</field>
             <field name="product_id" ref="product.product_product_2"/>
             <field name="product_uom_qty">30</field>
-            <field name="product_uos_qty">30</field>
             <field name="product_uom" ref="product.product_uom_hour"/>
             <field name="price_unit">38.25</field>
         </record>
@@ -87,7 +81,6 @@
             <field name="partner_shipping_id" ref="base.res_partner_4"/>
             <field name="user_id" ref="base.user_root"/>
             <field name="pricelist_id" ref="product.list0"/>
-            <field name="order_policy">manual</field>
             <field name="team_id" ref="sales_team.team_sales_department"/>
         </record>
 
@@ -96,7 +89,6 @@
             <field name="name">On Site Monitoring</field>
             <field name="product_id" ref="product.product_product_1"/>
             <field name="product_uom_qty">10</field>
-            <field name="product_uos_qty">10</field>
             <field name="product_uom" ref="product.product_uom_hour"/>
             <field name="price_unit">30.75</field>
         </record>
@@ -106,7 +98,6 @@
             <field name="name">Toner Cartridge</field>
             <field name="product_id" ref="product.product_product_39"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">70.00</field>
         </record>
@@ -125,7 +116,6 @@
             <field name="name">Service on demand</field>
             <field name="product_id" ref="product.product_product_1"/>
             <field name="product_uom_qty">16</field>
-            <field name="product_uos_qty">16</field>
             <field name="product_uom" ref="product.product_uom_hour"/>
             <field name="price_unit">75.00</field>
         </record>
@@ -135,7 +125,6 @@
             <field name="name">Webcam</field>
             <field name="product_id" ref="product.product_product_34"/>
             <field name="product_uom_qty">10</field>
-            <field name="product_uos_qty">10</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">45.00</field>
         </record>
@@ -145,7 +134,6 @@
             <field name="name">Multimedia Speakers</field>
             <field name="product_id" ref="product.product_product_31"/>
             <field name="product_uom_qty">3</field>
-            <field name="product_uos_qty">3</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">150.00</field>
         </record>
@@ -155,7 +143,6 @@
             <field name="name">Switch, 24 ports</field>
             <field name="product_id" ref="product.product_product_47"/>
             <field name="product_uom_qty">2</field>
-            <field name="product_uos_qty">2</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">70.00</field>
         </record>
@@ -175,7 +162,6 @@
             <field name="name">External Hard disk</field>
             <field name="product_id" ref="product.product_product_28"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">405.00</field>
         </record>
@@ -185,7 +171,6 @@
             <field name="name">Blank DVD-RW</field>
             <field name="product_id" ref="product.product_product_36"/>
             <field name="product_uom_qty">3</field>
-            <field name="product_uos_qty">3</field>
             <field name="product_uom" ref="product.product_uom_dozen"/>
             <field name="price_unit">24.00</field>
         </record>
@@ -195,7 +180,6 @@
             <field name="name">Printer, All-in-one</field>
             <field name="product_id" ref="product.product_product_37"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">4410.00</field>
         </record>
@@ -214,7 +198,6 @@
             <field name="name">PC Assamble + 2GB RAM</field>
             <field name="product_id" ref="product.product_product_4"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">750.00</field>
         </record>
@@ -225,9 +208,7 @@
             <field name="partner_shipping_id" ref="base.res_partner_address_11"/>
             <field name="user_id" ref="base.user_root"/>
             <field name="pricelist_id" ref="product.list0"/>
-            <field name="order_policy">manual</field>
             <field name="team_id" ref="sales_team.team_sales_department"/>
-            <field name="date_confirm" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
         </record>
 
         <record id="sale_order_line_16" model="sale.order.line">
@@ -235,7 +216,6 @@
             <field name="name">Laptop E5023</field>
             <field name="product_id" ref="product.product_product_25"/>
             <field name="product_uom_qty">5</field>
-            <field name="product_uos_qty">5</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">2950.00</field>
         </record>
@@ -245,7 +225,6 @@
             <field name="name">GrapWorks Software</field>
             <field name="product_id" ref="product.product_product_44"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">173.00</field>
         </record>
@@ -255,7 +234,6 @@
             <field name="name">Datacard</field>
             <field name="product_id" ref="product.product_product_46"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">40.00</field>
         </record>
@@ -265,7 +243,6 @@
             <field name="name">USB Adapter</field>
             <field name="product_id" ref="product.product_product_48"/>
             <field name="product_uom_qty">1</field>
-            <field name="product_uos_qty">1</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">18.00</field>
         </record>
@@ -277,7 +254,6 @@
             <field name="partner_shipping_id" ref="base.res_partner_address_25"/>
             <field name="user_id" ref="base.user_demo"/>
             <field name="pricelist_id" ref="product.list0"/>
-            <field name="order_policy">manual</field>
             <field name="team_id" ref="sales_team.crm_team_1"/>
         </record>
 
@@ -286,7 +262,6 @@
             <field name="name">Laptop Customized</field>
             <field name="product_id" ref="product.product_product_27"/>
             <field name="product_uom_qty">2</field>
-            <field name="product_uos_qty">2</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">3645.00</field>
         </record>
@@ -296,7 +271,6 @@
             <field name="name">Mouse, Wireless</field>
             <field name="product_id" ref="product.product_product_12"/>
             <field name="product_uom_qty">2</field>
-            <field name="product_uos_qty">2</field>
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">12.50</field>
         </record>
@@ -340,13 +314,14 @@ Thanks!</field>
         <!-- Demo Data for Product -->
 
         <record id="advance_product_0" model="product.product">
-            <field name="name">Advance</field>
+            <field name="name">Deposit</field>
             <field name="categ_id" ref="product.product_category_1"/>
             <field name="type">service</field>
             <field name="list_price">150.0</field>
+            <field name="invoice_policy">order</field>
             <field name="standard_price">100.0</field>
-            <field name="uom_id" ref="product.product_uom_day"/>
-            <field name="uom_po_id" ref="product.product_uom_day"/>
+            <field name="uom_id" ref="product.product_uom_unit"/>
+            <field name="uom_po_id" ref="product.product_uom_unit"/>
             <field name="company_id" eval="[]"/>
             <field name="image" type="base64" file="sale/static/img/advance_product_0-image.jpg"/>
         </record>
diff --git a/addons/sale/sale_product_demo.xml b/addons/sale/sale_product_demo.xml
new file mode 100644
index 000000000000..43ffa55a7d62
--- /dev/null
+++ b/addons/sale/sale_product_demo.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        <record id="product.product_product_59" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_58" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_57" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_56" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_55" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_54" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_53" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_52" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_51" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_50" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_49" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_48" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_47" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_46" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_45" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_44" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_43" model="product.product">
+            <field name="invoice_policy">order</field>
+        </record>
+
+        <record id="product.product_product_42" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_41" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_40" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_39" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_38" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_37" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_36" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_35" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_34" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_33" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_32" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_31" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_30" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_29" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_28" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_27" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_26" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_25" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_24" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_23" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_22" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_21" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_20" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_19" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_18" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_17" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_16" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_15" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_14" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_13" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_12" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_11b" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_11" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_10" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_9" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_8" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_7" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_6" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_5" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_5b" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_4d" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_4d" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_4c" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_4b" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_4" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_3" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_2" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_1" model="product.product">
+            <field name="invoice_policy">delivery</field>
+        </record>
+
+        <record id="product.product_product_1b" model="product.product">
+            <field name="invoice_policy">cost</field>
+        </record>
+
+        <record id="product.product_product_0" model="product.product">
+            <field name="invoice_policy">order</field>
+        </record>
+
+    </data>
+</openerp>
diff --git a/addons/sale/sale_unit_test.xml b/addons/sale/sale_unit_test.xml
index 25d7e34c5de8..2bc80d8853fa 100644
--- a/addons/sale/sale_unit_test.xml
+++ b/addons/sale/sale_unit_test.xml
@@ -17,7 +17,6 @@
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">450</field>
             <field name="product_uom_qty">2</field>
-            <field name="product_uos_qty">2</field>
         </record>
         <record id="test_order_1_line_3" model="sale.order.line">
             <field name="order_id" ref="test_order_1"/>
@@ -26,7 +25,6 @@
             <field name="product_uom" ref="product.product_uom_unit"/>
             <field name="price_unit">90</field>
             <field name="product_uom_qty">3</field>
-            <field name="product_uos_qty">3</field>
         </record>
 
         <assert id="test_order_1" model="sale.order" severity="error" string="The amount of the sales order is correctly computed">
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index 78f01b19b4c8..b60853aa9185 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -44,7 +44,7 @@
             <field name="model">sale.order</field>
             <field name="arch" type="xml">
                 <pivot string="Sales Orders">
-                    <field name="partner_id" type="row"/>
+                    <field name="date_order" type="row"/>
                     <field name="amount_total" type="measure"/>
                 </pivot>
             </field>
@@ -59,7 +59,7 @@
                     <field name="name"/>
                     <field name="partner_id"/>
                     <field name="amount_total"/>
-                    <field name="date_confirm"/>
+                    <field name="date_order"/>
                     <field name="state"/>
                     <templates>
                         <t t-name="kanban-box">
@@ -74,10 +74,10 @@
                                 </div>
                                 <div class="row">
                                     <div class="col-xs-6 text-muted">
-                                        <span><t t-esc="record.name.value"/> <t t-esc="record.date_confirm.value"/></span>
+                                        <span><t t-esc="record.name.value"/> <t t-esc="record.date_order.value"/></span>
                                     </div>
                                     <div class="col-xs-6">
-                                        <span t-attf-class="pull-right text-right label #{['draft', 'cancel'].indexOf(record.state.raw_value) > -1 ? 'label-default' : ['except_picking', 'invoice_except'].indexOf(record.state.raw_value) > -1 ? 'label-danger' : ['waiting_date', 'manual'].indexOf(record.state.raw_value) > -1 ? 'label-warning' : ['done'].indexOf(record.state.raw_value) > -1 ? 'label-success' : 'label-primary'}"><t t-esc="record.state.value"/></span>
+                                        <span t-attf-class="pull-right text-right label #{['draft', 'cancel'].indexOf(record.state.raw_value) > -1 ? 'label-default' : ['done'].indexOf(record.state.raw_value) > -1 ? 'label-success' : 'label-primary'}"><t t-esc="record.state.value"/></span>
                                     </div>
                                 </div>
                             </div>
@@ -92,14 +92,15 @@
             <field name="model">sale.order</field>
             <field name="priority">2</field>
             <field name="arch" type="xml">
-                <tree string="Sales Orders" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'" decoration-info="state in ('waiting_date','manual')" decoration-danger="state in ('invoice_except','shipping_except')">
+                <tree string="Sales Orders" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'">
                     <field name="message_needaction" invisible="1"/>
                     <field name="name" string="Order Number"/>
                     <field name="date_order"/>
                     <field name="partner_id"/>
                     <field name="user_id"/>
                     <field name="amount_total" sum="Total Tax Included" widget="monetary"/>
-                    <field name="state"/>
+                    <field name="invoice_status"/>
+                    <field name="state" invisible="1"/>
                 </tree>
             </field>
         </record>
@@ -109,7 +110,7 @@
             <field name="model">sale.order</field>
             <field name="priority">4</field>
             <field name="arch" type="xml">
-                <tree string="Quotation" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'" decoration-info="state in ('waiting_date','manual')" decoration-danger="state in ('invoice_except','shipping_except')">
+                <tree string="Quotation" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'">
                     <field name="message_needaction" invisible="1"/>
                     <field name="name" string="Quotation Number"/>
                     <field name="date_order"/>
@@ -126,22 +127,24 @@
             <field name="model">sale.order</field>
             <field name="arch" type="xml">
                 <form string="Sales Order">
-                    <header>
-                        <button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
-                        <button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
-                        <button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
-                        <button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
-                        <button name="action_quotation_send" string="Send by Email" type="object" states="sent,progress,manual" groups="base.group_user"/>
-                        <button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
-                        <button name="print_quotation" string="Print" type="object" states="sent,progress,manual" groups="base.group_user"/>
-                        <button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
-                        <button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
-                            type="action" states="manual" class="oe_highlight" groups="base.group_user"/>
-                        <button name="copy_quotation" states="cancel" string="New Copy of Quotation" type="object"/>
-                        <button name="cancel" states="draft,sent" string="Cancel" groups="base.group_user"/>
-                        <button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
-                        <button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
-                        <field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
+                <header>
+                    <button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
+                        type="action" class="btn-primary"
+                        attrs="{'invisible': [('invoice_status', '!=', 'to invoice')]}"/>
+                    <button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
+                        type="action" context="{'default_advance_payment_method': 'percentage'}"
+                        attrs="{'invisible': ['|',('invoice_status', '!=', 'no'), ('state', '!=', 'sale')]}"/>
+                    <button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="btn-primary"/>
+                    <button name="action_quotation_send" string="Send by Email" type="object" states="sent,sale"/>
+                    <button name="print_quotation" string="Print" type="object" states="draft" class="btn-primary"/>
+                    <button name="print_quotation" string="Print" type="object" states="sent,sale"/>
+                    <button name="action_confirm" states="sent" string="Confirm Sale" class="btn-primary" type="object" context="{'show_sale': True}"/>
+                    <button name="action_confirm" states="draft" string="Confirm Sale" type="object" context="{'show_sale': True}"/>
+                    <button name="action_cancel" states="draft,sent,sale" type="object" string="Cancel"/>
+                    <button name="action_draft" states="cancel" type="object" string="Set to Quotation"/>
+                    <button name="action_done" type="object" string="Set to Done" states="sale"
+                        help="If a sale order is done, you can not modify it manually anymore. But you will still be able to invoice or deliver."/>
+                    <field name="state" widget="statusbar" statusbar_visible="draft,sent,sale,done"/>
                 </header>
                 <sheet>
                     <div class="oe_button_box" name="button_box">
@@ -149,28 +152,25 @@
                             type="object"
                             class="oe_stat_button"
                             icon="fa-pencil-square-o"
-                            attrs="{'invisible': [('invoice_count', '=', 0)]}" groups="base.group_user">
+                            attrs="{'invisible': [('invoice_count', '=', 0)]}">
                             <field name="invoice_count" widget="statinfo" string="Invoices"/>
                         </button>
                     </div>
                     <div class="oe_title">
-                        <label string="Quotation" attrs="{'invisible': [('state', 'not in', ('draft', 'sent'))]}"/>
-                        <label string="Sales Order" attrs="{'invisible': [('state', 'in', ('draft', 'sent'))]}"/>
                         <h1>
                             <field name="name" class="oe_inline" readonly="1"/>
                         </h1>
                     </div>
                     <group>
                         <group>
-                            <field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
+                            <field name="partner_id" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
                             <field name="partner_invoice_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'invoice'}"/>
-                            <field name="partner_shipping_id" on_change="onchange_delivery_id(company_id, partner_id, partner_shipping_id, fiscal_position_id)" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
-                            <field name="project_id" context="{'partner_id':partner_invoice_id, 'manager_id': user_id, 'default_pricelist_id':pricelist_id, 'default_name':name, 'default_type': 'contract'}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
-                            <field name="contract_state" groups="sale.group_analytic_accounting" widget="label_selection" attrs="{'invisible': [('project_id', '=', False)]}" options="{'classes': {'template': 'default', 'draft': 'info', 'open': 'success', 'pending': 'warning', 'close': 'danger', 'cancelled': 'warning'}}" readonly="1"/>
+                            <field name="partner_shipping_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
+                            <field name="project_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}"/>
                         </group>
                         <group>
-                            <field name="date_order"/>
-                            <field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist" on_change="onchange_pricelist_id(pricelist_id,order_line)"/>
+                            <field name="date_order" invisible="1"/>
+                            <field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist"/>
                             <field name="currency_id" invisible="1"/>
                             <field name="validity_date"/>
                             <field name="payment_term_id" options="{'no_create': True}"/>
@@ -178,31 +178,35 @@
                     </group>
                     <notebook>
                         <page string="Order Lines">
-                            <field name="order_line" mode="tree,kanban">
+                            <field name="order_line" mode="tree,kanban"
+                                attrs="{'readonly': [('state', 'in', ('done','cancel'))]}">
                                 <form string="Sales Order Lines">
-                                    <header groups="base.group_user">
-                                        <button name="%(action_view_sale_order_line_make_invoice)d" states="confirmed" string="Invoice" type="action" icon="terp-document-new"/>
-                                        <field name="state" widget="statusbar" statusbar_visible="draft,confirmed,done" statusbar_colors='{"exception":"red","cancel":"red"}'/>
-                                    </header>
                                     <group>
                                         <group>
                                             <field name="product_id"
                                                 context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                                groups="base.group_user"
-                                                on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
-                                            <label for="product_uom_qty"/>
+                                                attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
+                                               />
+                                            <field name="invoice_status" invisible="1"/>
+                                            <field name="qty_to_invoice" invisible="1"/>
+                                            <field name="qty_delivered_updateable" invisible="1"/>
+                                            <label for="product_uom_qty" string="Ordered Quantity"/>
                                             <div>
                                                 <field
                                                     context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'uom_qty_change':True}"
-                                                    name="product_uom_qty" class="oe_inline"
-                                                    on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position_id, True, context)"/>
+                                                    name="product_uom_qty" class="oe_inline"/>
                                                 <field name="product_uom" groups="product.group_uom" class="oe_inline oe_no_button"
-                                                    on_change="product_uom_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, context)"/>
+                                                    attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"/>
+                                            </div>
+                                            <label for="qty_delivered" string="Delivered Quantity"
+                                                invisible="not context.get('show_sale')"/>
+                                            <div>
+                                                <field name="qty_delivered" invisible="not context.get('show_sale')"
+                                                    attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/>
                                             </div>
-                                            <label for="product_uos_qty" groups="product.group_uos"/>
-                                            <div groups="product.group_uos">
-                                                <field name="product_uos_qty" class="oe_inline"/>
-                                                <field name="product_uos" options='{"no_open": True}' class="oe_inline"/>
+                                            <label for="qty_invoiced" string="Invoiced Quantity" invisible="not context.get('show_sale')"/>
+                                            <div>
+                                                <field name="qty_invoiced" invisible="not context.get('show_sale')"/>
                                             </div>
                                             <field name="price_unit"/>
                                             <label for="discount" groups="sale.group_discount_per_so_line"/>
@@ -212,7 +216,10 @@
                                         </group>
                                         <group>
                                             <field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"/>
-                                            <field name="th_weight"/>
+                                            <label for="customer_lead"/>
+                                            <div>
+                                                <field name="customer_lead" class="oe_inline"/> days
+                                            </div>
                                         </group>
                                     </group>
                                     <label for="name"/>
@@ -221,41 +228,43 @@
                                         <label for="invoice_lines"/>
                                         <field name="invoice_lines"/>
                                     </div>
+                                    <field name="state" invisible="1"/>
                                 </form>
-                                <tree string="Sales Order Lines" editable="bottom">
+                                <tree string="Sales Order Lines" editable="bottom" decoration-info="invoice_status=='to invoice'">
                                     <field name="sequence" widget="handle"/>
-                                    <field name="state" invisible="1"/>
-                                    <field name="th_weight" invisible="1"/>
                                     <field name="product_id"
+                                        attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
                                         context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                        groups="base.group_user"
-                                        on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
+                                       />
                                     <field name="name"/>
                                     <field name="product_uom_qty"
-                                        context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                        on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position_id, True, context)"/>
+                                        string="Ordered Qty"
+                                        context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"/>
+                                    <field name="qty_delivered" invisible="not context.get('show_sale')"
+                                        attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/>
+                                    <field name="qty_invoiced"
+                                        invisible="not context.get('show_sale')"/>
+                                    <field name="qty_to_invoice" invisible="1"/>
                                     <field name="product_uom"
-                                        on_change="product_uom_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, context)"
+                                        attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
                                         groups="product.group_uom" options='{"no_open": True}'/>
-                                    <field name="product_uos_qty" groups="product.group_uos" invisible="1"/>
-                                    <field name="product_uos" string="UoS" groups="product.group_uos" invisible="1"/>
-                                    <field name="price_unit"/>
+                                    <field name="price_unit"
+                                        attrs="{'readonly': [('qty_invoiced', '&gt;', 0)]}"/>
                                     <field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"/>
                                     <field name="discount" groups="sale.group_discount_per_so_line"/>
                                     <field name="price_subtotal" widget="monetary"/>
+                                    <field name="qty_delivered_updateable" invisible="1"/>
+                                    <field name="state" invisible="1"/>
+                                    <field name="invoice_status" invisible="1"/>
                                 </tree>
                                 <kanban class="o_kanban_mobile">
                                     <field name="product_id"
                                         context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                        groups="base.group_user"
-                                        on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
+                                       />
                                     <field name="product_uom_qty"
-                                        context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                        on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position_id, True, context)"/>
+                                        context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"/>
                                     <field name="product_uom"
-                                        on_change="product_uom_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, context)"
                                         groups="product.group_uom" options='{"no_open": True}'/>
-                                    <field name="product_uos_qty" groups="product.group_uos" invisible="1"/>
                                     <field name="price_subtotal"/>
                                     <templates>
                                         <t t-name="kanban-box">
@@ -283,36 +292,36 @@
                                 <field name="amount_tax" widget='monetary' options="{'currency_field': 'currency_id'}"/>
                                 <div class="oe_subtotal_footer_separator oe_inline">
                                     <label for="amount_total" />
-                                    <button name="button_dummy"
-                                        states="draft,sent" string="(update)" type="object" class="oe_edit_only oe_link"/>
+                                    <!-- <button name="button_dummy"
+                                        states="draft,sent" string="(update)" type="object" class="oe_edit_only oe_link"/> -->
                                 </div>
                                 <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget='monetary' options="{'currency_field': 'currency_id'}"/>
                             </group>
-                            <field name="note" class="oe_inline" placeholder="An administrator can set up default Terms and conditions in your Company settings."/>
+                            <field name="note" class="oe_inline" placeholder="Setup default terms and conditions in your company settings."/>
                             <div class="oe_clear"/>
                         </page>
-                        <page string="Other Information" groups="base.group_user">
+                        <page string="Other Information">
                             <group>
-                                <group string="Sales Information" name="sales_person" groups="base.group_user">
+                                <group string="Sales Information" name="sales_person">
                                     <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice', 'base.group_sale_salesman_all_leads']}"/>
                                     <field name="team_id" options="{'no_create': True}"/>
                                     <field name="client_order_ref"/>
                                     <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
                                 </group>
                                 <group name="sale_pay" string="Invoicing">
-                                    <field name="fiscal_position_id" options="{'no_create': True}"
-                                        on_change="onchange_fiscal_position(fiscal_position_id, order_line, context)"/>
+                                    <field name="fiscal_position_id" options="{'no_create': True}"/>
+                                    <field name="invoice_status"
+                                        attrs="{'invisible': [('state', 'not in', ('sale','done'))]}"/>
                                 </group>
                                 <group string="Reporting" name="technical" groups="base.group_no_one">
                                     <field groups="base.group_no_one" name="origin"/>
-                                    <field groups="base.group_no_one" name="invoiced"/>
                                 </group>
                             </group>
                         </page>
                     </notebook>
                 </sheet>
                 <div class="oe_chatter">
-                    <field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
+                    <field name="message_follower_ids" widget="mail_followers"/>
                     <field name="message_ids" widget="mail_thread"/>
                 </div>
                 </form>
@@ -344,12 +353,13 @@
                     <field name="team_id" string="Sales Team"/>
                     <field name="project_id"/>
                     <field name="product_id"/>
-                    <filter string="My" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
+                    <filter string="My Orders" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
                     <separator/>
                     <filter string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/>
-                    <filter string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/>
-                    <filter string="To Invoice" domain="[('state','=','manual')]" help="Sales Order ready to be invoiced"/>
-                    <filter string="Done" domain="[('state','=','done')]" help="Sales Order done"/>
+                    <filter string="Sales" name="sales" domain="[('state','in',('progress','Done'))]"/>
+                    <separator/>
+                    <filter string="To Invoice" domain="[('invoice_status','=','to invoice')]"/>
+                    <filter string="Upselling" domain="[('invoice_status','=','upselling')]"/>
                     <separator/>
                     <filter string="Important Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
                     <group expand="0" string="Group By">
@@ -368,7 +378,7 @@
             <field name="view_type">form</field>
             <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>
             <field name="search_view_id" ref="view_sales_order_filter"/>
-            <field name="context">{}</field>
+            <field name="context">{'show_sale': True}</field>
             <field name="domain">[('state', 'not in', ('draft', 'sent', 'cancel'))]</field>
             <field name="help" type="html">
                 <p class="oe_view_nocontent_create">
@@ -381,28 +391,37 @@
             </field>
         </record>
 
-        <menuitem action="action_orders" id="menu_sale_order" parent="base.menu_sales" sequence="12" groups="base.group_sale_salesman,base.group_sale_manager"/>
+        <menuitem action="action_orders"
+            id="menu_sale_order" parent="base.menu_sales"
+            sequence="12" groups="base.group_sale_salesman,base.group_sale_manager"/>
 
-        <record id="action_orders_exception" model="ir.actions.act_window">
-            <field name="name">Sales in Exception</field>
+        <record id="action_orders_to_invoice" model="ir.actions.act_window">
+            <field name="name">Sales to Invoice</field>
             <field name="type">ir.actions.act_window</field>
             <field name="res_model">sale.order</field>
             <field name="view_type">form</field>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="domain">[('state','in',('shipping_except','invoice_except'))]</field>
-            <field name="filter" eval="True"/>
-            <field name="search_view_id" ref="view_sales_order_filter"/>
-        </record>
-
-        <record id="action_orders_in_progress" model="ir.actions.act_window">
-            <field name="name">Sales Order in Progress</field>
+            <field name="view_mode">tree,form,calendar,graph,pivot</field>
+            <field name="domain">[('invoice_status','=','to invoice')]</field>
+        </record>
+        <menuitem name="Invoicing"
+            id="menu_sale_invoicing" parent="base.menu_base_partner"
+            sequence="5"/>
+        <menuitem action="action_orders_to_invoice"
+            id="menu_sale_order_invoice" parent="sale.menu_sale_invoicing"
+            sequence="2"/>
+
+        <record id="action_orders_upselling" model="ir.actions.act_window">
+            <field name="name">Upselling Opportunities</field>
             <field name="type">ir.actions.act_window</field>
             <field name="res_model">sale.order</field>
             <field name="view_type">form</field>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="domain">[('state','in',('progress','waiting_date','manual'))]</field>
-            <field name="search_view_id" ref="view_sales_order_filter"/>
+            <field name="view_mode">tree,form,calendar,graph,pivot</field>
+            <field name="domain">[('invoice_status','=','upselling')]</field>
+            <field name="context">{'show_sale': True}</field>
         </record>
+        <menuitem action="action_orders_upselling"
+            id="menu_sale_order_upselling" parent="sale.menu_sale_invoicing"
+            sequence="5"/>
 
 
         <record id="action_quotations" model="ir.actions.act_window">
@@ -433,18 +452,6 @@
                 action="action_quotations" parent="base.menu_sales"
                 sequence="11"/>
 
-        <record id="action_order_tree" model="ir.actions.act_window">
-            <field name="name">Old Quotations</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.order</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form,calendar,graph</field>
-            <field name="domain">[('state','=','draft'),('date_order','&lt;',time.strftime('%Y-%m-%d %H:%M:%S'))]</field>
-            <field name="filter" eval="True"/>
-            <field name="search_view_id" ref="view_sales_order_filter"/>
-        </record>
-
-
         <record id="view_order_line_tree" model="ir.ui.view">
             <field name="name">sale.order.line.tree</field>
             <field name="model">sale.order.line</field>
@@ -454,63 +461,23 @@
                     <field name="order_id"/>
                     <field name="order_partner_id"/>
                     <field name="name"/>
+                    <field name="salesman_id"/>
                     <field name="product_uom_qty" string="Qty"/>
+                    <field name="qty_delivered"/>
+                    <field name="qty_invoiced"/>
+                    <field name="qty_to_invoice"/>
                     <field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
-                    <field name="salesman_id"/>
                     <field name="price_subtotal" sum="Total" widget="monetary"/>
-                    <field name="state"/>
-                    <field name="invoiced"/>
                 </tree>
             </field>
         </record>
-        <record id="view_order_line_form2" model="ir.ui.view">
-            <field name="name">sale.order.line.form2</field>
-            <field name="model">sale.order.line</field>
-            <field name="arch" type="xml">
-                <form string="Sales Order Lines" create="false">
-                    <header>
-                        <button name="%(action_view_sale_order_line_make_invoice)d" string="Create Invoice" type="action" attrs="{'invisible': ['|',('invoiced', '=', 1), ('state', 'not in', ('confirmed', 'draft'))]}" class="oe_highlight" groups="base.group_user"/>
-                        <button name="button_cancel" string="Cancel Line" type="object" states="confirmed,exception" groups="base.group_user"/>
-                        <button name="button_done" string="Done" type="object"  attrs="{'invisible': ['|',('invoiced', '=', 0), ('state', 'not in', ('confirmed', 'exception'))]}" class="oe_highlight" groups="base.group_user"/>
-                        <field name="state" widget="statusbar" statusbar_visible="draft,confirmed,done" statusbar_colors='{"exception":"red","cancel":"red"}'/>
-                    </header>
-                    <sheet>
-                    <label for="order_id" class="oe_edit_only"/>
-                    <h1><field name="order_id" domain="[('state','!=','done')]"/></h1>
-                    <label for="order_partner_id" class="oe_edit_only"/>
-                    <h2><field name="order_partner_id"/></h2>
-                    <group>
-                        <group>
-                            <field name="product_id"/>
-                            <label for="product_uom_qty"/>
-                            <div>
-                                <field name="product_uom_qty" readonly="1" class="oe_inline"/>
-                                <field name="product_uom" groups="product.group_uom" class="oe_inline"/>
-                            </div>
-                        </group>
-                        <group>
-                            <field name="price_unit"/>
-                            <field name="discount" groups="sale.group_discount_per_so_line"/>
-                            <field name="price_subtotal" widget="monetary"/>
-                            <field name="invoiced"/>
-                            <field name="company_id" groups="base.group_multi_company" readonly="1"/>
-                        </group>
-                    </group>
-                    <label for="name"/>
-                    <field name="name"/>
-                    </sheet>
-                </form>
-            </field>
-        </record>
 
         <record id="view_sales_order_line_filter" model="ir.ui.view">
             <field name="name">sale.order.line.select</field>
             <field name="model">sale.order.line</field>
             <field name="arch" type="xml">
                 <search string="Search Sales Order">
-                    <filter string="To Invoice" domain="[('invoiced','&lt;&gt;', 1),('state','=','done')]"  help="Sales Order Lines ready to be invoiced"/>
-                    <separator/>
-                    <filter string="Confirmed" domain="[('state', 'in', ['confirmed', 'done'])]" name="confirmed"/>
+                    <filter string="To Invoice" domain="[('qty_to_invoice','&lt;&gt;', 0)]"  help="Sales Order Lines ready to be invoiced"/>
                     <separator/>
                     <filter string="My Sales Order Lines" domain="[('salesman_id','=',uid)]" help="Sales Order Lines related to a Sales Order of mine"/>
                     <field name="order_id"/>
@@ -521,72 +488,18 @@
                         <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
                         <filter string="Order" domain="[]" context="{'group_by':'order_id'}"/>
                         <filter string="Salesperson" domain="[]" context="{'group_by':'salesman_id'}"/>
-                        <filter string="Status" domain="[]" context="{'group_by':'state'}"/>
                     </group>
                 </search>
             </field>
         </record>
 
-        <record id="view_sales_order_uninvoiced_line_filter" model="ir.ui.view">
-            <field name="name">sale.order.uninvoiced.line</field>
-            <field name="model">sale.order.line</field>
-            <field name="arch" type="xml">
-                <search string="Search Uninvoiced Lines">
-                    <filter string="To Do" domain="[('state','=','confirmed')]" name="sale order" help="Confirmed sales order lines, not yet delivered"/>
-                    <filter string="Done" domain="[('state','=','done')]" name="sale_order_done" help="Sales order lines done"/>
-                    <filter string="Shipped" domain="[('state','=','done')]" name="unshipped" help="Sales Order Lines that are in 'done' state"/>
-                    <separator/>
-                    <filter string="Uninvoiced" name="uninvoiced" domain="[('invoiced','&lt;&gt;', 1),('state','&lt;&gt;','draft'),('state','&lt;&gt;','cancel')]" help="Sales Order Lines that are confirmed, done or in exception state and haven't yet been invoiced"/>
-                    <separator/>
-                    <filter string="My Sales Order Lines" domain="[('salesman_id','=',uid)]" help="My Sales Order Lines"/>
-                    <field name="order_id"/>
-                    <field name="order_partner_id" operator="child_of"/>
-                    <field name="product_id"/>
-                    <field name="salesman_id"/>
-                    <group expand="0" string="Group By">
-                        <filter string="Order" domain="[]" context="{'group_by':'order_id'}" help="Order reference"/>
-                        <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
-                        <filter string="Status" domain="[]" context="{'group_by':'state'}"/>
-                    </group>
-                </search>
-            </field>
-        </record>
-
-        <record id="action_order_line_tree2" model="ir.actions.act_window">
-            <field name="name">Order Lines to Invoice</field>
-            <field name="type">ir.actions.act_window</field>
+        <record id="action_product_sale_list" model="ir.actions.act_window">
+            <field name="name">Sale Order Lines</field>
             <field name="res_model">sale.order.line</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree</field>
-            <field name="search_view_id" ref="view_sales_order_uninvoiced_line_filter"/>
-            <field name="context">{"search_default_uninvoiced":1}</field>
-            <field name="filter" eval="True"/>
-            <field name="help" type="html">
-              <p>
-                Here is a list of each sales order line to be invoiced. You can
-                invoice sales orders partially, by lines of sales order. You do
-                not need this list if you invoice from the delivery orders or
-                if you invoice sales totally.
-              </p>
-            </field>
+            <field name="context">{'search_default_product_id': [active_id], 'default_product_id': active_id}</field>
+            <field name="domain">[('state', 'in', ['sale', 'done'])]</field>
         </record>
 
-        <record id="action_order_line_tree3" model="ir.actions.act_window">
-            <field name="name">Uninvoiced and Delivered Lines</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.order.line</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">tree,form</field>
-            <field name="domain">[('invoiced','&lt;&gt;', 1),('state','=','done')]</field>
-            <field name="filter" eval="True"/>
-        </record>
-        <record id="action_order_line_product_tree" model="ir.actions.act_window">
-            <field name="context">{}</field><!-- force empty -->
-            <field name="name">Sales Order Lines</field>
-            <field name="res_model">sale.order.line</field>
-            <field name="view_id" ref="view_order_line_tree"/>
-            <field name="context">{'search_default_confirmed': 1}</field>
-        </record>
         <record model="ir.ui.view" id="product_form_view_sale_order_button">
             <field name="name">product.product.sale.order</field>
             <field name="model">product.product</field>
@@ -594,12 +507,13 @@
             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
             <field name="arch" type="xml">
                 <div name="button_box" position="inside">
-                    <button class="oe_stat_button" name="action_view_sales"
-                        type="object" icon="fa-usd"
+                    <button class="oe_stat_button" name="%(action_product_sale_list)d"
+                        type="action" icon="fa-usd"
                         groups="base.group_no_one">
                         <field string="Sales" name="sales_count" widget="statinfo" />
                     </button>
                 </div>
+
             </field>
         </record>
 
@@ -610,7 +524,7 @@
             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
             <field name="arch" type="xml">
                 <div name="button_box" position="inside">
-                    <button class="oe_stat_button" name="action_view_sales" 
+                    <button class="oe_stat_button" name="action_view_sales"
                         type="object" icon="fa-usd"
                         groups="base.group_no_one">
                         <field string="Sales" name="sales_count" widget="statinfo" />
@@ -619,6 +533,19 @@
             </field>
         </record>
 
+        <record model="ir.ui.view" id="product_template_form_view_invoice_policy">
+            <field name="name">product.template.invoice.policy</field>
+            <field name="model">product.template</field>
+            <field name="inherit_id" ref="product.product_template_form_view"/>
+            <field name="arch" type="xml">
+                <field name="type" position="after">
+                    <field name="invoice_policy" widget="radio"/>
+                    <field name="track_service" widget="radio" invisible="True"/>
+                </field>
+            </field>
+        </record>
+
+
 
         <record model="ir.ui.view" id="view_company_inherit_form2">
             <field name="name">res.company.form.inherit</field>
@@ -672,6 +599,7 @@
             <field name="search_view_id" ref="sale.view_sales_order_filter"/>
             <field name="domain">[('state','not in',('draft','sent','cancel'))]</field>
             <field name="context">{
+                    'show_sale': True,
                     'search_default_team_id': [active_id],
                     'default_team_id': active_id,
                 }
@@ -693,8 +621,9 @@
             <field name="view_type">form</field>
             <field name="view_mode">tree,form,calendar,graph</field>
             <field name="search_view_id" ref="sale.view_sales_order_filter"/>
-            <field name="domain">[('state', '=', 'manual')]</field>
+            <field name="domain">[('state', '=', 'sale'),('invoice_status','=','to invoice')]</field>
             <field name="context">{
+                    'show_sale': True,
                     'search_default_team_id': [active_id],
                     'default_team_id': active_id,
                 }
diff --git a/addons/sale/sale_workflow.xml b/addons/sale/sale_workflow.xml
deleted file mode 100644
index cae483cf28d7..000000000000
--- a/addons/sale/sale_workflow.xml
+++ /dev/null
@@ -1,303 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <record id="wkf_sale" model="workflow">
-            <field name="name">sale.order.basic</field>
-            <field name="osv">sale.order</field>
-            <field name="on_create">True</field>
-        </record>
-
-        <!-- Activity -->
-        <record id="act_draft" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="flow_start">True</field>
-            <field name="name">draft</field>
-        </record>
-        
-        <record id="act_sent" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">sent</field>
-            <field name="kind">function</field>
-            <field name="action">write({'state':'sent'})</field>
-        </record>
-     
-        <record id="act_router" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">router</field>
-            <field name="kind">function</field>
-            <field name="action">action_wait()</field>
-            <field name="split_mode">OR</field>
-        </record>
-        
-        <record id="act_wait_invoice" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">wait_invoice</field>
-        </record>
-
-        <record id="act_done" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">done</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">function</field>
-            <field name="action">action_done()</field>
-            <field name="join_mode">AND</field>
-        </record>
-
-        <record id="act_cancel" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">cancel</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">stopall</field>
-            <field name="action">action_cancel()</field>
-        </record>
-        
-        <record id="act_cancel2" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">cancel2</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">stopall</field>
-            <field name="action">action_cancel()</field>
-        </record>
-        
-        <record id="act_invoice" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">invoice</field>
-            <field name="kind">subflow</field>
-            <field name="subflow_id" search="[('name','=','account.invoice.basic')]"/>
-            <field name="action">action_invoice_create()</field>
-        </record>
-        <record id="act_invoice_except" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">invoice_except</field>
-            <field name="kind">function</field>
-            <field name="action">action_invoice_cancel()</field>
-        </record>
-        <record id="act_invoice_end" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">invoice_end</field>
-            <field name="kind">function</field>
-            <field name="action">action_invoice_end()</field>
-        </record>
-        <record id="act_invoice_cancel" model="workflow.activity">
-            <field name="wkf_id" ref="wkf_sale"/>
-            <field name="name">invoice_cancel</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">stopall</field>
-            <field name="action">action_cancel()</field>
-        </record>
-
-        <!-- Transistion -->
-
-        <record id="trans_draft_sent" model="workflow.transition">
-            <field name="act_from" ref="act_draft"/>
-            <field name="act_to" ref="act_sent"/>
-            <field name="signal">quotation_sent</field>
-        </record>
-        <record id="trans_draft_router" model="workflow.transition">
-            <field name="act_from" ref="act_draft"/>
-            <field name="act_to" ref="act_router"/>
-            <field name="signal">order_confirm</field>
-        </record>
-        <record id="trans_draft_cancel" model="workflow.transition">
-            <field name="act_from" ref="act_draft"/>
-            <field name="act_to" ref="act_cancel"/>
-            <field name="signal">cancel</field>
-        </record>
-        <record id="trans_sent_router" model="workflow.transition">
-            <field name="act_from" ref="act_sent"/>
-            <field name="act_to" ref="act_router"/>
-            <field name="signal">order_confirm</field>
-        </record>
-        <record id="trans_sent_cancel" model="workflow.transition">
-            <field name="act_from" ref="act_sent"/>
-            <field name="act_to" ref="act_cancel"/>
-            <field name="signal">cancel</field>
-        </record>
-        <record id="trans_router_wait_invoice" model="workflow.transition">
-            <field name="act_from" ref="act_router"/>
-            <field name="act_to" ref="act_wait_invoice"/>
-        </record>
-        <record id="trans_wait_invoice_all_lines_invoiced" model="workflow.transition">
-            <field name="act_from" ref="act_wait_invoice"/>
-            <field name="act_to" ref="act_invoice_end"/>
-            <field name="signal">all_lines</field>
-        </record>
-        <record id="trans_wait_invoice_cancel2" model="workflow.transition">
-            <field name="act_from" ref="act_wait_invoice"/>
-            <field name="act_to" ref="act_cancel2"/>
-            <field name="signal">cancel</field>
-        </record>
-        <record id="trans_wait_invoice_invoice_manual" model="workflow.transition">
-            <field name="act_from" ref="act_wait_invoice"/>
-            <field name="act_to" ref="act_invoice"/>
-            <field name="signal">manual_invoice</field>
-        </record>
-        <record id="trans_invoice_invoice_end" model="workflow.transition">
-            <field name="act_from" ref="act_invoice"/>
-            <field name="act_to" ref="act_invoice_end"/>
-            <field name="signal">subflow.paid</field>
-        </record>
-        <record id="trans_invoice_invoice_except" model="workflow.transition">
-            <field name="act_from" ref="act_invoice"/>
-            <field name="act_to" ref="act_invoice_except"/>
-            <field name="signal">subflow.cancel</field>
-        </record>
-        <record id="trans_invoice_except_invoice" model="workflow.transition">
-            <field name="act_from" ref="act_invoice_except"/>
-            <field name="act_to" ref="act_invoice"/>
-            <field name="signal">invoice_recreate</field>
-        </record>
-        <record id="trans_invoice_except_invoice_end" model="workflow.transition">
-            <field name="act_from" ref="act_invoice_except"/>
-            <field name="act_to" ref="act_invoice_end"/>
-            <field name="signal">invoice_corrected</field>
-        </record>
-        <record id="trans_invoice_except_invoice_cancel" model="workflow.transition">
-            <field name="act_from" ref="act_invoice_except"/>
-            <field name="act_to" ref="act_invoice_cancel"/>
-            <field name="signal">invoice_cancel</field>
-        </record>
-        <record id="trans_invoice_end_done" model="workflow.transition">
-            <field name="act_from" ref="act_invoice_end"/>
-            <field name="act_to" ref="act_done"/>
-        </record>
-
-    <!--
-        Procurements creation and checking branch
-    -->
-    
-        <!-- Activity -->
-        
-        <record id="act_wait_ship" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">wait_ship</field>
-        </record>
-        
-        <record id="act_cancel3" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">cancel3</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">stopall</field>
-            <field name="action">action_cancel()</field>
-        </record>
-        
-        <record id="act_ship" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">ship</field>
-            <field name="kind">function</field>
-            <field name="action">action_ship_create()</field>
-        </record>
-
-        <record id="act_ship_ignore" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">ship_ignore</field>
-            <field name="kind">function</field>
-            <field name="action">action_ignore_delivery_exception()</field>
-        </record>
-        
-        <record id="act_ship_end" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">ship_end</field>
-            <field name="kind">dummy</field>
-        </record>
-        
-        <record id="act_ship_cancel" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">ship_cancel</field>
-            <field name="flow_stop">True</field>
-            <field name="kind">stopall</field>
-            <field name="action">action_cancel()</field>
-        </record>
-        
-        <record id="act_ship_except" model="workflow.activity">
-            <field name="wkf_id" ref="sale.wkf_sale"/>
-            <field name="name">ship_except</field>
-            <field name="kind">function</field>
-            <field name="action">write({'state':'shipping_except'})</field>
-        </record>
-        
-        
-
-        <!-- Transition -->
-        
-        <record id="trans_router_wait_ship" model="workflow.transition">
-            <field name="act_from" ref="sale.act_router"/>
-            <field name="act_to" ref="act_wait_ship"/>
-        </record>
-        
-        <record id="trans_router_wait_invoice_shipping" model="workflow.transition">
-            <field name="act_from" ref="sale.act_wait_invoice"/>
-            <field name="act_to" ref="sale.act_invoice_end"/>
-            <field name="condition">(order_policy=='picking')</field>
-        </record>
-
-        <record id="trans_wait_invoice_invoice" model="workflow.transition">
-            <field name="act_from" ref="sale.act_wait_invoice"/>
-            <field name="act_to" ref="sale.act_invoice"/>
-            <field name="condition">order_policy=='prepaid'</field>
-        </record>
-        
-        <record id="trans_wait_ship_cancel3" model="workflow.transition">
-            <field name="act_from" ref="act_wait_ship"/>
-            <field name="act_to" ref="act_cancel3"/>
-            <field name="signal">cancel</field>
-        </record>
-
-        <record id="trans_wait_ship_ship" model="workflow.transition">
-            <field name="act_from" ref="act_wait_ship"/>
-            <field name="act_to" ref="act_ship"/>
-            <field name="condition">procurement_needed() and ((order_policy!='prepaid') or invoiced)</field>
-        </record>
-        <record id="trans_wait_ship_done" model="workflow.transition">
-            <field name="act_from" ref="act_wait_ship"/>
-            <field name="act_to" ref="act_ship_end"/>
-            <field name="condition">not procurement_needed()</field>
-        </record>
-        
-        <record id="trans_ship_end_done" model="workflow.transition">
-            <field name="act_from" ref="act_ship_end"/>
-            <field name="act_to" ref="sale.act_done"/>
-        </record>
-        
-        <record id="trans_ship_ship_end" model="workflow.transition">
-            <field name="act_from" ref="act_ship"/>
-            <field name="act_to" ref="act_ship_end"/>
-            <field name="signal">ship_end</field>
-            <field name="trigger_model" eval="False"/> <!-- Force empty  -->
-            <field name="trigger_expr_id" eval="False"/> <!-- Force empty -->
-            <field name="condition" eval="True"/> <!-- Force empty -->
-        </record>
-        
-        <record id="trans_ship_ship_except" model="workflow.transition">
-            <field name="act_from" ref="act_ship"/>
-            <field name="act_to" ref="act_ship_except"/>
-            <field name="signal">ship_except</field>
-            <field name="condition" eval="True"/> <!-- Force empty -->
-        </record>
-        
-        <record id="trans_ship_except_ship" model="workflow.transition">
-            <field name="act_from" ref="act_ship_except"/>
-            <field name="act_to" ref="act_ship"/>
-            <field name="signal">ship_recreate</field>
-        </record>
-        
-        <record id="trans_ship_except_ship_ignore" model="workflow.transition">
-            <field name="act_from" ref="act_ship_except"/>
-            <field name="act_to" ref="act_ship_ignore"/>
-            <field name="signal">ship_corrected</field>
-        </record>
-
-        <record id="trans_ship_ignore_ship_end" model="workflow.transition">
-            <field name="act_from" ref="act_ship_ignore"/>
-            <field name="act_to" ref="act_ship_end"/>
-        </record>
-        
-        <record id="trans_ship_except_ship_cancel" model="workflow.transition">
-            <field name="act_from" ref="act_ship_except"/>
-            <field name="act_to" ref="act_ship_cancel"/>
-            <field name="signal">ship_cancel</field>
-        </record>
-        
-
-    </data>
-</openerp>
diff --git a/addons/sale/test/cancel_order.yml b/addons/sale/test/cancel_order.yml
deleted file mode 100644
index f893706c47d0..000000000000
--- a/addons/sale/test/cancel_order.yml
+++ /dev/null
@@ -1,82 +0,0 @@
--
-  Salesman can also cancel order therefore test with that user which have salesman rights,
--
-  !context
-    uid: 'res_users_salesman'
--
-  In order to test the cancel sale order.
-  I confirm order (with at least 2 lines)
--
-  !workflow {model: sale.order, action: order_confirm, ref: sale_order_8}
-
--
-  I check state of order in 'Sale Order'.
--
-  !assert {model: sale.order, id: sale_order_8, string: Sale order should be In Progress state}:
-    - state == 'manual'  
--
-  I check that Invoice should not be created.
--
-  !python {model: sale.order}: |
-    sale_order = self.browse(cr, uid, ref("sale_order_8"))
-    assert len(sale_order.invoice_ids) == False, "Invoice should not be created."
--
-  I create an invoice for the first line
--
-   !python {model: sale.order.line.make.invoice}: |
-
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order.line', "active_ids": [ref("sale_order_line_20")], "active_id":ref("sale_order_line_20")})
-    pay_id = self.create(cr, uid, {})
-    self.make_invoices(cr, uid, [pay_id], context=ctx)
-    invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
--
-  I create an invoice for a fixed price (25% of the second line, thus 5)
--
-   !python {model: sale.advance.payment.inv}: |
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_8")], "active_id":ref("sale_order_8")})
-    pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'amount': 5, 'product_id': ref("sale_order_line_21")})
-    self.create_invoices(cr, uid, [pay_id], context=ctx)
-    invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
-
--
-  I create an invoice for the remaining and check the amount (should be the remaining amount of second line)
--
-   !python {model: sale.advance.payment.inv}: |
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_8")], "active_id":ref("sale_order_8")})
-    pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
-    self.create_invoices(cr, uid, [pay_id], context=ctx)
-    invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
-    assert len(invoice_ids) == 3, "All invoices are not created"
-    for invoice in invoice_ids:
-        assert invoice.amount_total in (7290,5,20), "Invoice total is not correct"
--
-  I cancel all the invoices.
--
-  !python {model: sale.order}: |
-    invoice_ids = self.browse(cr, uid, ref("sale_order_8")).invoice_ids
-    for invoice in invoice_ids:
-      invoice.signal_workflow('invoice_cancel')
--
-  I check order status in "Invoice Exception" and related invoice is in cancel state.
--
-  !assert {model: sale.order, id: sale_order_8, string: Sale order should be in Invoice Exception state}:
-    - state == "invoice_except", "Order should be in Invoice Exception state after cancel Invoice"
-- 
-  I click recreate invoice.
--
-  !workflow {model: sale.order, action: invoice_recreate, ref: sale_order_8}
-- 
-  I check that the invoice is correctly created with all lines.
--
-  !python {model: sale.order}: |
-    sale_order = self.browse(cr, uid, ref("sale_order_8"))
-    total_order_line = 0
-    assert len(sale_order.invoice_ids), "Invoice should be created."
-    for invoice in sale_order.invoice_ids:
-        if invoice.state != 'cancel':
-            total_order_line += len(invoice.invoice_line_ids)
-    assert total_order_line == 2, "wrong number of invoice lines, got %s" % total_order_line
-
diff --git a/addons/sale/test/canceled_lines_order.yml b/addons/sale/test/canceled_lines_order.yml
deleted file mode 100644
index dd260e021a1a..000000000000
--- a/addons/sale/test/canceled_lines_order.yml
+++ /dev/null
@@ -1,60 +0,0 @@
--
-  I create a draft Sale Order with 2 lines but 1 canceled in order to check if the canceled lines are not considered in the logic
--
-  !record {model: sale.order, id: sale_order_cl_2}:
-    partner_id: base.res_partner_3
-    partner_invoice_id: base.res_partner_address_25
-    partner_shipping_id: base.res_partner_address_25
-    pricelist_id: product.list0
-    order_policy: manual
--
-  !record {model: sale.order.line, id: sale_order_cl_2_line_1}:
-    order_id: sale_order_cl_2
-    product_id: product.product_product_27
-    product_uom_qty: 1
-    product_uom: 1
-    price_unit: 3645
-    name: 'Laptop Customized'
--
-  !record {model: sale.order.line, id: sale_order_cl_2_line_2}:
-    order_id: sale_order_cl_2
-    product_id: product.product_product_12
-    product_uom_qty: 1
-    product_uom: 1
-    price_unit: 12.50
-    name: 'Mouse, Wireless'
--
-  I cancel the first line
--
-   !python {model: sale.order.line, id: sale_order_cl_2_line_1}: |
-     self.button_cancel()
--
-  I confirm the sale order
--
-  !workflow {model: sale.order, action: order_confirm, ref: sale_order_cl_2}
--
-  Invoice the whole sale order
--
-  !python {model: sale.advance.payment.inv}: |
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order',
-                "active_ids": [ref("sale_order_cl_2")],
-                "active_id":ref("sale_order_cl_2")})
-    pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
-    self.create_invoices(cr, uid, [pay_id], context=ctx)
--
-  I check the invoice
--
-  !python {model: sale.order, id: sale_order_cl_2}: |
-    invoice = self.invoice_ids
-    assert len(invoice.invoice_line_ids) == 1, "Only 1 line should be invoiced because the other one is canceled, got %d" % len(invoice.invoice_line_ids)
--
-  I set the sale to done
--
-  !python {model: sale.order, id: sale_order_cl_2}: |
-    self.action_done()
--
-  And check if the canceled line is still canceled
--
-  !assert {model: sale.order.line, id: sale_order_cl_2_line_1, string: The canceled line should still be canceled}:
-    - state == 'cancel'
diff --git a/addons/sale/test/create_sale_users.yml b/addons/sale/test/create_sale_users.yml
deleted file mode 100644
index 0035b7675dfd..000000000000
--- a/addons/sale/test/create_sale_users.yml
+++ /dev/null
@@ -1,28 +0,0 @@
--
-   Create a user as 'Salesmanager'
--
-  !record {model: res.users, id: res_users_salesmanager, view: False}:
-    company_id: base.main_company
-    name: Sales manager
-    login: sm
-    email: salesmanager@yourcompany.com
--
-  I added groups for Salesmanager.
--
-  !record {model: res.users, id: res_users_salesmanager}:
-    groups_id:
-      - base.group_sale_manager
--
-   Create a user as 'Salesman'
--
-  !record {model: res.users, id: res_users_salesman, view: False}:
-    company_id: base.main_company
-    name: Salesman
-    login: su
-    email: salesman@yourcompany.com
--
-  I added groups for Salesman.
--
-  !record {model: res.users, id: res_users_salesman}:
-    groups_id:
-      - base.group_sale_salesman_all_leads
diff --git a/addons/sale/test/delete_order.yml b/addons/sale/test/delete_order.yml
deleted file mode 100644
index 9d8db337d5bf..000000000000
--- a/addons/sale/test/delete_order.yml
+++ /dev/null
@@ -1,19 +0,0 @@
--
-  Sales manager can only delete order therefore test with that user which have sales manager rights,
--
-  !context
-    uid: 'res_users_salesmanager'
--
-  I try to delete In progress order and check Error Message.
--
-  !python {model: sale.order}: |
-    try:
-        self.unlink(cr, uid, [ref("sale_order_7")])
-    except Exception,e:
-        pass
--
-  I make duplicate order and delete.
--
-  !python {model: sale.order}: |
-    id = self.copy(cr, uid, ref('sale_order_7'))
-    self.unlink(cr, uid, [id])
diff --git a/addons/sale/test/manual_order_policy.yml b/addons/sale/test/manual_order_policy.yml
deleted file mode 100644
index a00b0b4fada6..000000000000
--- a/addons/sale/test/manual_order_policy.yml
+++ /dev/null
@@ -1,66 +0,0 @@
--
-  I confirm the Quotation with "On Demand" order policy.
--
-  !workflow {model: sale.order, action: order_confirm, ref: sale_order_2}
--
-  I check that Invoice should not created.
--
-  !python {model: sale.order}: |
-    sale_order = self.browse(cr, uid, ref("sale_order_2"))
-    assert len(sale_order.invoice_ids) == 0, "Invoice should not created."
--
-  I create advance invoice where type is 'Fixed Price'.
--
-  !python {model: sale.advance.payment.inv}: |
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
-    order_line = self.pool.get('sale.order.line').browse(cr, uid, ref("sale_order_line_4"), context=context)
-    pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'product_id': order_line.product_id.id, 'amount': order_line.price_unit})
-    self.create_invoices(cr, uid, [pay_id], context=ctx)
--
-  I check Invoice which made advance.
--
-  !python {model: sale.order}: |
-    order = self.browse(cr, uid, ref('sale_order_2'))
-    assert order.invoice_ids, "Invoice should be created after make advance invoice."
--
-  I create advance invoice where type is 'Invoice all the Sale Order'.
--
-  !python {model: sale.advance.payment.inv}: |
-    ctx = context.copy()
-    ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
-    pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
-    self.create_invoices(cr, uid, [pay_id], context=ctx)
--
-  I check Invoice which made advance where type is 'Invoice all the Sale Order'.
--
-  !python {model: sale.order}: |
-    order = self.browse(cr, uid, ref('sale_order_2'))
-    assert order.invoice_ids, "Invoice should be created after make advance invoice where type is 'Invoice all the Sale Order'."
--
-  I open the Invoice.
--
-  !python {model: sale.order}: |
-    so = self.browse(cr, uid, ref("sale_order_2"))
-    account_invoice_obj = self.pool.get('account.invoice')
-    for invoice in so.invoice_ids:
-      invoice.signal_workflow('invoice_open')
--
-  I pay the invoice.
--
-  !python {model: account.invoice}: |
-    sale_order = self.pool.get('sale.order')
-    order = sale_order.browse(cr, uid, ref("sale_order_2"))
-    journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash'), ('company_id', '=', order.company_id.id)], limit=1)
-    import time
-    for invoice in order.invoice_ids:
-        self.pay_and_reconcile(cr, uid, [invoice.id], journal_ids[0])
--
-  I check Invoice after do manual.
--
-  !python {model: sale.order}: |
-    sale_order = self.browse(cr, uid, ref("sale_order_2"))
-    assert sale_order.invoice_ids, "Invoice should be created."
-    assert sale_order.invoice_count, "Order is not invoiced."
-    assert sale_order.invoiced, "Order is not paid."
-    assert sale_order.state == 'done', 'Order should be Done.'
diff --git a/addons/sale/test/sale_order_demo.yml b/addons/sale/test/sale_order_demo.yml
deleted file mode 100644
index 0c849a391616..000000000000
--- a/addons/sale/test/sale_order_demo.yml
+++ /dev/null
@@ -1,46 +0,0 @@
--
-  Test the data with salesman,
--
-  !context
-    uid: 'res_users_salesman'
--
-  In order to test process of the Sale Order, I create sale order
--
-  !record {model: sale.order, id: sale_order_test1}:
-    partner_id: base.res_partner_2
-    note: Invoice after delivery
-    payment_term_id: account.account_payment_term
-    order_line: 
-      - product_id: product.product_product_7
-        product_uom_qty: 8
--
-  I verify that the onchange was correctly triggered
--
-  !python {model: sale.order}: |
-    from openerp.tools import float_compare
-    order_line = self.browse(cr, uid, ref('sale.sale_order_test1')).order_line
-    assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
-    assert float_compare(order_line[0].price_unit, 79.0, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
-    assert order_line[0].product_uom_qty == 8, "The onchange function of product was not correctly triggered"
-    assert order_line[0].product_uom.id == ref('product.product_uom_unit'), "The onchange function of product was not correctly triggered"
-
--
-  I create another sale order
--
-  !record {model: sale.order, id: sale_order_test2}:
-    partner_id: base.res_partner_2
-    order_line: 
-      - product_id: product.product_product_7
-        product_uom_qty: 16
-        product_uom: product.product_uom_dozen
--
-  I verify that the onchange was correctly triggered
--
-  !python {model: sale.order}: |
-    from openerp.tools import float_compare
-    order_line = self.browse(cr, uid, ref('sale.sale_order_test2')).order_line
-    assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
-    assert float_compare(order_line[0].price_unit, 79.0 * 12, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
-    assert order_line[0].product_uom.id == ref('product.product_uom_dozen'), "The onchange function of product was not correctly triggered"
-    assert order_line[0].product_uom_qty == 16, "The onchange function of product was not correctly triggered"
-
diff --git a/addons/sale/tests/__init__.py b/addons/sale/tests/__init__.py
index 5ce968f63de2..29f17446e860 100644
--- a/addons/sale/tests/__init__.py
+++ b/addons/sale/tests/__init__.py
@@ -1,4 +1,4 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import test_sale_to_invoice
+import test_sale_to_invoice
+import test_sale_order
diff --git a/addons/sale/tests/test_sale_common.py b/addons/sale/tests/test_sale_common.py
new file mode 100644
index 000000000000..7314fb0d78b4
--- /dev/null
+++ b/addons/sale/tests/test_sale_common.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from openerp.tests import common
+
+
+class TestSale(common.TransactionCase):
+    def setUp(self):
+        super(TestSale, self).setUp()
+        # some users
+        group_manager = self.env.ref('base.group_sale_manager')
+        group_user = self.env.ref('base.group_sale_salesman')
+        self.manager = self.env['res.users'].create({
+            'name': 'Andrew Manager',
+            'login': 'manager',
+            'alias_name': 'andrew',
+            'email': 'a.m@example.com',
+            'signature': '--\nAndreww',
+            'notify_email': 'always',
+            'groups_id': [(6, 0, [group_manager.id])]
+        })
+        self.user = self.env['res.users'].create({
+            'name': 'Mark User',
+            'login': 'user',
+            'alias_name': 'mark',
+            'email': 'm.u@example.com',
+            'signature': '--\nMark',
+            'notify_email': 'always',
+            'groups_id': [(6, 0, [group_user.id])]
+        })
+        # create quotation with differend kinds of products (all possible combinations)
+        self.products = {
+            'prod_order': self.env.ref('product.product_product_43'),
+            'prod_del': self.env.ref('product.product_product_47'),
+            'serv_order': self.env.ref('product.product_product_0'),
+            'serv_del': self.env.ref('product.product_product_56'),
+        }
+
+        self.partner = self.env.ref('base.res_partner_1')
diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py
new file mode 100644
index 000000000000..0b55dacf9b26
--- /dev/null
+++ b/addons/sale/tests/test_sale_order.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from openerp.exceptions import UserError, AccessError
+
+from test_sale_common import TestSale
+
+
+class TestSaleOrder(TestSale):
+    def test_sale_order(self):
+        """ Test the sale order flow (invoicing and quantity updates)
+            - Invoice repeatedly while varrying delivered quantities and check that invoice are always what we expect
+        """
+        # DBO TODO: validate invoice and register payments
+        inv_obj = self.env['account.invoice']
+        so = self.env['sale.order'].create({
+            'partner_id': self.partner.id,
+            'partner_invoice_id': self.partner.id,
+            'partner_shipping_id': self.partner.id,
+            'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
+            'pricelist_id': self.env.ref('product.list0').id,
+        })
+        self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in self.products.iteritems()]), 'Sale: total amount is wrong')
+
+        # send quotation
+        so.force_quotation_send()
+        self.assertTrue(so.state == 'sent', 'Sale: state after sending is wrong')
+
+        # confirm quotation
+        so.action_confirm()
+        self.assertTrue(so.state == 'sale')
+        self.assertTrue(so.invoice_status == 'to invoice')
+
+        # create invoice: only 'invoice on order' products are invoiced
+        inv_id = so.action_invoice_create()
+        inv = inv_obj.browse(inv_id)
+        self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: invoice is missing lines')
+        self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in self.products.iteritems()]), 'Sale: invoice total amount is wrong')
+        self.assertTrue(so.invoice_status == 'no', 'Sale: SO status after invoicing should be "nothing to invoice"')
+        self.assertTrue(len(so.invoice_ids) == 1, 'Sale: invoice is missing')
+
+        # deliver lines except 'time and material' then invoice again
+        for line in so.order_line:
+            line.qty_delivered = 2 if line.product_id.invoice_policy in ['order', 'delivery'] else 0
+        self.assertTrue(so.invoice_status == 'to invoice', 'Sale: SO status after delivery should be "to invoice"')
+        inv_id = so.action_invoice_create()
+        inv = inv_obj.browse(inv_id)
+        self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: second invoice is missing lines')
+        self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in self.products.iteritems()]), 'Sale: second invoice total amount is wrong')
+        self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything should be "invoiced"')
+        self.assertTrue(len(so.invoice_ids) == 2, 'Sale: invoice is missing')
+        # go over the sold quantity
+        for line in so.order_line:
+            if line.product_id == self.products['serv_order']:
+                line.qty_delivered = 10
+        self.assertTrue(so.invoice_status == 'upselling', 'Sale: SO status after increasing delivered qty higher than ordered qty should be "upselling"')
+
+        # upsell and invoice
+        for line in so.order_line:
+            if line.product_id == self.products['serv_order']:
+                line.product_uom_qty = 10
+        inv_id = so.action_invoice_create()
+        inv = inv_obj.browse(inv_id)
+        self.assertEqual(len(inv.invoice_line_ids), 1, 'Sale: third invoice is missing lines')
+        self.assertEqual(inv.amount_total, 8 * self.products['serv_order'].list_price, 'Sale: second invoice total amount is wrong')
+        self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything (including the upsel) should be "invoiced"')
+
+    def test_unlink_cancel(self):
+        """ Test deleting and cancelling sale orders depending on their state and on the user's rights """
+        so = self.env['sale.order'].create({
+            'partner_id': self.partner.id,
+            'partner_invoice_id': self.partner.id,
+            'partner_shipping_id': self.partner.id,
+            'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
+            'pricelist_id': self.env.ref('product.list0').id,
+        })
+        # only quotations are deletable
+        with self.assertRaises(UserError):
+            so.action_confirm()
+            so.unlink()
+        so_copy = so.copy()
+        with self.assertRaises(AccessError):
+            so_copy.sudo(self.user).unlink()
+        self.assertTrue(so_copy.sudo(self.manager).unlink(), 'Sale: deleting a quotation should be possible')
+
+        # cancelling and setting to done, you should not be able to delete any SO ever
+        so.action_cancel()
+        self.assertTrue(so.state == 'cancel', 'Sale: cancelling SO should always be possible')
+        with self.assertRaises(UserError):
+            so.sudo(self.manager).unlink()
+        so.action_done()
+        self.assertTrue(so.state == 'done', 'Sale: SO not done')
+
+    def test_cost_invoicing(self):
+        """ Test confirming a vendor invoice to reinvoice cost on the so """
+        serv_cost = self.env.ref('product.product_product_1b')
+        prod_gap = self.env.ref('product.product_product_1')
+        so = self.env['sale.order'].create({
+            'partner_id': self.partner.id,
+            'partner_invoice_id': self.partner.id,
+            'partner_shipping_id': self.partner.id,
+            'order_line': [(0, 0, {'name': prod_gap.name, 'product_id': prod_gap.id, 'product_uom_qty': 2, 'product_uom': prod_gap.uom_id.id, 'price_unit': prod_gap.list_price})],
+            'pricelist_id': self.env.ref('product.list0').id,
+        })
+        so.action_confirm()
+        so._create_analytic_account()
+        inv_partner = self.env.ref('base.res_partner_2')
+        company = self.env.ref('base.main_company')
+        journal = self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'STPJ', 'type': 'purchase', 'company_id': company.id})
+        account_payable = self.env['account.account'].create({'code': 'X1111', 'name': 'Sale - Test Payable Account', 'user_type_id': self.env.ref('account.data_account_type_payable').id, 'reconcile': True})
+        account_income = self.env['account.account'].create({'code': 'X1112', 'name': 'Sale - Test Account', 'user_type_id': self.env.ref('account.data_account_type_direct_costs').id})
+        invoice_vals = {
+            'name': '',
+            'type': 'in_invoice',
+            'partner_id': inv_partner.id,
+            'invoice_line_ids': [(0, 0, {'name': serv_cost.name, 'product_id': serv_cost.id, 'quantity': 2, 'uom_id': serv_cost.uom_id.id, 'price_unit': serv_cost.standard_price, 'account_analytic_id': so.project_id.id, 'account_id': account_income.id})],
+            'account_id': account_payable.id,
+            'journal_id': journal.id,
+            'currency_id': company.currency_id.id,
+        }
+        inv = self.env['account.invoice'].create(invoice_vals)
+        inv.signal_workflow('invoice_open')
+        sol = so.order_line.filtered(lambda l: l.product_id == serv_cost)
+        self.assertTrue(sol, 'Sale: cost invoicing does not add lines when confirming vendor invoice')
+        self.assertTrue(sol.price_unit == 160 and sol.qty_delivered == 2 and sol.product_uom_qty == sol.qty_invoiced == 0, 'Sale: line is wrong after confirming vendor invoice')
diff --git a/addons/sale/tests/test_sale_to_invoice.py b/addons/sale/tests/test_sale_to_invoice.py
index 47b6a7b20bb5..342a490c68cb 100644
--- a/addons/sale/tests/test_sale_to_invoice.py
+++ b/addons/sale/tests/test_sale_to_invoice.py
@@ -20,7 +20,6 @@ class TestSale(TestMail):
         account_obj = self.env['account.account']
         # Usefull record id
         group_id = IrModelData.xmlid_to_res_id('account.group_account_invoice') or False
-        product_id = IrModelData.xmlid_to_res_id('product.product_category_3') or False
         company_id = IrModelData.xmlid_to_res_id('base.main_company') or False
 
         # Usefull accounts
@@ -53,13 +52,13 @@ class TestSale(TestMail):
         # In order to test I create sale order and confirmed it.
         order = self.env['sale.order'].create({
             'partner_id': partner.id,
-            'date_order': datetime.today()})
-        order_line = self.env['sale.order.line'].create({
-            'order_id': order.id,
-            'product_id': product_id})
+            'partner_invoice_id': partner.id,
+            'partner_shipping_id': partner.id,
+            'date_order': datetime.today(),
+            'pricelist_id': self.env.ref('product.list0').id})
         assert order, "Sale order will not created."
         context = {"active_model": 'sale.order', "active_ids": [order.id], "active_id": order.id}
-        order.with_context(context).action_button_confirm()
+        order.with_context(context).action_confirm()
         # Now I create invoice.
         payment = self.env['sale.advance.payment.inv'].create({'advance_payment_method': 'fixed', 'amount': 5})
         invoice = payment.with_context(context).create_invoices()
diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml
index 05cf41262a58..928d83f2a399 100644
--- a/addons/sale/views/report_saleorder.xml
+++ b/addons/sale/views/report_saleorder.xml
@@ -67,28 +67,30 @@
                     </tr>
                </thead>
                <tbody class="sale_tbody">
-                    <tr t-foreach="doc.order_line" t-as="l">
-                        <td>
-                           <span t-field="l.name"/>
-                        </td>
-                        <td class="text-right">
-                            <span t-field="l.product_uom_qty"/>
-                            <span groups="product.group_uom" t-field="l.product_uom"/>
-                        </td>
-                        <td class="text-right">
-                            <span t-field="l.price_unit"/>
-                        </td>
-                        <td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
-                            <span t-field="l.discount"/>
-                        </td>
-                        <td>
-                            <span t-esc="', '.join(map(lambda x: x.name, l.tax_id))"/>
-                        </td>
-                        <td class="text-right">
-                            <span t-field="l.price_subtotal"
-                                t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
-                        </td>
-                    </tr>
+                    <t t-foreach="doc.order_line" t-as="l">
+                        <tr t-if="l.product_uom_qty">
+                            <td>
+                               <span t-field="l.name"/>
+                            </td>
+                            <td class="text-right">
+                                <span t-field="l.product_uom_qty"/>
+                                <span groups="product.group_uom" t-field="l.product_uom"/>
+                            </td>
+                            <td class="text-right">
+                                <span t-field="l.price_unit"/>
+                            </td>
+                            <td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
+                                <span t-field="l.discount"/>
+                            </td>
+                            <td>
+                                <span t-esc="', '.join(map(lambda x: x.name, l.tax_id))"/>
+                            </td>
+                            <td class="text-right">
+                                <span t-field="l.price_subtotal"
+                                    t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
+                            </td>
+                        </tr>
+                    </t>
                 </tbody>
             </table>
 
diff --git a/addons/sale/wizard/__init__.py b/addons/sale/wizard/__init__.py
index 529f79adf42c..92be178df5ad 100644
--- a/addons/sale/wizard/__init__.py
+++ b/addons/sale/wizard/__init__.py
@@ -1,6 +1,4 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
-import sale_make_invoice
-import sale_line_invoice
 import sale_make_invoice_advance
diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py
deleted file mode 100644
index f06356c3faf0..000000000000
--- a/addons/sale/wizard/sale_line_invoice.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import osv, fields
-from openerp.tools.translate import _
-from openerp import workflow
-from openerp.exceptions import UserError
-
-class sale_order_line_make_invoice(osv.osv_memory):
-    _name = "sale.order.line.make.invoice"
-    _description = "Sale OrderLine Make_invoice"
-
-    def _prepare_invoice(self, cr, uid, order, lines, context=None):
-        a = order.partner_id.property_account_receivable_id.id
-        if order.partner_id and order.partner_id.property_payment_term_id.id:
-            pay_term = order.partner_id.property_payment_term_id.id
-        else:
-            pay_term = False
-        return {
-            'name': order.client_order_ref or '',
-            'origin': order.name,
-            'type': 'out_invoice',
-            'reference': "P%dSO%d" % (order.partner_id.id, order.id),
-            'account_id': a,
-            'partner_id': order.partner_invoice_id.id,
-            'invoice_line_ids': [(6, 0, lines)],
-            'currency_id' : order.pricelist_id.currency_id.id,
-            'comment': order.note,
-            'payment_term_id': pay_term,
-            'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
-            'user_id': order.user_id and order.user_id.id or False,
-            'company_id': order.company_id and order.company_id.id or False,
-            'date_invoice': fields.date.today(),
-            'team_id': order.team_id.id,
-        }
-
-    
-    def make_invoices(self, cr, uid, ids, context=None):
-        """
-             To make invoices.
-
-             @param self: The object pointer.
-             @param cr: A database cursor
-             @param uid: ID of the user currently logged in
-             @param ids: the ID or list of IDs
-             @param context: A standard dictionary
-
-             @return: A dictionary which of fields with values.
-
-        """
-        if context is None: context = {}
-        res = False
-        invoices = {}
-
-    #TODO: merge with sale.py/make_invoice
-        def make_invoice(order, lines):
-            """
-                 To make invoices.
-
-                 @param order:
-                 @param lines:
-
-                 @return:
-
-            """
-            inv = self._prepare_invoice(cr, uid, order, lines)
-            inv_id = self.pool.get('account.invoice').create(cr, uid, inv)
-            return inv_id
-
-        sales_order_line_obj = self.pool.get('sale.order.line')
-        sales_order_obj = self.pool.get('sale.order')
-        for line in sales_order_line_obj.browse(cr, uid, context.get('active_ids', []), context=context):
-            if (not line.invoiced) and (line.state not in ('draft', 'cancel')):
-                if not line.order_id in invoices:
-                    invoices[line.order_id] = []
-                line_id = sales_order_line_obj.invoice_line_create(cr, uid, [line.id])
-                for lid in line_id:
-                    invoices[line.order_id].append(lid)
-        for order, il in invoices.items():
-            res = make_invoice(order, il)
-            cr.execute('INSERT INTO sale_order_invoice_rel \
-                    (order_id,invoice_id) values (%s,%s)', (order.id, res))
-            sales_order_obj.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
-            flag = True
-            sales_order_obj.message_post(cr, uid, [order.id], body=_("Invoice created"), context=context)
-            data_sale = sales_order_obj.browse(cr, uid, order.id, context=context)
-            for line in data_sale.order_line:
-                if not line.invoiced and line.state != 'cancel':
-                    flag = False
-                    break
-            if flag:
-                line.order_id.write({'state': 'progress'})
-                workflow.trg_validate(uid, 'sale.order', order.id, 'all_lines', cr)
-
-        if not invoices:
-            raise UserError(_('Invoice cannot be created for this Sales Order Line due to one of the following reasons:\n1.The state of this sales order line is either "draft" or "cancel"!\n2.The Sales Order Line is Invoiced!'))
-        if context.get('open_invoices', False):
-            return self.open_invoices(cr, uid, ids, res, context=context)
-        return {'type': 'ir.actions.act_window_close'}
-
-    def open_invoices(self, cr, uid, ids, invoice_ids, context=None):
-        """ open a view on one of the given invoice_ids """
-        ir_model_data = self.pool.get('ir.model.data')
-        form_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_form')
-        form_id = form_res and form_res[1] or False
-        tree_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_tree')
-        tree_id = tree_res and tree_res[1] or False
-
-        return {
-            'name': _('Invoice'),
-            'view_type': 'form',
-            'view_mode': 'form,tree',
-            'res_model': 'account.invoice',
-            'res_id': invoice_ids,
-            'view_id': False,
-            'views': [(form_id, 'form'), (tree_id, 'tree')],
-            'context': {'type': 'out_invoice'},
-            'type': 'ir.actions.act_window',
-        }
diff --git a/addons/sale/wizard/sale_line_invoice.xml b/addons/sale/wizard/sale_line_invoice.xml
deleted file mode 100644
index 148810992c2d..000000000000
--- a/addons/sale/wizard/sale_line_invoice.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <record id="view_sale_order_line_make_invoice" model="ir.ui.view">
-            <field name="name">Sales OrderLine Make Invoice</field>
-            <field name="model">sale.order.line.make.invoice</field>
-            <field name="arch" type="xml">
-             <form string="Create invoices">
-                <p class="oe_grey">
-                    All items in these order lines will be invoiced. You can also invoice a percentage of the sales order
-                    or a fixed price (for advances) directly from the sales order form if you prefer.
-                </p>
-                <footer>
-                    <button name="make_invoices" string="Create &amp; View Invoice" type="object"
-                        context="{'open_invoices': True}" class="oe_highlight"/>
-                    <button name="make_invoices" string="Create Invoices" type="object" class="btn-primary"/>
-                    <button string="Cancel" class="btn-default" special="cancel" />
-                </footer>
-            </form>
-            </field>
-        </record>
-
-        <record id="action_view_sale_order_line_make_invoice" model="ir.actions.act_window">
-            <field name="name">Create Invoice</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.order.line.make.invoice</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">form</field>
-            <field name="view_id" ref="view_sale_order_line_make_invoice"/>
-            <field name="target">new</field>
-        </record>
-
-        <record model="ir.values" id="sale_order_line_make_invoice">
-            <field name="model_id" ref="sale.model_sale_order_line" />
-            <field name="name">Make Invoices</field>
-            <field name="key2">client_action_multi</field>
-            <field name="value" eval="'ir.actions.act_window,' + str(ref('action_view_sale_order_line_make_invoice'))" />
-            <field name="key">action</field>
-            <field name="model">sale.order.line</field>
-        </record>
-    </data>
-</openerp>
diff --git a/addons/sale/wizard/sale_make_invoice.py b/addons/sale/wizard/sale_make_invoice.py
deleted file mode 100644
index fd1427d45951..000000000000
--- a/addons/sale/wizard/sale_make_invoice.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-
-class sale_make_invoice(osv.osv_memory):
-    _name = "sale.make.invoice"
-    _description = "Sales Make Invoice"
-    _columns = {
-        'grouped': fields.boolean('Group the invoices', help='Check the box to group the invoices for the same customers'),
-        'invoice_date': fields.date('Invoice Date'),
-    }
-    _defaults = {
-        'grouped': False,
-        'invoice_date': fields.date.context_today,
-    }
-
-    def view_init(self, cr, uid, fields_list, context=None):
-        if context is None:
-            context = {}
-        record_id = context and context.get('active_id', False)
-        order = self.pool.get('sale.order').browse(cr, uid, record_id, context=context)
-        if order.state == 'draft':
-            raise UserError(_('You cannot create invoice when sales order is not confirmed.'))
-        return False
-
-    def make_invoices(self, cr, uid, ids, context=None):
-        order_obj = self.pool.get('sale.order')
-        mod_obj = self.pool.get('ir.model.data')
-        act_obj = self.pool.get('ir.actions.act_window')
-        newinv = []
-        if context is None:
-            context = {}
-        data = self.read(cr, uid, ids)[0]
-        for sale_order in order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
-            if sale_order.state != 'manual':
-                raise UserError(_("You shouldn't manually invoice the following sale order %s") % (sale_order.name))
-
-        order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_invoice=data['invoice_date'])
-        orders = order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context)
-        for o in orders:
-            for i in o.invoice_ids:
-                newinv.append(i.id)
-        # Dummy call to workflow, will not create another invoice but bind the new invoice to the subflow
-        order_obj.signal_workflow(cr, uid, [o.id for o in orders if o.order_policy == 'manual'], 'manual_invoice')
-        result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
-        id = result and result[1] or False
-        result = act_obj.read(cr, uid, [id], context=context)[0]
-        result['domain'] = "[('id','in', [" + ','.join(map(str, newinv)) + "])]"
-
-        return result
diff --git a/addons/sale/wizard/sale_make_invoice.xml b/addons/sale/wizard/sale_make_invoice.xml
deleted file mode 100644
index 4beaadde2092..000000000000
--- a/addons/sale/wizard/sale_make_invoice.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <record id="view_sale_order_make_invoice" model="ir.ui.view">
-            <field name="name">Create invoices</field>
-            <field name="model">sale.make.invoice</field>
-            <field name="arch" type="xml">
-                <form string="Create invoices">
-                    <separator colspan="4" string="Do you really want to create the invoice(s)?" />
-                    <group>
-                        <field name="grouped"/>
-                        <field name="invoice_date"/>
-                    </group>
-                    <footer>
-                        <button name="make_invoices" string="Create Invoices" type="object" class="btn-primary"/>
-                        <button string="Cancel" class="btn-default" special="cancel" />
-                    </footer>
-               </form>
-            </field>
-        </record>
-
-        <record id="action_sale_order_make_invoice" model="ir.actions.act_window">
-            <field name="name">Make Invoices</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">sale.make.invoice</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">form</field>
-            <field name="view_id" ref="view_sale_order_make_invoice"/>
-            <field name="target">new</field>
-            <field name="multi">True</field>
-        </record>
-
-        <record model="ir.values" id="sale_order_make_invoice">
-            <field name="model_id" ref="sale.model_sale_order" />
-            <field name="name">Make Invoices</field>
-            <field name="key2">client_action_multi</field>
-            <field name="value" eval="'ir.actions.act_window,' + str(ref('action_sale_order_make_invoice'))" />
-            <field name="key">action</field>
-            <field name="model">sale.order</field>
-        </record>
-    </data>
-</openerp>
diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py
index add69e5839a5..d79b0d4f3aab 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.py
+++ b/addons/sale/wizard/sale_make_invoice_advance.py
@@ -1,197 +1,135 @@
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
+import time
+
+from openerp import api, fields, models, _
 import openerp.addons.decimal_precision as dp
 from openerp.exceptions import UserError
 
-class sale_advance_payment_inv(osv.osv_memory):
+class SaleAdvancePaymentInv(models.TransientModel):
     _name = "sale.advance.payment.inv"
     _description = "Sales Advance Payment Invoice"
 
-    _columns = {
-        'advance_payment_method':fields.selection(
-            [('all', 'Invoice the whole sales order'), ('percentage','Percentage'), ('fixed','Fixed price (deposit)'),
-                ('lines', 'Some order lines')],
-            'What do you want to invoice?', required=True,
-            help="""Use Invoice the whole sale order to create the final invoice.\nUse Percentage to invoice a percentage of the total amount.\nUse Fixed Price to invoice a specific amount in advance.\nUse Some Order Lines to invoice a selection of the sales order lines."""),
-        'qtty': fields.float('Quantity', digits=(16, 2), required=True),
-        'product_id': fields.many2one('product.product', 'Advance Product',
-            domain=[('type', '=', 'service')],
-            help="Select a product of type service which is called 'Advance Product'.\nYou may have to create it and set it as a default value on this field."),
-        'amount': fields.float('Advance Amount', digits=0,
-            help="The amount to be invoiced in advance. \nTaxes are not taken into account for advance invoices."),
-    }
-
-    def _get_advance_product(self, cr, uid, context=None):
+    @api.model
+    def _count(self):
+        return len(self._context.get('active_ids', []))
+
+    @api.model
+    def _get_advance_payment_method(self):
+        if self._count() == 1:
+            sale_obj = self.env['sale.order']
+            order = sale_obj.browse(self._context.get('active_ids'))[0]
+            if all([line.product_id.invoice_policy == 'order' for line in order.order_line]):
+                return 'all'
+        return 'delivered'
+
+    @api.model
+    def _get_advance_product(self):
         try:
-            product = self.pool.get('ir.model.data').get_object(cr, uid, 'sale', 'advance_product_0')
+            return self.env['ir.model.data'].xmlid_to_res_id('sale.advance_product_0', raise_if_not_found=True)
         except ValueError:
-            # a ValueError is returned if the xml id given is not found in the table ir_model_data
             return False
-        return product.id
-
-    _defaults = {
-        'advance_payment_method': 'all',
-        'qtty': 1.0,
-        'product_id': _get_advance_product,
-    }
-
-    def _translate_advance(self, cr, uid, percentage=False, context=None):
-        return _("Advance of %s %%") if percentage else _("Advance of %s %s")
-
-    def onchange_method(self, cr, uid, ids, advance_payment_method, product_id, context=None):
-        if advance_payment_method == 'percentage':
-            return {'value': {'amount':0, 'product_id':False }}
-        if product_id:
-            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
-            return {'value': {'amount': product.list_price}}
-        return {'value': {'amount': 0}}
-
-    def _prepare_advance_invoice_vals(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        sale_obj = self.pool.get('sale.order')
-        ir_property_obj = self.pool.get('ir.property')
-        fiscal_obj = self.pool.get('account.fiscal.position')
-        inv_line_obj = self.pool.get('account.invoice.line')
-        invoice_obj = self.pool.get('account.invoice')
-        wizard = self.browse(cr, uid, ids[0], context)
-        sale_ids = context.get('active_ids', [])
-
-        result = []
-        for sale in sale_obj.browse(cr, uid, sale_ids, context=context):
-            new_invoice = invoice_obj.new(cr, uid, {
-                                'invoice_line_ids':[(0, 0, {'product_id': wizard.product_id.id})],
-                                'partner_id': sale.partner_id.id,
-                                'fiscal_position_id': sale.fiscal_position_id.id,
-                                'type': 'out_invoice',
-                            })
-            inv_line = new_invoice.invoice_line_ids[0]
-            inv_line.invoice_id = new_invoice #Little hack to in order to old <-> new api
-            inv_line._onchange_product_id()
-
-            res = inv_line._convert_to_write(inv_line._cache)
-
-            # determine and check income account
-            if not wizard.product_id.id :
-                prop = ir_property_obj.get(cr, uid,
-                            'property_account_income_categ_id', 'product.category', context=context)
-                prop_id = prop and prop.id or False
-                account_id = fiscal_obj.map_account(cr, uid, sale.fiscal_position_id or False, prop_id)
-                if not account_id:
-                    raise UserError(
-                            _('There is no income account defined as global property.'))
-                res['account_id'] = account_id
-            if not res.get('account_id'):
-                raise UserError(
-                        _('There is no income account defined for this product: "%s" (id:%d).') % \
-                            (wizard.product_id.name, wizard.product_id.id,))
 
-            # determine invoice amount
-            if wizard.amount <= 0.00:
-                raise UserError(_('The value of Advance Amount must be positive.'))
-            if wizard.advance_payment_method == 'percentage':
-                inv_amount = sale.amount_untaxed * wizard.amount / 100
-                if not res.get('name'):
-                    res['name'] = self._translate_advance(cr, uid, percentage=True, context=dict(context, lang=sale.partner_id.lang)) % (wizard.amount)
-            else:
-                inv_amount = wizard.amount
-                if not res.get('name'):
-                    #TODO: should find a way to call formatLang() from rml_parse
-                    symbol = sale.pricelist_id.currency_id.symbol
-                    if sale.pricelist_id.currency_id.position == 'after':
-                        symbol_order = (inv_amount, symbol)
-                    else:
-                        symbol_order = (symbol, inv_amount)
-                    res['name'] = self._translate_advance(cr, uid, context=dict(context, lang=sale.partner_id.lang)) % symbol_order
-
-            # create the invoice
-            inv_line_values = {
-                'name': res.get('name'),
-                'origin': sale.name,
-                'account_id': res['account_id'],
-                'price_unit': inv_amount,
-                'quantity': wizard.qtty or 1.0,
-                'discount': False,
-                'uos_id': res.get('uos_id', False),
-                'product_id': wizard.product_id.id,
-                'invoice_line_tax_ids': res.get('invoice_line_tax_ids'),
-                'account_analytic_id': sale.project_id.id or False,
-            }
-            inv_values = {
-                'name': sale.client_order_ref or sale.name,
-                'origin': sale.name,
-                'type': 'out_invoice',
-                'reference': False,
-                'account_id': sale.partner_id.property_account_receivable_id.id,
-                'partner_id': sale.partner_invoice_id.id,
-                'invoice_line_ids': [(0, 0, inv_line_values)],
-                'currency_id': sale.pricelist_id.currency_id.id,
-                'comment': sale.note,
-                'payment_term_id': sale.payment_term_id.id,
-                'fiscal_position_id': sale.fiscal_position_id.id or sale.partner_id.property_account_position_id.id,
-                'team_id': sale.team_id.id,
-            }
-            result.append((sale.id, inv_values))
-        return result
-
-    def _create_invoices(self, cr, uid, inv_values, sale_id, context=None):
-        inv_obj = self.pool.get('account.invoice')
-        sale_obj = self.pool.get('sale.order')
-        inv_id = inv_obj.create(cr, uid, inv_values, context=context)
-        inv_obj.compute_taxes(cr, uid, [inv_id], context=context)
-        # add the invoice to the sales order's invoices
-        sale_obj.write(cr, uid, sale_id, {'invoice_ids': [(4, inv_id)]}, context=context)
-        return inv_id
-
-    def create_invoices(self, cr, uid, ids, context=None):
-        """ create invoices for the active sales orders """
-        sale_obj = self.pool.get('sale.order')
-        act_window = self.pool.get('ir.actions.act_window')
-        wizard = self.browse(cr, uid, ids[0], context)
-        sale_ids = context.get('active_ids', [])
-        if wizard.advance_payment_method == 'all':
-            # create the final invoices of the active sales orders
-            res = sale_obj.manual_invoice(cr, uid, sale_ids, context)
-            if context.get('open_invoices', False):
-                return res
-            return {'type': 'ir.actions.act_window_close'}
-
-        if wizard.advance_payment_method == 'lines':
-            # open the list view of sales order lines to invoice
-            res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context)
-            res['context'] = {
-                'search_default_uninvoiced': 1
-            }
-            res['domain'] = [('order_id','=', sale_ids and sale_ids[0] or False)]
-            return res
-        assert wizard.advance_payment_method in ('fixed', 'percentage')
-
-        inv_ids = []
-        for sale_id, inv_values in self._prepare_advance_invoice_vals(cr, uid, ids, context=context):
-            inv_ids.append(self._create_invoices(cr, uid, inv_values, sale_id, context=context))
-
-        if context.get('open_invoices', False):
-            return self.open_invoices( cr, uid, ids, inv_ids, context=context)
+    advance_payment_method = fields.Selection([
+        ('delivered', 'Ready to invoice'),
+        ('all', 'Ready to invoice (with refunds)'),
+        ('percentage', 'Deposit (percentage)'),
+        ('fixed', 'Deposit (fixed amount)')
+        ], string='What do you want to invoice?', default=_get_advance_payment_method, required=True)
+    product_id = fields.Many2one('product.product', string='Deposit Product', domain=[('type', '=', 'service')], default=_get_advance_product)
+    count = fields.Integer(default=_count, string='# of Orders')
+    amount = fields.Float('Deposit Amount', digits=dp.get_precision('Account'), help="The amount to be invoiced in advance, taxes excluded.")
+
+    @api.onchange('advance_payment_method')
+    def onchange_advance_payment_method(self):
+        if self.advance_payment_method == 'percentage':
+            return {'value': {'amount':0, 'product_id':False}}
+        return {}
+
+    @api.multi
+    def _create_invoice(self, order, so_line, amount):
+        inv_obj = self.env['account.invoice']
+        ir_property_obj = self.env['ir.property']
+
+        account_id = False
+        if self.product_id.id:
+            account_id = self.product_id.property_account_income_id.id
+        if not account_id:
+            prop = ir_property_obj.get('property_account_income_categ_id', 'product.category')
+            prop_id = prop and prop.id or False
+            account_id = order.fiscal_position_id.map_account(prop_id)
+        if not account_id:
+            raise UserError(
+                _('There is no income account defined for this product: "%s". You may have to install a chart of account from Accounting app, settings menu.') % \
+                    (self.product_id.name,))
+
+        if self.amount <= 0.00:
+            raise UserError(_('The value of Advance Amount must be positive.'))
+        if self.advance_payment_method == 'percentage':
+            amount = order.amount_untaxed * self.amount / 100
+            name = _("Advance of %s%%") % (self.amount,)
+        else:
+            amount = self.amount
+            name = _('Advance')
+
+        invoice = inv_obj.create({
+            'name': order.client_order_ref or order.name,
+            'origin': order.name,
+            'type': 'out_invoice',
+            'reference': False,
+            'account_id': order.partner_id.property_account_receivable_id.id,
+            'partner_id': order.partner_invoice_id.id,
+            'invoice_line_ids': [(0, 0, {
+                'name': name,
+                'origin': order.name,
+                'account_id': account_id,
+                'price_unit': amount,
+                'quantity': 1.0,
+                'discount': 0.0,
+                'uom_id': self.product_id.uom_id.id,
+                'product_id': self.product_id.id,
+                'sale_line_ids': [(6, 0, [so_line.id])],
+                'invoice_line_tax_ids': [(6, 0, [x.id for x in self.product_id.taxes_id])],
+                'account_analytic_id': order.project_id.id or False,
+            })],
+            'currency_id': order.pricelist_id.currency_id.id,
+            'payment_term_id': order.payment_term_id.id,
+            'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
+            'team_id': order.team_id.id,
+        })
+        invoice.compute_taxes()
+        return invoice
+
+    @api.multi
+    def create_invoices(self):
+        sale_orders = self.env['sale.order'].browse(self._context.get('active_ids', []))
+
+        if self.advance_payment_method == 'delivered':
+            sale_orders.action_invoice_create()
+        elif self.advance_payment_method == 'all':
+            sale_orders.action_invoice_create(final=True)
+        else:
+            sale_line_obj = self.env['sale.order.line']
+            for order in sale_orders:
+                if self.advance_payment_method == 'percentage':
+                    amount = order.amount_untaxed * self.amount / 100
+                else:
+                    amount = self.amount
+                if self.product_id.invoice_policy != 'order':
+                    raise UserError(_('The product used to invoice a deposit should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'))
+                if self.product_id.type != 'service':
+                    raise UserError(_("The product used to invoice an deposit should be of type 'Service'. Please use another product or update this product."))
+                so_line = sale_line_obj.create({
+                    'name': _('Advance: %s') % (time.strftime('%m %Y'),),
+                    'price_unit': amount,
+                    'product_uom_qty': 0.0,
+                    'order_id': order.id,
+                    'discount': 0.0,
+                    'product_uom': self.product_id.uom_id.id,
+                    'product_id': self.product_id.id,
+                    'tax_id': self.product_id.taxes_id,
+                })
+                self._create_invoice(order, so_line, amount)
+        if self._context.get('open_invoices', False):
+            return sale_orders.action_view_invoice()
         return {'type': 'ir.actions.act_window_close'}
-
-    def open_invoices(self, cr, uid, ids, invoice_ids, context=None):
-        """ open a view on one of the given invoice_ids """
-        ir_model_data = self.pool.get('ir.model.data')
-        form_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_form')
-        form_id = form_res and form_res[1] or False
-        tree_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_tree')
-        tree_id = tree_res and tree_res[1] or False
-
-        return {
-            'name': _('Advance Invoice'),
-            'view_type': 'form',
-            'view_mode': 'form,tree',
-            'res_model': 'account.invoice',
-            'res_id': invoice_ids[0],
-            'view_id': False,
-            'views': [(form_id, 'form'), (tree_id, 'tree')],
-            'context': "{'type': 'out_invoice'}",
-            'type': 'ir.actions.act_window',
-        }
diff --git a/addons/sale/wizard/sale_make_invoice_advance.xml b/addons/sale/wizard/sale_make_invoice_advance.xml
index 4822fdf43d1c..cbfa1fa5cbe2 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.xml
+++ b/addons/sale/wizard/sale_make_invoice_advance.xml
@@ -2,23 +2,21 @@
 <openerp>
     <data>
         <record id="view_sale_advance_payment_inv" model="ir.ui.view">
-            <field name="name">Invoice Order</field>
+            <field name="name">Invoice Orders</field>
             <field name="model">sale.advance.payment.inv</field>
             <field name="arch" type="xml">
                 <form string="Invoice Sales Order">
                     <p class="oe_grey">
-                        Select how you want to invoice this order. This
-                        will create a draft invoice that can be modified
-                        before validation.
+                        Invoices will be created in draft so that you can update
+                        them before validation.
                     </p>
                     <group>
+                        <field name="count" invisible="[('count','=',1)]" readonly="True"/>
                         <field name="advance_payment_method" class="oe_inline" widget="radio"
-                            on_change="onchange_method(advance_payment_method, product_id)"/>
-                        <field name="qtty" invisible="1"/>
+                            attrs="{'invisible': [('count','&gt;',1)]}"/>
                         <field name="product_id"
-                            on_change="onchange_method(advance_payment_method, product_id)"
-                            context="{'search_default_services': 1}"
-                            attrs="{'invisible': [('advance_payment_method','!=','fixed')]}"/>
+                            context="{'search_default_services': 1, 'default_type': 'service', 'default_invoice_policy': 'order'}" class="oe_inline"
+                            attrs="{'invisible': [('advance_payment_method','not in', ('fixed','percentage'))]}"/>
                         <label for="amount" attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"/>
                         <div attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}">
                             <field name="amount"
@@ -27,19 +25,12 @@
                                 attrs="{'invisible': [('advance_payment_method', '!=', 'percentage')]}" class="oe_inline"/>
                         </div>
                     </group>
-                    <div>
-                        <b><label string="After clicking 'Show Lines to Invoice', select lines to invoice and create the invoice from the 'More' dropdown menu." attrs="{'invisible': [('advance_payment_method', '!=', 'lines')]}"/></b>
-                    </div>
                     <footer>
-                        <button name="create_invoices" string="Create and View Invoice" type="object"
-                            context="{'open_invoices': True}" class="btn-primary"
-                            attrs="{'invisible': [('advance_payment_method', '=', 'lines')]}"/>
-                        <button name="create_invoices" string="Create Invoice" type="object"
-                            class="btn-primary"
-                            attrs="{'invisible': [('advance_payment_method', '=', 'lines')]}"/>
-                        <button name="create_invoices" string="Show Lines to Invoice" type="object"
-                            class="btn-primary"
-                            attrs="{'invisible': [('advance_payment_method', '!=', 'lines')]}"/>
+                        <button name="create_invoices" string="Create and View Invoices" type="object"
+                            context="{'open_invoices': True}" class="btn-primary"/>
+                        <button name="create_invoices" string="Create Invoices" type="object"
+                            class="btn-primary"/>
+                        or
                         <button string="Cancel" class="btn-default" special="cancel"/>
                     </footer>
                 </form>
@@ -54,5 +45,16 @@
             <field name="view_mode">form</field>
             <field name="target">new</field>
         </record>
+
+        <!-- TODO: check if we need this -->
+        <record model="ir.values" id="sale_order_line_make_invoice">
+            <field name="model_id" ref="sale.model_sale_order_line" />
+            <field name="name">Invoice Orders</field>
+            <field name="key2">client_action_multi</field>
+            <field name="value" eval="'ir.actions.act_window,' + str(ref('action_view_sale_advance_payment_inv'))" />
+            <field name="key">action</field>
+            <field name="model">sale.order</field>
+        </record>
+
     </data>
 </openerp>
-- 
GitLab