[Merge] Merge with trunk addons
[odoo/odoo.git] / addons / sale / sale.py
index 3e3b19b..e684a1a 100644 (file)
@@ -2,7 +2,7 @@
 ##############################################################################
 #
 #    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
 import time
 import netsvc
 from osv import fields, osv
-from mx import DateTime
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
 from tools import config
 from tools.translate import _
 
+import decimal_precision as dp
+
 
 class sale_shop(osv.osv):
     _name = "sale.shop"
@@ -40,16 +43,20 @@ class sale_shop(osv.osv):
 sale_shop()
 
 
-def _incoterm_get(self, cr, uid, context={}):
+def _incoterm_get(self, cr, uid, context=None):
+    if context is None:
+            context = {}
     cr.execute('select code, code||\', \'||name from stock_incoterms where active')
     return cr.fetchall()
 
-
 class sale_order(osv.osv):
     _name = "sale.order"
+    _log_create = True
     _description = "Sale Order"
 
-    def copy(self, cr, uid, id, default=None, context={}):
+    def copy(self, cr, uid, id, default=None, context=None):
+        if context is None:
+            context = {}
         if not default:
             default = {}
         default.update({
@@ -59,18 +66,22 @@ class sale_order(osv.osv):
             'picking_ids': [],
             'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order'),
         })
-        return super(sale_order, self).copy(cr, uid, id, default, context)
+        return super(sale_order, self).copy(cr, uid, id, default, context=context)
 
-    def _amount_line_tax(self, cr, uid, line, context={}):
+    def _amount_line_tax(self, cr, uid, line, context=None):
+        if context is None:
+            context = {}
         val = 0.0
         for c in self.pool.get('account.tax').compute(cr, uid, line.tax_id, line.price_unit * (1-(line.discount or 0.0)/100.0), line.product_uom_qty, line.order_id.partner_invoice_id.id, line.product_id, line.order_id.partner_id):
             val += c['amount']
         return val
 
-    def _amount_all(self, cr, uid, ids, field_name, arg, context):
+    def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
         cur_obj = self.pool.get('res.currency')
-        for order in self.browse(cr, uid, ids):
+        for order in self.browse(cr, uid, ids, context):
             res[order.id] = {
                 'amount_untaxed': 0.0,
                 'amount_tax': 0.0,
@@ -80,31 +91,34 @@ class sale_order(osv.osv):
             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)
+                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 _picked_rate(self, cr, uid, ids, name, arg, context=None):
+        if context is None:
+            context = {}
         if not ids:
             return {}
         res = {}
         for id in ids:
             res[id] = [0.0, 0.0]
         cr.execute('''SELECT
-                p.sale_id,sum(m.product_qty), m.state
+                p.sale_id,sum(m.product_qty), mp.state as mp_state
             FROM
                 stock_move m
             LEFT JOIN
                 stock_picking p on (p.id=m.picking_id)
+            LEFT JOIN
+                mrp_procurement mp on (mp.move_id=m.id)
             WHERE
-                p.sale_id in ('''+','.join(map(str, ids))+''')
-            GROUP BY m.state, p.sale_id''')
-        for oid, nbr, state in cr.fetchall():
-            if state == 'cancel':
+                p.sale_id = ANY(%s) GROUP BY mp.state, p.sale_id''',(ids,))
+        for oid, nbr, mp_state in cr.fetchall():
+            if mp_state == 'cancel':
                 continue
-            if state == 'done':
+            if mp_state == 'done':
                 res[oid][0] += nbr or 0.0
                 res[oid][1] += nbr or 0.0
             else:
@@ -114,12 +128,14 @@ class sale_order(osv.osv):
                 res[r] = 0.0
             else:
                 res[r] = 100.0 * res[r][0] / res[r][1]
-        for order in self.browse(cr, uid, ids, context):
+        for order in self.browse(cr, uid, ids, context=context):
             if order.shipped:
                 res[order.id] = 100.0
         return res
 
     def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
         for sale in self.browse(cursor, user, ids, context=context):
             if sale.invoiced:
@@ -137,6 +153,8 @@ class sale_order(osv.osv):
         return res
 
     def _invoiced(self, cursor, user, ids, name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
         for sale in self.browse(cursor, user, ids, context=context):
             res[sale.id] = True
@@ -148,7 +166,9 @@ class sale_order(osv.osv):
                 res[sale.id] = False
         return res
 
-    def _invoiced_search(self, cursor, user, obj, name, args):
+    def _invoiced_search(self, cursor, user, obj, name, args, context=None):
+        if context is None:
+            context = {}
         if not len(args):
             return []
         clause = ''
@@ -175,7 +195,9 @@ class sale_order(osv.osv):
             return [('id', '=', 0)]
         return [('id', 'in', [x[0] for x in res])]
 
-    def _get_order(self, cr, uid, ids, context={}):
+    def _get_order(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         result = {}
         for line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
             result[line.order_id.id] = True
@@ -184,8 +206,8 @@ class sale_order(osv.osv):
     _columns = {
         'name': fields.char('Order Reference', size=64, required=True, select=True),
         'shop_id': fields.many2one('sale.shop', 'Shop', required=True, readonly=True, states={'draft': [('readonly', False)]}),
-        'origin': fields.char('Origin', size=64),
-        'client_order_ref': fields.char('Customer Ref', size=64),
+        'origin': fields.char('Source document', size=64, help="Reference of the document that generated this sale order request."),
+        'client_order_ref': fields.char('Customer Reference', size=64),
 
         'state': fields.selection([
             ('draft', 'Quotation'),
@@ -196,14 +218,15 @@ class sale_order(osv.osv):
             ('invoice_except', 'Invoice Exception'),
             ('done', 'Done'),
             ('cancel', 'Cancelled')
-            ], 'Order State', readonly=True, help="Gives the state of the quotation or sale order. The exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception). The 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to run on the date 'Date Ordered'.", select=True),
-        'date_order': fields.date('Date Ordered', required=True, readonly=True, states={'draft': [('readonly', False)]}),
-
+            ], 'Order State', readonly=True, help="Gives the state of the quotation or sale order. The exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception). The 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to run on the date 'Ordered Date'.", select=True),
+        'date_order': fields.date('Ordered Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
+        'create_date': fields.date('Creation Date', readonly=True),
+        'date_confirm': fields.date('Confirmation Date', readonly=True),
         'user_id': fields.many2one('res.users', 'Salesman', states={'draft': [('readonly', False)]}, select=True),
         'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)]}, required=True, change_default=True, select=True),
         'partner_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)]}),
-        'partner_order_id': fields.many2one('res.partner.address', 'Ordering Contact', readonly=True, required=True, states={'draft': [('readonly', False)]}, help="The name and address of the contact that requested the order or quotation."),
-        'partner_shipping_id': fields.many2one('res.partner.address', 'Shipping Address', readonly=True, required=True, states={'draft': [('readonly', False)]}),
+        'partner_order_id': fields.many2one('res.partner.address', 'Ordering Contact', readonly=True, required=True, states={'draft': [('readonly', False)]}, help="The name and address of the contact who requested the order or quotation."),
+        'partner_shipping_id': fields.many2one('res.partner.address', 'Shipping Address', readonly=True, required=True, states={'draft': [('readonly', False)]}, help="Shipping address for current sale order"),
 
         'incoterm': fields.selection(_incoterm_get, 'Incoterm', size=3),
         'picking_policy': fields.selection([('direct', 'Partial Delivery'), ('one', 'Complete Delivery')],
@@ -215,11 +238,11 @@ class sale_order(osv.osv):
             ('picking', 'Invoice from Picking'),
         ], 'Shipping Policy', required=True, readonly=True, states={'draft': [('readonly', False)]},
                     help="""The Shipping Policy is used to synchronise invoice and delivery operations.
-  - The 'Pay before delivery' choice will first generate the invoice and then generate the packing order after the payment of this invoice.
-  - The 'Shipping & Manual Invoice' will create the packing order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice.
+  - The 'Pay before delivery' choice will first generate the invoice and then generate the picking order after the payment of this invoice.
+  - The 'Shipping & Manual Invoice' will create the picking order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice.
   - The 'Invoice on Order After Delivery' choice will generate the draft invoice based on sale order after all picking lists have been finished.
-  - The 'Invoice from the picking' choice is used to create an invoice during the packing process."""),
-        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)]}),
+  - The 'Invoice from the picking' choice is used to create an invoice during the picking process."""),
+        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Pricelist version for current sale order"),
         'project_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)]}),
 
         'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)]}),
@@ -230,21 +253,21 @@ class sale_order(osv.osv):
         'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
         'invoiced': fields.function(_invoiced, method=True, string='Paid',
             fnct_search=_invoiced_search, type='boolean'),
-        'note': fields.text('Notes'),
+        'note': fields.text('Notes', translate=True),
 
-        'amount_untaxed': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Untaxed Amount',
+        'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), 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'),
-        'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Taxes',
+        'amount_tax': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), 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'),
-        'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
+        'amount_total': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), 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),
@@ -254,10 +277,10 @@ class sale_order(osv.osv):
         'invoice_quantity': fields.selection([('order', 'Ordered Quantities'), ('procurement', 'Shipped Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks.", required=True),
         'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
         'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
-        'company_id': fields.many2one('res.company','Company'),
+        'company_id': fields.many2one('res.company','Company',select=1),
     }
     _defaults = {
-        'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'sale.order', c),
+        'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'sale.order', context=c),
         'picking_policy': lambda *a: 'direct',
         'date_order': lambda *a: time.strftime('%Y-%m-%d'),
         'order_policy': lambda *a: 'manual',
@@ -268,13 +291,15 @@ class sale_order(osv.osv):
         '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_order_id': lambda self, cr, uid, context: context.get('partner_id', False) and  self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['contact'])['contact'],
         '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'],
-        'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist.id,
+#        'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist.id,
     }
     _order = 'name desc'
 
     # Form filling
     def unlink(self, cr, uid, ids, context=None):
-        sale_orders = self.read(cr, uid, ids, ['state'])
+        if context is None:
+            context = {}
+        sale_orders = self.read(cr, uid, ids, ['state'], context=context)
         unlink_ids = []
         for s in sale_orders:
             if s['state'] in ['draft', 'cancel']:
@@ -297,29 +322,45 @@ class sale_order(osv.osv):
     def action_cancel_draft(self, cr, uid, ids, *args):
         if not len(ids):
             return False
-        cr.execute('select id from sale_order_line where order_id in ('+','.join(map(str, ids))+')', ('draft',))
+        cr.execute('select id from sale_order_line where order_id = ANY(%s) and state=%s',(ids,'cancel'))
         line_ids = map(lambda x: x[0], cr.fetchall())
         self.write(cr, uid, ids, {'state': 'draft', 'invoice_ids': [], 'shipped': 0})
         self.pool.get('sale.order.line').write(cr, uid, line_ids, {'invoiced': False, 'state': 'draft', 'invoice_lines': [(6, 0, [])]})
         wf_service = netsvc.LocalService("workflow")
         for inv_id in ids:
+            # Deleting the existing instance of workflow for SO
+            wf_service.trg_delete(uid, 'sale.order', inv_id, cr)
             wf_service.trg_create(uid, 'sale.order', inv_id, cr)
         return True
 
     def onchange_partner_id(self, cr, uid, ids, part):
         if not part:
             return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'partner_order_id': False, 'payment_term': False, 'fiscal_position': False}}
+
         addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['delivery', 'invoice', 'contact'])
         part = self.pool.get('res.partner').browse(cr, uid, part)
         pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
         payment_term = part.property_payment_term and part.property_payment_term.id or False
         fiscal_position = part.property_account_position and part.property_account_position.id or False
-        val = {'partner_invoice_id': addr['invoice'], 'partner_order_id': addr['contact'], 'partner_shipping_id': addr['delivery'], 'payment_term': payment_term, 'fiscal_position': fiscal_position}
+        dedicated_salesman = part.user_id and part.user_id.id or uid
+
+        val = {
+            'partner_invoice_id': addr['invoice'],
+            'partner_order_id': addr['contact'],
+            'partner_shipping_id': addr['delivery'],
+            'payment_term': payment_term,
+            'fiscal_position': fiscal_position,
+            'user_id': dedicated_salesman,
+        }
+
         if pricelist:
             val['pricelist_id'] = pricelist
+
         return {'value': val}
 
-    def shipping_policy_change(self, cr, uid, ids, policy, context={}):
+    def shipping_policy_change(self, cr, uid, ids, policy, context=None):
+        if context is None:
+            context = {}
         if not policy:
             return {}
         inv_qty = 'order'
@@ -330,6 +371,8 @@ class sale_order(osv.osv):
         return {'value': {'invoice_quantity': inv_qty}}
 
     def write(self, cr, uid, ids, vals, context=None):
+        if context is None:
+            context = {}
         if 'order_policy' in vals:
             if vals['order_policy'] == 'prepaid':
                 vals.update({'invoice_quantity': 'order'})
@@ -337,7 +380,9 @@ class sale_order(osv.osv):
                 vals.update({'invoice_quantity': 'procurement'})
         return super(sale_order, self).write(cr, uid, ids, vals, context=context)
 
-    def create(self, cr, uid, vals, context={}):
+    def create(self, cr, uid, vals, context=None):
+        if context is None:
+            context = {}
         if 'order_policy' in vals:
             if vals['order_policy'] == 'prepaid':
                 vals.update({'invoice_quantity': 'order'})
@@ -345,17 +390,23 @@ class sale_order(osv.osv):
                 vals.update({'invoice_quantity': 'procurement'})
         return super(sale_order, self).create(cr, uid, vals, context=context)
 
-    def button_dummy(self, cr, uid, ids, context={}):
+    def button_dummy(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         return True
 
-#FIXME: the method should return the list of invoices created (invoice_ids)
-# and not the id of the last invoice created (res). The problem is that we
-# cannot change it directly since the method is called by the sale order
-# workflow and I suppose it expects a single id...
-    def _inv_get(self, cr, uid, order, context={}):
+    #FIXME: the method should return the list of invoices created (invoice_ids)
+    # and not the id of the last invoice created (res). The problem is that we
+    # cannot change it directly since the method is called by the sale order
+    # workflow and I suppose it expects a single id...
+    def _inv_get(self, cr, uid, order, context=None):
+        if context is None:
+            context = {}
         return {}
 
-    def _make_invoice(self, cr, uid, order, lines, context={}):
+    def _make_invoice(self, cr, uid, order, lines, context=None):
+        if context is None:
+            context = {}
         a = order.partner_id.property_account_receivable.id
         if order.payment_term:
             pay_term = order.payment_term.id
@@ -366,6 +417,11 @@ class sale_order(osv.osv):
                 for preline in preinv.invoice_line:
                     inv_line_id = self.pool.get('account.invoice.line').copy(cr, uid, preline.id, {'invoice_id': False, 'price_unit': -preline.price_unit})
                     lines.append(inv_line_id)
+        journal_obj = self.pool.get('account.journal')
+        journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', order.company_id.id)], limit=1)
+        if not journal_ids:
+            raise osv.except_osv(_('Error !'),
+                _('There is no sale journal defined for this company: "%s" (id:%d)') % (order.company_id.name, order.company_id.id))
         inv = {
             'name': order.client_order_ref or order.name,
             'origin': order.name,
@@ -373,19 +429,20 @@ class sale_order(osv.osv):
             'reference': "P%dSO%d" % (order.partner_id.id, order.id),
             'account_id': a,
             'partner_id': order.partner_id.id,
+            'journal_id': journal_ids[0],
             'address_invoice_id': order.partner_invoice_id.id,
             'address_contact_id': order.partner_order_id.id,
             'invoice_line': [(6, 0, lines)],
             'currency_id': order.pricelist_id.currency_id.id,
             'comment': order.note,
             'payment_term': pay_term,
-            'fiscal_position': order.partner_id.property_account_position.id,
+            'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
             'date_invoice' : context.get('date_invoice',False),
             'company_id' : order.company_id.id,
         }
         inv_obj = self.pool.get('account.invoice')
         inv.update(self._inv_get(cr, uid, order))
-        inv_id = inv_obj.create(cr, uid, inv)
+        inv_id = inv_obj.create(cr, uid, inv, context=context)
         data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], pay_term, time.strftime('%Y-%m-%d'))
         if data.get('value', False):
             inv_obj.write(cr, uid, [inv_id], data['value'], context=context)
@@ -435,8 +492,10 @@ class sale_order(osv.osv):
                     cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
         return res
 
-    def action_invoice_cancel(self, cr, uid, ids, context={}):
-        for sale in self.browse(cr, uid, ids):
+    def action_invoice_cancel(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
+        for sale in self.browse(cr, uid, ids, context=context):
             for line in sale.order_line:
                 invoiced = False
                 for iline in line.invoice_lines:
@@ -448,15 +507,31 @@ class sale_order(osv.osv):
         self.write(cr, uid, ids, {'state': 'invoice_except', 'invoice_ids': False})
         return True
 
-    def action_cancel(self, cr, uid, ids, context={}):
+    def action_invoice_end(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
+
+        for order in self.browse(cr, uid, ids, context=context):
+            for line in order.order_line:
+                if line.state == 'exception':
+                    self.pool.get('sale.order.line').write(cr, uid, [line.id], {'state': 'confirmed'}, context=context)
+
+            if order.state == 'invoice_except':
+                self.write(cr, uid, [order.id], {'state' : 'progress'}, context=context)
+
+        return True
+
+    def action_cancel(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         ok = True
         sale_order_line_obj = self.pool.get('sale.order.line')
-        for sale in self.browse(cr, uid, ids):
+        for sale in self.browse(cr, uid, ids, context=context):
             for pick in sale.picking_ids:
                 if pick.state not in ('draft', 'cancel'):
                     raise osv.except_osv(
                         _('Could not cancel sale order !'),
-                        _('You must first cancel all packing attached to this sale order.'))
+                        _('You must first cancel all picking attached to this sale order.'))
             for r in self.read(cr, uid, ids, ['picking_ids']):
                 for pick in r['picking_ids']:
                     wf_service = netsvc.LocalService("workflow")
@@ -476,20 +551,14 @@ class sale_order(osv.osv):
         return True
 
     def action_wait(self, cr, uid, ids, *args):
-        event_p = self.pool.get('res.partner.event.type').check(cr, uid, 'sale_open')
-        event_obj = self.pool.get('res.partner.event')
+        for (id,name) in self.name_get(cr, uid, ids):
+            message = _('Quotation ') + " '" + name + "' "+ _("converted to sale order.")
+            self.log(cr, uid, id, message)
         for o in self.browse(cr, uid, ids):
-            if event_p:
-                event_obj.create(cr, uid, {'name': 'Sale Order: '+ o.name,\
-                        'partner_id': o.partner_id.id,\
-                        'date': time.strftime('%Y-%m-%d %H:%M:%S'),\
-                        'user_id': (o.user_id and o.user_id.id) or uid,\
-                        'partner_type': 'customer', 'probability': 1.0,\
-                        'planned_revenue': o.amount_untaxed})
             if (o.order_policy == 'manual'):
-                self.write(cr, uid, [o.id], {'state': 'manual'})
+                self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': time.strftime('%Y-%m-%d')})
             else:
-                self.write(cr, uid, [o.id], {'state': 'progress'})
+                self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': time.strftime('%Y-%m-%d')})
             self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line])
 
     def procurement_lines_get(self, cr, uid, ids, *args):
@@ -511,25 +580,9 @@ class sale_order(osv.osv):
         notcanceled = False
         write_done_ids = []
         write_cancel_ids = []
-        stock_move_obj = self.pool.get('stock.move')
         for order in self.browse(cr, uid, ids, context={}):
-
-            #check for pending deliveries
-            pending_deliveries = False
-            # check => if order_lines do not exist,don't proceed for any mode.
-            if not order.order_line:
-                return False
             for line in order.order_line:
-                move_ids = stock_move_obj.search(cr, uid, [('sale_line_id','=', line.id)])
-                for move in stock_move_obj.browse( cr, uid, move_ids ):
-                    #if one of the related order lines is in state draft, auto or confirmed
-                    #this order line is not yet delivered
-                    if move.state in ('draft', 'auto', 'confirmed'):
-                        pending_deliveries = True
-                # Reason => if there are no move lines,the following condition will always set to be true,and will set SO to 'DONE'.
-                # Added move_ids check to SOLVE.
-                if move_ids and ((not line.procurement_id) or (line.procurement_id.state=='done')) and not pending_deliveries:
-#                    finished = True
+                if (not line.procurement_id) or (line.procurement_id.state=='done'):
                     if line.state != 'done':
                         write_done_ids.append(line.id)
                 else:
@@ -562,15 +615,17 @@ class sale_order(osv.osv):
             picking_id = False
             for line in order.order_line:
                 proc_id = False
-                date_planned = DateTime.now() + DateTime.DateTimeDeltaFromDays(line.delay or 0.0)
-                date_planned = (date_planned - DateTime.DateTimeDeltaFromDays(company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
+                date_planned = datetime.now() + relativedelta(days=line.delay or 0.0)
+                date_planned = (date_planned - relativedelta(company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
                 if line.state == 'done':
                     continue
                 if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'):
                     location_id = order.shop_id.warehouse_id.lot_stock_id.id
                     if not picking_id:
                         loc_dest_id = order.partner_id.property_stock_customer.id
+                        pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
                         picking_id = self.pool.get('stock.picking').create(cr, uid, {
+                            'name': pick_name,
                             'origin': order.name,
                             'type': 'out',
                             'state': 'auto',
@@ -662,8 +717,10 @@ class sale_order(osv.osv):
 
         return True
 
-    def action_ship_end(self, cr, uid, ids, context={}):
-        for order in self.browse(cr, uid, ids):
+    def action_ship_end(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
+        for order in self.browse(cr, uid, ids, context=context):
             val = {'shipped': True}
             if order.state == 'shipping_except':
                 val['state'] = 'progress'
@@ -717,24 +774,30 @@ sale_order()
 # - update it on change product and unit price
 # - use it in report if there is a uos
 class sale_order_line(osv.osv):
-    def _amount_line_net(self, cr, uid, ids, field_name, arg, context):
+    def _amount_line_net(self, cr, uid, ids, field_name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
-        for line in self.browse(cr, uid, ids):
+        for line in self.browse(cr, uid, ids, context=context):
             res[line.id] = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
         return res
 
-    def _amount_line(self, cr, uid, ids, field_name, arg, context):
+    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
         cur_obj = self.pool.get('res.currency')
-        for line in self.browse(cr, uid, ids):
+        for line in self.browse(cr, uid, ids, context=context):
             res[line.id] = line.price_unit * line.product_uom_qty * (1 - (line.discount or 0.0) / 100.0)
             cur = line.order_id.pricelist_id.currency_id
             res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
         return res
 
-    def _number_packages(self, cr, uid, ids, field_name, arg, context):
+    def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
+        if context is None:
+            context = {}
         res = {}
-        for line in self.browse(cr, uid, ids):
+        for line in self.browse(cr, uid, ids, context=context):
             try:
                 res[line.id] = int(line.product_uom_qty / line.product_packaging.qty)
             except:
@@ -742,37 +805,42 @@ class sale_order_line(osv.osv):
         return res
 
     _name = 'sale.order.line'
-    _description = 'Sale Order line'
+    _description = 'Sale Order Line'
     _columns = {
-        'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True),
-        'name': fields.char('Description', size=256, required=True, select=True),
-        'sequence': fields.integer('Sequence'),
-        'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the the shipping of the products to the customer"),
+        'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
+        'name': fields.char('Description', size=256, required=True, select=True, readonly=True, states={'draft':[('readonly',False)]}),
+        'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sale order lines."),
+        'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the the shipping of the products to the customer", readonly=True, states={'draft':[('readonly',False)]}),
         'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
         'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
         'invoiced': fields.boolean('Invoiced', readonly=True),
-        'procurement_id': fields.many2one('mrp.procurement', 'Requisition'),
-        'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
-        'price_net': fields.function(_amount_line_net, method=True, string='Net Price', digits=(16, int(config['price_accuracy']))),
-        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'),
-        'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes'),
-        'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Requisition Method', required=True),
-        'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties'),
+        'procurement_id': fields.many2one('mrp.procurement', 'Procurement'),
+        'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Sale Price'), readonly=True, states={'draft':[('readonly',False)]}),
+        'price_net': fields.function(_amount_line_net, method=True, string='Net Price', digits_compute= dp.get_precision('Sale Price')),
+        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal', digits_compute= dp.get_precision('Sale Price')),
+        'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft':[('readonly',False)]}),
+        'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
+        'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft':[('readonly',False)]}),
         'address_allotment_id': fields.many2one('res.partner.address', 'Allotment Partner'),
-        'product_uom_qty': fields.float('Quantity (UoM)', digits=(16, 2), required=True),
-        'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
-        'product_uos_qty': fields.float('Quantity (UoS)'),
+        'product_uom_qty': fields.float('Quantity (UoM)', digits=(16, 2), 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)', readonly=True, states={'draft':[('readonly',False)]}),
         'product_uos': fields.many2one('product.uom', 'Product UoS'),
         'product_packaging': fields.many2one('product.packaging', 'Packaging'),
         'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True),
-        'discount': fields.float('Discount (%)', digits=(16, 2)),
+        'discount': fields.float('Discount (%)', digits=(16, 2), readonly=True, states={'draft':[('readonly',False)]}),
         'number_packages': fields.function(_number_packages, method=True, type='integer', string='Number Packages'),
-        'notes': fields.text('Notes'),
+        'notes': fields.text('Notes', translate=True),
         'th_weight': fields.float('Weight'),
-        'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled'), ('exception', 'Exception')], 'State', required=True, readonly=True),
+        'state': fields.selection([('draft', 'Draft'),('confirmed', 'Confirmed'),('done', 'Done'),('cancel', 'Cancelled'),('exception', 'Exception')], 'State', required=True, readonly=True,
+                help=' * The \'Draft\' state is set automatically when sale order in draft state. \
+                    \n* The \'Confirmed\' state is set automatically when sale order in confirm state. \
+                    \n* The \'Exception\' state is set automatically when sale order is set as exception. \
+                    \n* The \'Done\' state is set automatically when sale order is set as done. \
+                    \n* The \'Cancelled\' state is set automatically when user cancel sale order.'),
         'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', string='Customer'),
         'salesman_id':fields.related('order_id','user_id',type='many2one',relation='res.users',string='Salesman'),
-        'company_id': fields.related('order_id','company_id',type='many2one',relation='res.company',string='Company'),
+        'company_id': fields.related('order_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
     }
     _order = 'sequence, id'
     _defaults = {
@@ -787,7 +855,9 @@ class sale_order_line(osv.osv):
         'product_packaging': lambda *a: False
     }
 
-    def invoice_line_create(self, cr, uid, ids, context={}):
+    def invoice_line_create(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         def _get_line_qty(line):
             if (line.order_id.invoice_quantity=='order') or not line.procurement_id:
                 if line.product_uos:
@@ -795,7 +865,7 @@ class sale_order_line(osv.osv):
                 return line.product_uom_qty
             else:
                 return self.pool.get('mrp.procurement').quantity_get(cr, uid,
-                        line.procurement_id.id, context)
+                        line.procurement_id.id, context=context)
 
         def _get_line_uom(line):
             if (line.order_id.invoice_quantity=='order') or not line.procurement_id:
@@ -804,11 +874,11 @@ class sale_order_line(osv.osv):
                 return line.product_uom.id
             else:
                 return self.pool.get('mrp.procurement').uom_get(cr, uid,
-                        line.procurement_id.id, context)
+                        line.procurement_id.id, context=context)
 
         create_ids = []
         sales = {}
-        for line in self.browse(cr, uid, ids, context):
+        for line in self.browse(cr, uid, ids, context=context):
             if not line.invoiced:
                 if line.product_id:
                     a = line.product_id.product_tmpl_id.property_account_income.id
@@ -828,7 +898,7 @@ class sale_order_line(osv.osv):
                 pu = 0.0
                 if uosqty:
                     pu = round(line.price_unit * line.product_uom_qty / uosqty,
-                            int(config['price_accuracy']))
+                            self.pool.get('decimal.precision').precision_get(cr, uid, 'Sale Price'))
                 fpos = line.order_id.fiscal_position or False
                 a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
                 if not a:
@@ -859,19 +929,25 @@ class sale_order_line(osv.osv):
             wf_service.trg_write(uid, 'sale.order', sid, cr)
         return create_ids
 
-    def button_cancel(self, cr, uid, ids, context={}):
+    def button_cancel(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         for line in self.browse(cr, uid, ids, context=context):
             if line.invoiced:
                 raise osv.except_osv(_('Invalid action !'), _('You cannot cancel a sale order line that has already been invoiced !'))
         return self.write(cr, uid, ids, {'state': 'cancel'})
 
-    def button_confirm(self, cr, uid, ids, context={}):
+    def button_confirm(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         return self.write(cr, uid, ids, {'state': 'confirmed'})
 
-    def button_done(self, cr, uid, ids, context={}):
+    def button_done(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         wf_service = netsvc.LocalService("workflow")
         res = self.write(cr, uid, ids, {'state': 'done'})
-        for line in self.browse(cr, uid, ids, context):
+        for line in self.browse(cr, uid, ids, context=context):
             wf_service.trg_write(uid, 'sale.order', line.order_id.id, cr)
 
         return res
@@ -896,11 +972,13 @@ class sale_order_line(osv.osv):
             pass
         return {'value': value}
 
-    def copy_data(self, cr, uid, id, default=None, context={}):
+    def copy_data(self, cr, uid, id, default=None, context=None):
+        if context is None:
+            context = {}
         if not default:
             default = {}
         default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []})
-        return super(sale_order_line, self).copy_data(cr, uid, id, default, context)
+        return super(sale_order_line, self).copy_data(cr, uid, id, default, 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,
@@ -931,7 +1009,7 @@ class sale_order_line(osv.osv):
 
         if packaging:
             default_uom = product_obj.uom_id and product_obj.uom_id.id
-            pack = self.pool.get('product.packaging').browse(cr, uid, packaging, context)
+            pack = self.pool.get('product.packaging').browse(cr, uid, packaging, context=context)
             q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
 #            qty = qty - qty % q + q
             if qty and (q and not (qty % q) == 0):
@@ -958,7 +1036,6 @@ class sale_order_line(osv.osv):
                     uos = False
             else:
                 uos = False
-        result.update({'type': product_obj.procure_method})
         if product_obj.description_sale:
             result['notes'] = product_obj.description_sale
         fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False
@@ -966,8 +1043,10 @@ class sale_order_line(osv.osv):
             result['delay'] = (product_obj.sale_delay or 0.0)
             partner = partner_obj.browse(cr, uid, partner_id)
             result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)
+            result.update({'type': product_obj.procure_method})
+
         if not flag:
-            result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id])[0][1]
+            result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context)[0][1]
         domain = {}
         if (not uom) and (not uos):
             result['product_uom'] = product_obj.uom_id.id
@@ -985,7 +1064,7 @@ class sale_order_line(osv.osv):
                         'product_uos':
                         [('category_id', '=', uos_category_id)]}
 
-        elif uos: # only happens if uom is False
+        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
@@ -1006,7 +1085,7 @@ class sale_order_line(osv.osv):
             warning = {
                 'title': 'No Pricelist !',
                 'message':
-                    'You have to select a pricelist in the sale form !\n'
+                    'You have to select a pricelist or a customer in the sale form !\n'
                     'Please set one before choosing a product.'
                 }
         else:
@@ -1030,7 +1109,7 @@ class sale_order_line(osv.osv):
             uom=False, qty_uos=0, uos=False, name='', partner_id=False,
             lang=False, update_tax=True, date_order=False):
         res = self.product_id_change(cursor, user, ids, pricelist, product,
-                qty=0, uom=uom, qty_uos=qty_uos, uos=uos, name=name,
+                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)
         if 'product_uom' in res['value']:
@@ -1039,7 +1118,9 @@ class sale_order_line(osv.osv):
             res['value']['price_unit'] = 0.0
         return res
 
-    def unlink(self, cr, uid, ids, context={}):
+    def unlink(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         """Allows to delete sale order lines in draft,cancel states"""
         for rec in self.browse(cr, uid, ids, context=context):
             if rec.state not in ['draft', 'cancel']:
@@ -1051,6 +1132,8 @@ sale_order_line()
 
 class sale_config_picking_policy(osv.osv_memory):
     _name = 'sale.config.picking_policy'
+    _inherit = 'res.config'
+
     _columns = {
         'name': fields.char('Name', size=64),
         'picking_policy': fields.selection([
@@ -1060,7 +1143,8 @@ class sale_config_picking_policy(osv.osv_memory):
         'order_policy': fields.selection([
             ('manual', 'Invoice Based on Sales Orders'),
             ('picking', 'Invoice Based on Deliveries'),
-        ], 'Shipping Default Policy', required=True),
+        ], 'Shipping Default Policy', required=True,
+           help="You can generate invoices based on sales orders or based on shippings."),
         'step': fields.selection([
             ('one', 'Delivery Order Only'),
             ('two', 'Picking List & Delivery Order')
@@ -1072,11 +1156,13 @@ class sale_config_picking_policy(osv.osv_memory):
     }
     _defaults = {
         'picking_policy': lambda *a: 'direct',
-        'order_policy': lambda *a: 'picking',
+        'order_policy': lambda *a: 'manual',
         'step': lambda *a: 'one'
     }
 
-    def set_default(self, cr, uid, ids, context=None):
+    def execute(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
         for o in self.browse(cr, uid, ids, context=context):
             ir_values_obj = self.pool.get('ir.values')
             ir_values_obj.set(cr, uid, 'default', False, 'picking_policy', ['sale.order'], o.picking_policy)
@@ -1085,31 +1171,13 @@ class sale_config_picking_policy(osv.osv_memory):
             if o.step == 'one':
                 md = self.pool.get('ir.model.data')
                 group_id = md._get_id(cr, uid, 'base', 'group_no_one')
-                group_id = md.browse(cr, uid, group_id, context).res_id
+                group_id = md.browse(cr, uid, group_id, context=context).res_id
                 menu_id = md._get_id(cr, uid, 'stock', 'menu_action_picking_tree_delivery')
-                menu_id = md.browse(cr, uid, menu_id, context).res_id
+                menu_id = md.browse(cr, uid, menu_id, context=context).res_id
                 self.pool.get('ir.ui.menu').write(cr, uid, [menu_id], {'groups_id': [(6, 0, [group_id])]})
 
                 location_id = md._get_id(cr, uid, 'stock', 'stock_location_output')
-                location_id = md.browse(cr, uid, location_id, context).res_id
+                location_id = md.browse(cr, uid, location_id, context=context).res_id
                 self.pool.get('stock.location').write(cr, uid, [location_id], {'chained_auto_packing': 'transparent'})
-
-        return {
-                'view_type': 'form',
-                "view_mode": 'form',
-                'res_model': 'ir.actions.configuration.wizard',
-                'type': 'ir.actions.act_window',
-                'target': 'new',
-         }
-
-    def action_cancel(self, cr, uid, ids, context=None):
-        return {
-                'view_type': 'form',
-                "view_mode": 'form',
-                'res_model': 'ir.actions.configuration.wizard',
-                'type': 'ir.actions.act_window',
-                'target': 'new',
-         }
-
 sale_config_picking_policy()