modifs
[odoo/odoo.git] / addons / stock / stock.py
index e8fcd34..94fab1d 100644 (file)
@@ -1,33 +1,37 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 ##############################################################################
 #
 #    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
-#    $Id$
+#    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 General Public License as published by
-#    the Free Software Foundation, either version 3 of the License, or
-#    (at your option) any later version.
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
 #
 #    This program is distributed in the hope that it will be useful,
 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
+#    GNU Affero General Public License for more details.
 #
-#    You should have received a copy of the GNU General Public License
+#    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 ##############################################################################
 
-from mx import DateTime
-import time
-import netsvc
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+
 from osv import fields, osv
 from tools import config
 from tools.translate import _
+import math
+import netsvc
+import time
 import tools
 
+import decimal_precision as dp
+
 
 #----------------------------------------------------------
 # Incoterms
@@ -36,9 +40,9 @@ class stock_incoterms(osv.osv):
     _name = "stock.incoterms"
     _description = "Incoterms"
     _columns = {
-        'name': fields.char('Name', size=64, required=True),
-        'code': fields.char('Code', size=3, required=True),
-        'active': fields.boolean('Active'),
+        'name': fields.char('Name', size=64, required=True,help="Incoterms are series of sales terms.They are used to divide transaction costs and responsibilities between buyer and seller and reflect state-of-the-art transportation practices."),
+        'code': fields.char('Code', size=3, required=True,help="Code for Incoterms"),
+        'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the incoterms without removing it."),
     }
     _defaults = {
         'active': lambda *a: True,
@@ -58,6 +62,21 @@ class stock_location(osv.osv):
     _parent_order = 'id'
     _order = 'parent_left'
 
+    def name_get(self, cr, uid, ids, context={}):
+        if not len(ids):
+            return []
+        reads = self.read(cr, uid, ids, ['name','location_id'], context)
+        res = []
+        for record in reads:
+            name = record['name']
+            if context.get('full',False):
+                if record['location_id']:
+                    name = record['location_id'][1]+' / '+name
+                res.append((record['id'], name))
+            else:
+                res.append((record['id'], name))
+        return res
+
     def _complete_name(self, cr, uid, ids, name, args, context):
         def _get_one_full_name(location, level=4):
             if location.location_id:
@@ -96,11 +115,20 @@ class stock_location(osv.osv):
         cr.execute('select distinct product_id from stock_move where (location_id=%s) or (location_dest_id=%s)', (id, id))
         result = cr.dictfetchall()
         if result:
+            # Choose the right filed standard_price to read
+            # Take the user company
+            price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id
+            pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
             for r in result:
                 c = (context or {}).copy()
                 c['location'] = id
-                product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, 'standard_price'], context=c)
-                final_value += (product[field_to_read] * product['standard_price'])
+                product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read], context=c)
+                # Compute the amount_unit in right currency
+
+                context['currency_id']=self.pool.get('res.users').browse(cr,uid,uid).company_id.currency_id.id
+                amount_unit=self.pool.get('product.product').browse(cr,uid,r['product_id']).price_get(pricetype.field, context)[r['product_id']]
+
+                final_value += (product[field_to_read] * amount_unit)
         return final_value
 
     def _product_value(self, cr, uid, ids, field_names, arg, context={}):
@@ -115,7 +143,7 @@ class stock_location(osv.osv):
 
     _columns = {
         'name': fields.char('Location Name', size=64, required=True, translate=True),
-        'active': fields.boolean('Active'),
+        'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the stock location without removing it."),
         'usage': fields.selection([('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production')], 'Location Type', required=True),
         'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True),
 
@@ -135,12 +163,12 @@ class stock_location(osv.osv):
             [('auto', 'Automatic Move'), ('manual', 'Manual Operation'), ('transparent', 'Automatic No Step Added')],
             'Automatic Move',
             required=True,
-            help="This is used only if you selected a chained location type.\n" \
+            help="This is used only if you select a chained location type.\n" \
                 "The 'Automatic Move' value will create a stock move after the current one that will be "\
                 "validated automatically. With 'Manual Operation', the stock move has to be validated "\
                 "by a worker. With 'Automatic No Step Added', the location is replaced in the original move."
             ),
-        'chained_delay': fields.integer('Chained Delay (days)'),
+        'chained_delay': fields.integer('Chained lead time (days)'),
         'address_id': fields.many2one('res.partner.address', 'Location Address'),
         'icon': fields.selection(tools.icons, 'Icon', size=64),
 
@@ -153,6 +181,7 @@ class stock_location(osv.osv):
         'parent_right': fields.integer('Right Parent', select=1),
         'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock"),
         'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock"),
+        'company_id': fields.many2one('res.company', 'Company', required=True,select=1),
     }
     _defaults = {
         'active': lambda *a: 1,
@@ -160,10 +189,11 @@ class stock_location(osv.osv):
         'allocation_method': lambda *a: 'fifo',
         'chained_location_type': lambda *a: 'none',
         'chained_auto_packing': lambda *a: 'manual',
+        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.location', context=c),
         'posx': lambda *a: 0,
         'posy': lambda *a: 0,
         'posz': lambda *a: 0,
-        'icon': lambda *a: False
+        'icon': lambda *a: False,
     }
 
     def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}):
@@ -195,6 +225,11 @@ class stock_location(osv.osv):
         if context is None:
             context = {}
         product_obj = self.pool.get('product.product')
+        # Take the user company and pricetype
+        price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id
+        pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
+        context['currency_id']=self.pool.get('res.users').browse(cr,uid,uid).company_id.currency_id.id
+
         if not product_ids:
             product_ids = product_obj.search(cr, uid, [])
 
@@ -225,10 +260,16 @@ class stock_location(osv.osv):
                         continue
                     product = products_by_id[product_id]
                     quantity_total += qty[product_id]
-                    price = qty[product_id] * product.standard_price
+
+                    # Compute based on pricetype
+                    # Choose the right filed standard_price to read
+                    amount_unit=product.price_get(pricetype.field, context)[product.id]
+                    price = qty[product_id] * amount_unit
+                    # price = qty[product_id] * product.standard_price
+
                     total_price += price
                     result['product'].append({
-                        'price': product.standard_price,
+                        'price': amount_unit,
                         'prod_name': product.name,
                         'code': product.default_code, # used by lot_overview_all report!
                         'variants': product.variants or '',
@@ -321,11 +362,11 @@ class stock_tracking(osv.osv):
         return sequence + str(self.checksum(sequence))
 
     _columns = {
-        'name': fields.char('Tracking', size=64, required=True),
-        'active': fields.boolean('Active'),
+        'name': fields.char('Tracking ID', size=64, required=True),
+        'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the tracking lots without removing it."),
         'serial': fields.char('Reference', size=64),
         'move_ids': fields.one2many('stock.move', 'tracking_id', 'Moves Tracked'),
-        'date': fields.datetime('Date Created', required=True),
+        'date': fields.datetime('Created Date', required=True),
     }
     _defaults = {
         'active': lambda *a: 1,
@@ -333,7 +374,7 @@ class stock_tracking(osv.osv):
         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
     }
 
-    def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
+    def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
         if not args:
             args = []
         if not context:
@@ -359,7 +400,7 @@ stock_tracking()
 #----------------------------------------------------------
 class stock_picking(osv.osv):
     _name = "stock.picking"
-    _description = "Packing List"
+    _description = "Picking List"
 
     def _set_maximum_date(self, cr, uid, ids, name, value, arg, context):
         if not value:
@@ -405,9 +446,9 @@ class stock_picking(osv.osv):
             from
                 stock_move
             where
-                picking_id in (""" + ','.join(map(str, ids)) + """)
+                picking_id=ANY(%s)
             group by
-                picking_id""")
+                picking_id""",(ids,))
         for pick, dt1, dt2 in cr.fetchall():
             res[pick]['min_date'] = dt1
             res[pick]['max_date'] = dt2
@@ -421,15 +462,17 @@ class stock_picking(osv.osv):
 
     _columns = {
         'name': fields.char('Reference', size=64, select=True),
-        'origin': fields.char('Origin Reference', size=64),
-        'backorder_id': fields.many2one('stock.picking', 'Back Order'),
-        'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True, select=True),
-        'active': fields.boolean('Active'),
+        'origin': fields.char('Origin', size=64, help="Reference of the document that produced this picking."),
+        'backorder_id': fields.many2one('stock.picking', 'Back Order', help="If the picking is splitted then the picking id in available state of move for this picking is stored in Backorder."),
+        'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."),
+        'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the picking without removing it."),
         'note': fields.text('Notes'),
 
-        'location_id': fields.many2one('stock.location', 'Location'),
-        'location_dest_id': fields.many2one('stock.location', 'Dest. Location'),
-        'move_type': fields.selection([('direct', 'Direct Delivery'), ('one', 'All at once')], 'Delivery Method', required=True),
+        'location_id': fields.many2one('stock.location', 'Location', help="Keep empty if you produce at the location where the finished products are needed." \
+                "Set a location if you produce at a fixed location. This can be a partner location " \
+                "if you subcontract the manufacturing operations."),
+        'location_dest_id': fields.many2one('stock.location', 'Dest. Location',help="Location where the system will stock the finished products."),
+        'move_type': fields.selection([('direct', 'Direct Delivery'), ('one', 'All at once')], 'Delivery Method', required=True, help="It specifies goods to be delivered all at once or by direct delivery"),
         'state': fields.selection([
             ('draft', 'Draft'),
             ('auto', 'Waiting'),
@@ -437,21 +480,27 @@ class stock_picking(osv.osv):
             ('assigned', 'Available'),
             ('done', 'Done'),
             ('cancel', 'Cancelled'),
-            ], 'Status', readonly=True, select=True),
+            ], 'State', readonly=True, select=True,
+            help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed picking. \
+            \n* The \'Confirmed\' state is used for stock movement to do with unavailable products. \
+            \n* The \'Available\' state is set automatically when the products are ready to be moved.\
+            \n* The \'Waiting\' state is used in MTO moves when a movement is waiting for another one.'),
         'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
-                 method=True, store=True, type='datetime', string='Planned Date', select=1),
-        'date': fields.datetime('Date Order'),
-        'date_done': fields.datetime('Date Done'),
+                 method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for Picking. Default it takes current date"),
+        'date': fields.datetime('Order Date', help="Date of Order"),
+        'date_done': fields.datetime('Date Done', help="Date of completion"),
         'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
-                 method=True, store=True, type='datetime', string='Max. Planned Date', select=2),
-        'move_lines': fields.one2many('stock.move', 'picking_id', 'Move lines', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
-        'auto_picking': fields.boolean('Auto-Packing'),
-        'address_id': fields.many2one('res.partner.address', 'Partner'),
+                 method=True, store=True, type='datetime', string='Max. Expected Date', select=2),
+        'move_lines': fields.one2many('stock.move', 'picking_id', 'Entry lines', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
+        'delivery_line':fields.one2many('stock.delivery', 'picking_id', 'Delivery lines', readonly=True),
+        'auto_picking': fields.boolean('Auto-Picking'),
+        'address_id': fields.many2one('res.partner.address', 'Partner', help="Address of partner"),
         'invoice_state': fields.selection([
             ("invoiced", "Invoiced"),
             ("2binvoiced", "To Be Invoiced"),
-            ("none", "Not from Packing")], "Invoice Status",
+            ("none", "Not from Picking")], "Invoice Status",
             select=True, required=True, readonly=True, states={'draft': [('readonly', False)]}),
+        'company_id': fields.many2one('res.company', 'Company', required=True,select=1),
     }
     _defaults = {
         'name': lambda self, cr, uid, context: '/',
@@ -461,6 +510,7 @@ class stock_picking(osv.osv):
         'type': lambda *a: 'in',
         'invoice_state': lambda *a: 'none',
         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
+        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock_picking', context=c)
     }
 
     def copy(self, cr, uid, id, default=None, context={}):
@@ -493,24 +543,26 @@ class stock_picking(osv.osv):
         # TODO: Check locations to see if in the same location ?
         return True
 
-    def button_confirm(self, cr, uid, ids, *args):
-        for id in ids:
-            wf_service = netsvc.LocalService("workflow")
-            wf_service.trg_validate(uid, 'stock.picking', id, 'button_confirm', cr)
-        self.force_assign(cr, uid, ids, *args)
-        return True
+#    def button_confirm(self, cr, uid, ids, *args):
+#        for id in ids:
+#            wf_service = netsvc.LocalService("workflow")
+#            wf_service.trg_validate(uid, 'stock.picking', id, 'button_confirm', cr)
+#        self.force_assign(cr, uid, ids, *args)
+#        return True
 
     def action_assign(self, cr, uid, ids, *args):
         for pick in self.browse(cr, uid, ids):
             move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed']
+            if not move_ids:
+                raise osv.except_osv(_('Warning !'),_('Not Available. Moves are not confirmed.'))
             self.pool.get('stock.move').action_assign(cr, uid, move_ids)
         return True
 
     def force_assign(self, cr, uid, ids, *args):
         wf_service = netsvc.LocalService("workflow")
         for pick in self.browse(cr, uid, ids):
-#           move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed']
-            move_ids = [x.id for x in pick.move_lines]
+            move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed','waiting']]
+#            move_ids = [x.id for x in pick.move_lines]
             self.pool.get('stock.move').force_assign(cr, uid, move_ids)
             wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
         return True
@@ -597,39 +649,48 @@ class stock_picking(osv.osv):
                         context=context)
         return True
 
-    def get_currency_id(self, cursor, user, picking):
+    def get_currency_id(self, cr, uid, picking):
         return False
 
-    def _get_payment_term(self, cursor, user, picking):
+    def _get_payment_term(self, cr, uid, picking):
         '''Return {'contact': address, 'invoice': address} for invoice'''
         partner_obj = self.pool.get('res.partner')
         partner = picking.address_id.partner_id
         return partner.property_payment_term and partner.property_payment_term.id or False
 
-    def _get_address_invoice(self, cursor, user, picking):
+    def _get_address_invoice(self, cr, uid, picking):
         '''Return {'contact': address, 'invoice': address} for invoice'''
         partner_obj = self.pool.get('res.partner')
         partner = picking.address_id.partner_id
 
-        return partner_obj.address_get(cursor, user, [partner.id],
+        return partner_obj.address_get(cr, uid, [partner.id],
                 ['contact', 'invoice'])
 
-    def _get_comment_invoice(self, cursor, user, picking):
+    def _get_comment_invoice(self, cr, uid, picking):
         '''Return comment string for invoice'''
         return picking.note or ''
 
-    def _get_price_unit_invoice(self, cursor, user, move_line, type):
+    def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None):
         '''Return the price unit for the move line'''
+        if context is None:
+            context = {}
+
         if type in ('in_invoice', 'in_refund'):
-            return move_line.product_id.standard_price
+            # Take the user company and pricetype
+            price_type_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.property_valuation_price_type.id
+            pricetype = self.pool.get('product.price.type').browse(cr, uid, price_type_id)
+            context['currency_id'] = move_line.company_id.currency_id.id
+
+            amount_unit = move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id]
+            return amount_unit
         else:
             return move_line.product_id.list_price
 
-    def _get_discount_invoice(self, cursor, user, move_line):
+    def _get_discount_invoice(self, cr, uid, move_line):
         '''Return the discount for the move line'''
         return 0.0
 
-    def _get_taxes_invoice(self, cursor, user, move_line, type):
+    def _get_taxes_invoice(self, cr, uid, move_line, type):
         '''Return taxes ids for the move line'''
         if type in ('in_invoice', 'in_refund'):
             taxes = move_line.product_id.supplier_taxes_id
@@ -638,34 +699,37 @@ class stock_picking(osv.osv):
 
         if move_line.picking_id and move_line.picking_id.address_id and move_line.picking_id.address_id.partner_id:
             return self.pool.get('account.fiscal.position').map_tax(
-                cursor,
-                user,
+                cr,
+                uid,
                 move_line.picking_id.address_id.partner_id.property_account_position,
                 taxes
             )
         else:
             return map(lambda x: x.id, taxes)
 
-    def _get_account_analytic_invoice(self, cursor, user, picking, move_line):
+    def _get_account_analytic_invoice(self, cr, uid, picking, move_line):
         return False
 
-    def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id):
+    def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id):
         '''Call after the creation of the invoice line'''
         return
 
-    def _invoice_hook(self, cursor, user, picking, invoice_id):
+    def _invoice_hook(self, cr, uid, picking, invoice_id):
         '''Call after the creation of the invoice'''
         return
 
-    def action_invoice_create(self, cursor, user, ids, journal_id=False,
+    def action_invoice_create(self, cr, uid, ids, journal_id=False,
             group=False, type='out_invoice', context=None):
         '''Return ids of created invoices for the pickings'''
+        if context is None:
+            context = {}
+
         invoice_obj = self.pool.get('account.invoice')
         invoice_line_obj = self.pool.get('account.invoice.line')
         invoices_group = {}
         res = {}
 
-        for picking in self.browse(cursor, user, ids, context=context):
+        for picking in self.browse(cr, uid, ids, context=context):
             if picking.invoice_state != '2binvoiced':
                 continue
             payment_term_id = False
@@ -676,23 +740,24 @@ class stock_picking(osv.osv):
 
             if type in ('out_invoice', 'out_refund'):
                 account_id = partner.property_account_receivable.id
-                payment_term_id = self._get_payment_term(cursor, user, picking)
+                payment_term_id = self._get_payment_term(cr, uid, picking)
             else:
                 account_id = partner.property_account_payable.id
 
             address_contact_id, address_invoice_id = \
-                    self._get_address_invoice(cursor, user, picking).values()
+                    self._get_address_invoice(cr, uid, picking).values()
 
-            comment = self._get_comment_invoice(cursor, user, picking)
+            comment = self._get_comment_invoice(cr, uid, picking)
             if group and partner.id in invoices_group:
                 invoice_id = invoices_group[partner.id]
-                invoice = invoice_obj.browse(cursor, user, invoice_id)
+                invoice = invoice_obj.browse(cr, uid, invoice_id)
                 invoice_vals = {
                     'name': (invoice.name or '') + ', ' + (picking.name or ''),
                     'origin': (invoice.origin or '') + ', ' + (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
                     'comment': (comment and (invoice.comment and invoice.comment+"\n"+comment or comment)) or (invoice.comment and invoice.comment or ''),
+                    'date_invoice':context.get('date_inv',False)
                 }
-                invoice_obj.write(cursor, user, [invoice_id], invoice_vals, context=context)
+                invoice_obj.write(cr, uid, [invoice_id], invoice_vals, context=context)
             else:
                 invoice_vals = {
                     'name': picking.name,
@@ -704,14 +769,16 @@ class stock_picking(osv.osv):
                     'address_contact_id': address_contact_id,
                     'comment': comment,
                     'payment_term': payment_term_id,
-                    'fiscal_position': partner.property_account_position.id
+                    'fiscal_position': partner.property_account_position.id,
+                    'date_invoice': context.get('date_inv',False),
+                    'company_id': picking.company_id.id,
                     }
-                cur_id = self.get_currency_id(cursor, user, picking)
+                cur_id = self.get_currency_id(cr, uid, picking)
                 if cur_id:
                     invoice_vals['currency_id'] = cur_id
                 if journal_id:
                     invoice_vals['journal_id'] = journal_id
-                invoice_id = invoice_obj.create(cursor, user, invoice_vals,
+                invoice_id = invoice_obj.create(cr, uid, invoice_vals,
                         context=context)
                 invoices_group[partner.id] = invoice_id
             res[picking.id] = invoice_id
@@ -737,20 +804,25 @@ class stock_picking(osv.osv):
                         account_id = move_line.product_id.categ_id.\
                                 property_account_expense_categ.id
 
-                price_unit = self._get_price_unit_invoice(cursor, user,
+                price_unit = self._get_price_unit_invoice(cr, uid,
                         move_line, type)
-                discount = self._get_discount_invoice(cursor, user, move_line)
-                tax_ids = self._get_taxes_invoice(cursor, user, move_line, type)
-                account_analytic_id = self._get_account_analytic_invoice(cursor,
-                        user, picking, move_line)
-                
+                discount = self._get_discount_invoice(cr, uid, move_line)
+                tax_ids = self._get_taxes_invoice(cr, uid, move_line, type)
+                account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, move_line)
+
                 #set UoS if it's a sale and the picking doesn't have one
                 uos_id = move_line.product_uos and move_line.product_uos.id or False
                 if not uos_id and type in ('out_invoice', 'out_refund'):
                     uos_id = move_line.product_uom.id
 
-                account_id = self.pool.get('account.fiscal.position').map_account(cursor, user, partner.property_account_position, account_id)
-                invoice_line_id = invoice_line_obj.create(cursor, user, {
+                account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id)
+                notes = False
+                if move_line.sale_line_id:
+                    notes = move_line.sale_line_id.notes
+                elif move_line.purchase_line_id:
+                    notes = move_line.purchase_line_id.notes
+
+                invoice_line_id = invoice_line_obj.create(cr, uid, {
                     'name': name,
                     'origin': origin,
                     'invoice_id': invoice_id,
@@ -762,16 +834,17 @@ class stock_picking(osv.osv):
                     'quantity': move_line.product_uos_qty or move_line.product_qty,
                     'invoice_line_tax_id': [(6, 0, tax_ids)],
                     'account_analytic_id': account_analytic_id,
+                    'note': notes,
                     }, context=context)
-                self._invoice_line_hook(cursor, user, move_line, invoice_line_id)
+                self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
 
-            invoice_obj.button_compute(cursor, user, [invoice_id], context=context,
+            invoice_obj.button_compute(cr, uid, [invoice_id], context=context,
                     set_total=(type in ('in_invoice', 'in_refund')))
-            self.write(cursor, user, [picking.id], {
+            self.write(cr, uid, [picking.id], {
                 'invoice_state': 'invoiced',
                 }, context=context)
-            self._invoice_hook(cursor, user, picking, invoice_id)
-        self.write(cursor, user, res.keys(), {
+            self._invoice_hook(cr, uid, picking, invoice_id)
+        self.write(cr, uid, res.keys(), {
             'invoice_state': 'invoiced',
             }, context=context)
         return res
@@ -784,18 +857,163 @@ class stock_picking(osv.osv):
                 if move.state not in ('cancel',):
                     return False
         return True
-    
+
     def unlink(self, cr, uid, ids, context=None):
         for pick in self.browse(cr, uid, ids, context=context):
             if pick.state in ['done','cancel']:
                 raise osv.except_osv(_('Error'), _('You cannot remove the picking which is in %s state !')%(pick.state,))
             elif pick.state in ['confirmed','assigned']:
                 ids2 = [move.id for move in pick.move_lines]
+                context.update({'call_unlink':True})
                 self.pool.get('stock.move').action_cancel(cr, uid, ids2, context)
             else:
-                continue    
+                continue
         return super(stock_picking, self).unlink(cr, uid, ids, context=context)
 
+    def do_partial(self, cr, uid, ids, partial_datas, context={}):
+        """
+        @ partial_datas : dict. contain details of partial picking
+                          like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
+        """
+        res = {}
+        move_obj = self.pool.get('stock.move')
+        delivery_obj = self.pool.get('stock.delivery')
+        product_obj = self.pool.get('product.product')
+        currency_obj = self.pool.get('res.currency')
+        users_obj = self.pool.get('res.users')
+        uom_obj = self.pool.get('product.uom')
+        price_type_obj = self.pool.get('product.price.type')
+        sequence_obj = self.pool.get('ir.sequence')
+        wf_service = netsvc.LocalService("workflow")
+        partner_id = partial_datas.get('partner_id', False)
+        address_id = partial_datas.get('address_id', False)
+        delivery_date = partial_datas.get('delivery_date', False)
+        for pick in self.browse(cr, uid, ids, context=context):
+            new_picking = None
+            new_moves = []
+
+            complete, too_many, too_few = [], [], []
+            move_product_qty = {}
+            for move in pick.move_lines:
+                if move.state in ('done', 'cancel'):
+                    continue
+                partial_data = partial_datas.get('move%s'%(move.id), False)
+                assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
+                product_qty = partial_data.get('product_qty',0.0)
+                move_product_qty[move.id] = product_qty
+                product_uom = partial_data.get('product_uom',False)
+                product_price = partial_data.get('product_price',0.0)
+                product_currency = partial_data.get('product_currency',False)
+                if move.product_qty == product_qty:
+                    complete.append(move)
+                elif move.product_qty > product_qty:
+                    too_few.append(move)
+                else:
+                    too_many.append(move)
+
+                # Average price computation
+                if (pick.type == 'in') and (move.product_id.cost_method == 'average'):
+                    product = product_obj.browse(cr, uid, move.product_id.id)
+                    user = users_obj.browse(cr, uid, uid)
+                    context['currency_id'] = move.company_id.currency_id.id
+                    qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+                    pricetype = False
+                    if user.company_id.property_valuation_price_type:
+                        pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
+                    if pricetype and qty > 0:
+                        new_price = currency_obj.compute(cr, uid, product_currency,
+                                user.company_id.currency_id.id, product_price)
+                        new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
+                                product.uom_id.id)
+                        if product.qty_available <= 0:
+                            new_std_price = new_price
+                        else:
+                            # Get the standard price
+                            amount_unit = product.price_get(pricetype.field, context)[product.id]
+                            new_std_price = ((amount_unit * product.qty_available)\
+                                + (new_price * qty))/(product.qty_available + qty)
+
+                        # Write the field according to price type field
+                        product_obj.write(cr, uid, [product.id],
+                                {pricetype.field: new_std_price})
+                        move_obj.write(cr, uid, [move.id], {'price_unit': new_price})
+
+
+            for move in too_few:
+                product_qty = move_product_qty[move.id]
+                if not new_picking:
+
+                    new_picking = self.copy(cr, uid, pick.id,
+                            {
+                                'name': sequence_obj.get(cr, uid, 'stock.picking.%s'%(pick.type)),
+                                'move_lines' : [],
+                                'state':'draft',
+                            })
+                if product_qty != 0:
+
+                    new_obj = move_obj.copy(cr, uid, move.id,
+                        {
+                            'product_qty' : product_qty,
+                            'product_uos_qty': product_qty, #TODO: put correct uos_qty
+                            'picking_id' : new_picking,
+                            'state': 'assigned',
+                            'move_dest_id': False,
+                            'price_unit': move.price_unit,
+                        })
+
+                move_obj.write(cr, uid, [move.id],
+                        {
+                            'product_qty' : move.product_qty - product_qty,
+                            'product_uos_qty':move.product_qty - product_qty, #TODO: put correct uos_qty
+
+                        })
+
+            if new_picking:
+                move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
+                for move in too_many:
+                    product_qty = move_product_qty[move.id]
+                    move_obj.write(cr, uid, [move.id],
+                            {
+                                'product_qty' : product_qty,
+                                'product_uos_qty': product_qty, #TODO: put correct uos_qty
+                                'picking_id': new_picking,
+                            })
+            else:
+                for move in too_many:
+                    product_qty = move_product_qty[move.id]
+                    move_obj.write(cr, uid, [move.id],
+                            {
+                                'product_qty': product_qty,
+                                'product_uos_qty': product_qty #TODO: put correct uos_qty
+                            })
+
+            # At first we confirm the new picking (if necessary)
+            if new_picking:
+                wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
+            # Then we finish the good picking
+            if new_picking:
+                self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
+                self.action_move(cr, uid, [new_picking])
+                wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
+                wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
+                delivered_pack_id = new_picking
+            else:
+                self.action_move(cr, uid, [pick.id])
+                wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr)
+                delivered_pack_id = pick.id
+
+            delivered_pack = self.browse(cr, uid, delivered_pack_id, context=context)
+            delivery_id = delivery_obj.create(cr, uid, {
+                'name':  delivered_pack.name,
+                'partner_id': partner_id,
+                'address_id': address_id,
+                'date': delivery_date,
+                'picking_id' :  pick.id,
+                'move_delivered' : [(6,0, map(lambda x:x.id, delivered_pack.move_lines))]
+            }, context=context)
+            res[pick.id] = {'delivered_picking': delivered_pack.id or False}
+        return res
+
 stock_picking()
 
 
@@ -803,12 +1021,15 @@ class stock_production_lot(osv.osv):
     def name_get(self, cr, uid, ids, context={}):
         if not ids:
             return []
-        reads = self.read(cr, uid, ids, ['name', 'ref'], context)
+        reads = self.read(cr, uid, ids, ['name', 'prefix', 'ref'], context)
         res = []
         for record in reads:
             name = record['name']
+            prefix = record['prefix']
+            if prefix:
+                name = prefix + '/' + name
             if record['ref']:
-                name = name + '/' + record['ref']
+                name = '%s [%s]' % (name, record['ref'])
             res.append((record['id'], name))
         return res
 
@@ -825,7 +1046,6 @@ class stock_production_lot(osv.osv):
             ids = [ids]
 
         res = {}.fromkeys(ids, 0.0)
-
         if locations:
             cr.execute('''select
                     prodlot_id,
@@ -833,15 +1053,11 @@ class stock_production_lot(osv.osv):
                 from
                     stock_report_prodlots
                 where
-                    location_id in ('''+','.join(map(str, locations))+''')  and
-                    prodlot_id in  ('''+','.join(map(str, ids))+''')
-                group by
-                    prodlot_id
-            ''')
+                    location_id =ANY(%s) and prodlot_id =ANY(%s) group by prodlot_id''',(locations,ids,))
             res.update(dict(cr.fetchall()))
         return res
 
-    def _stock_search(self, cr, uid, obj, name, args):
+    def _stock_search(self, cr, uid, obj, name, args, context):
         locations = self.pool.get('stock.location').search(cr, uid, [('usage', '=', 'internal')])
         cr.execute('''select
                 prodlot_id,
@@ -849,22 +1065,21 @@ class stock_production_lot(osv.osv):
             from
                 stock_report_prodlots
             where
-                location_id in ('''+','.join(map(str, locations)) + ''')
-            group by
-                prodlot_id
-            having  sum(name)  ''' + str(args[0][1]) + ''' ''' + str(args[0][2])
-        )
+                location_id =ANY(%s) group by prodlot_id
+            having  sum(name) '''+ str(args[0][1]) + str(args[0][2]),(locations,))
         res = cr.fetchall()
         ids = [('id', 'in', map(lambda x: x[0], res))]
         return ids
 
     _columns = {
         'name': fields.char('Serial', size=64, required=True),
-        'ref': fields.char('Internal Ref', size=64),
+        'ref': fields.char('Internal Reference', size=256),
+        'prefix': fields.char('Prefix', size=64),
         'product_id': fields.many2one('product.product', 'Product', required=True),
         'date': fields.datetime('Created Date', required=True),
         'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Available", select="2"),
         'revisions': fields.one2many('stock.production.lot.revision', 'lot_id', 'Revisions'),
+        'company_id': fields.many2one('res.company','Company',select=1),
     }
     _defaults = {
         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
@@ -877,7 +1092,6 @@ class stock_production_lot(osv.osv):
 
 stock_production_lot()
 
-
 class stock_production_lot_revision(osv.osv):
     _name = 'stock.production.lot.revision'
     _description = 'Production lot revisions'
@@ -888,6 +1102,7 @@ class stock_production_lot_revision(osv.osv):
         'indice': fields.char('Revision', size=16),
         'author_id': fields.many2one('res.users', 'Author'),
         'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True, ondelete='cascade'),
+        'company_id': fields.related('lot_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
     }
 
     _defaults = {
@@ -897,6 +1112,22 @@ class stock_production_lot_revision(osv.osv):
 
 stock_production_lot_revision()
 
+class stock_delivery(osv.osv):
+
+    """ Tracability of partialdeliveries """
+
+    _name = "stock.delivery"
+    _description = "Delivery"
+    _columns = {
+        'name': fields.char('Name', size=60, required=True),
+        'date': fields.datetime('Date', required=True),
+        'partner_id': fields.many2one('res.partner', 'Partner', required=True),
+        'address_id': fields.many2one('res.partner.address', 'Address', required=True),
+        'move_delivered':fields.one2many('stock.move', 'delivered_id', 'Move Delivered'),
+        'picking_id': fields.many2one('stock.picking', 'Picking list'),
+
+    }
+stock_delivery()
 # ----------------------------------------------------
 # Move
 # ----------------------------------------------------
@@ -926,8 +1157,8 @@ class stock_move(osv.osv):
                ( \
                    (move.product_id.track_production and move.location_id.usage=='production') or \
                    (move.product_id.track_production and move.location_dest_id.usage=='production') or \
-                   (move.product_id.track_incoming and move.location_id.usage=='supplier') or \
-                   (move.product_id.track_outgoing and move.location_dest_id.usage=='customer') \
+                   (move.product_id.track_incoming and move.location_id.usage in ('supplier','internal')) or \
+                   (move.product_id.track_outgoing and move.location_dest_id.usage in ('customer','internal')) \
                )):
                 return False
         return True
@@ -942,7 +1173,7 @@ class stock_move(osv.osv):
         'name': fields.char('Name', size=64, required=True, select=True),
         'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
 
-        'date': fields.datetime('Date Created'),
+        'date': fields.datetime('Created Date'),
         'date_planned': fields.datetime('Date', required=True, help="Scheduled date for the movement of the products or real date if the move is done."),
 
         'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
@@ -951,11 +1182,11 @@ class stock_move(osv.osv):
         'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
         'product_uos_qty': fields.float('Quantity (UOS)'),
         'product_uos': fields.many2one('product.uom', 'Product UOS'),
-        'product_packaging': fields.many2one('product.packaging', 'Packaging'),
+        'product_packaging': fields.many2one('product.packaging', 'Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."),
 
-        'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
-        'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
-        'address_id': fields.many2one('res.partner.address', 'Dest. Address'),
+        'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
+        'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True, help="Location where the system will stock the finished products."),
+        'address_id': fields.many2one('res.partner.address', 'Dest. Address', help="Address where goods are to be delivered"),
 
         'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production"),
         'tracking_id': fields.many2one('stock.tracking', 'Tracking Lot', select=True, help="Tracking lot is the code that will be put on the logistical unit/pallet"),
@@ -966,13 +1197,22 @@ class stock_move(osv.osv):
         'move_dest_id': fields.many2one('stock.move', 'Dest. Move'),
         'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History'),
         'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History'),
-        'picking_id': fields.many2one('stock.picking', 'Packing List', select=True),
+        'picking_id': fields.many2one('stock.picking', 'Picking List', select=True),
 
         'note': fields.text('Notes'),
 
-        'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Status', readonly=True, select=True),
+        'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True,
+                                  help='When the stock move is created it is in the \'Draft\' state.\n After that it is set to \'Confirmed\' state.\n If stock is available state is set to \'Avaiable\'.\n When the picking it done the state is \'Done\'.\
+                                  \nThe state is \'Waiting\' if the move is waiting for another one.'),
         'price_unit': fields.float('Unit Price',
-            digits=(16, int(config['price_accuracy']))),
+            digits_compute= dp.get_precision('Account')),
+        'company_id': fields.many2one('res.company', 'Company', required=True,select=1),
+        'partner_id': fields.related('picking_id','address_id','partner_id',type='many2one', relation="res.partner", string="Partner"),
+        'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Orders"),
+        'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Origin"),
+        'move_stock_return_history': fields.many2many('stock.move', 'stock_move_return_history', 'move_id', 'return_move_id', 'Move Return History',readonly=True),
+        'delivered_id': fields.many2one('stock.delivery', 'Product delivered'),
+        'scraped': fields.boolean('Scraped'),
     }
     _constraints = [
         (_check_tracking,
@@ -1009,11 +1249,25 @@ class stock_move(osv.osv):
         'location_dest_id': _default_location_destination,
         'state': lambda *a: 'draft',
         'priority': lambda *a: '1',
+        'scraped' : lambda *a:False,
         'product_qty': lambda *a: 1.0,
         'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
+        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c)
     }
 
+    def copy(self, cr, uid, id, default=None, context={}):
+        if default is None:
+            default = {}
+        default = default.copy()
+        default['move_stock_return_history'] = []
+        return super(stock_move, self).copy(cr, uid, id, default, context)
+
+    def create(self, cr, user, vals, context=None):
+        if vals.get('move_stock_return_history',False):
+            vals['move_stock_return_history'] = []
+        return super(stock_move, self).create(cr, user, vals, context)
+
     def _auto_init(self, cursor, context):
         res = super(stock_move, self)._auto_init(cursor, context)
         cursor.execute('SELECT indexname \
@@ -1025,7 +1279,7 @@ class stock_move(osv.osv):
             cursor.commit()
         return res
 
-    def onchange_lot_id(self, cr, uid, ids, prodlot_id=False, product_qty=False, loc_id=False, context=None):
+    def onchange_lot_id(self, cr, uid, ids, prodlot_id=False, product_qty=False, loc_id=False, product_id=False, context=None):
         if not prodlot_id or not loc_id:
             return {}
         ctx = context and context.copy() or {}
@@ -1039,38 +1293,45 @@ class stock_move(osv.osv):
                 'message': 'You are moving %.2f products but only %.2f available in this lot.' % (product_qty, prodlot.stock_available or 0.0)
             }
         return {'warning': warning}
-    
+
     def onchange_quantity(self, cr, uid, ids, product_id, product_qty, product_uom, product_uos):
         result = {
                   'product_uos_qty': 0.00
           }
-        
+
         if (not product_id) or (product_qty <=0.0):
             return {'value': result}
-            
+
         product_obj = self.pool.get('product.product')
         uos_coeff = product_obj.read(cr, uid, product_id, ['uos_coeff'])
-        
+
         if product_uos and product_uom and (product_uom != product_uos):
             result['product_uos_qty'] = product_qty * uos_coeff['uos_coeff']
         else:
             result['product_uos_qty'] = product_qty
-        
+
         return {'value': result}
-    
-    def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False, loc_dest_id=False):
+
+    def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False, loc_dest_id=False, address_id=False):
         if not prod_id:
             return {}
-        product = self.pool.get('product.product').browse(cr, uid, [prod_id])[0]
+        lang = False
+        if address_id:
+            addr_rec = self.pool.get('res.partner.address').browse(cr, uid, address_id)
+            if addr_rec:
+                lang = addr_rec.partner_id and addr_rec.partner_id.lang or False
+        ctx = {'lang': lang}
+
+        product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0]
         uos_id  = product.uos_id and product.uos_id.id or False
         result = {
-            'name': product.partner_ref,
             'product_uom': product.uom_id.id,
             'product_uos': uos_id,
             'product_qty': 1.00,
             'product_uos_qty' : self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty']
         }
-        
+        if not ids:
+            result['name'] = product.partner_ref
         if loc_id:
             result['location_id'] = loc_id
         if loc_dest_id:
@@ -1091,8 +1352,8 @@ class stock_move(osv.osv):
             if dest:
                 if dest[1] == 'transparent':
                     self.write(cr, uid, [m.id], {
-                        'date_planned': (DateTime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
-                            DateTime.RelativeDateTime(days=dest[2] or 0)).strftime('%Y-%m-%d'),
+                        'date_planned': (datetime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
+                            relativedelta(days=dest[2] or 0)).strftime('%Y-%m-%d'),
                         'location_dest_id': dest[0].id})
                 else:
                     result.setdefault(m.picking_id, [])
@@ -1128,7 +1389,7 @@ class stock_move(osv.osv):
                         'picking_id': pickid,
                         'state': 'waiting',
                         'move_history_ids': [],
-                        'date_planned': (DateTime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + DateTime.RelativeDateTime(days=delay or 0)).strftime('%Y-%m-%d'),
+                        'date_planned': (datetime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
                         'move_history_ids2': []}
                     )
                     self.pool.get('stock.move').write(cr, uid, [move.id], {
@@ -1177,8 +1438,8 @@ class stock_move(osv.osv):
                 if res:
                     #_product_available_test depends on the next status for correct functioning
                     #the test does not work correctly if the same product occurs multiple times
-                    #in the same order. This is e.g. the case when using the button 'split in two' of 
-                    #the stock outgoing form                    
+                    #in the same order. This is e.g. the case when using the button 'split in two' of
+                    #the stock outgoing form
                     self.write(cr, uid, move.id, {'state':'assigned'})
                     done.append(move.id)
                     pickings[move.picking_id.id] = 1
@@ -1213,14 +1474,14 @@ class stock_move(osv.osv):
                     pickings[move.picking_id.id] = True
             if move.move_dest_id and move.move_dest_id.state == 'waiting':
                 self.write(cr, uid, [move.move_dest_id.id], {'state': 'assigned'})
-                if move.move_dest_id.picking_id:
+                if context.get('call_unlink',False) and move.move_dest_id.picking_id:
                     wf_service = netsvc.LocalService("workflow")
                     wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
         self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False})
-
-        for pick in self.pool.get('stock.picking').browse(cr, uid, pickings.keys()):
-            if all(move.state == 'cancel' for move in pick.move_lines):
-                self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'})
+        if not context.get('call_unlink',False):
+            for pick in self.pool.get('stock.picking').browse(cr, uid, pickings.keys()):
+                if all(move.state == 'cancel' for move in pick.move_lines):
+                    self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'})
 
         wf_service = netsvc.LocalService("workflow")
         for id in ids:
@@ -1230,7 +1491,9 @@ class stock_move(osv.osv):
 
     def action_done(self, cr, uid, ids, context=None):
         track_flag = False
+        picking_ids = []
         for move in self.browse(cr, uid, ids):
+            if move.picking_id: picking_ids.append(move.picking_id.id)
             if move.move_dest_id.id and (move.state != 'done'):
                 cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%s,%s)', (move.id, move.move_dest_id.id))
                 if move.move_dest_id.state in ('waiting', 'confirmed'):
@@ -1292,13 +1555,19 @@ class stock_move(osv.osv):
                     ref = move.picking_id and move.picking_id.name or False
                     product_uom_obj = self.pool.get('product.uom')
                     default_uom = move.product_id.uom_id.id
+                    date = time.strftime('%Y-%m-%d')
                     q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
                     if move.product_id.cost_method == 'average' and move.price_unit:
                         amount = q * move.price_unit
+                    # Base computation on valuation price type
                     else:
-                        amount = q * move.product_id.standard_price
+                        company_id=move.company_id.id
+                        context['currency_id']=move.company_id.currency_id.id
+                        pricetype=self.pool.get('product.price.type').browse(cr,uid,move.company_id.property_valuation_price_type.id)
+                        amount_unit=move.product_id.price_get(pricetype.field, context)[move.product_id.id]
+                        amount=amount_unit * q or 1.0
+                        # amount = q * move.product_id.standard_price
 
-                    date = time.strftime('%Y-%m-%d')
                     partner_id = False
                     if move.picking_id:
                         partner_id = move.picking_id.address_id and (move.picking_id.address_id.partner_id and move.picking_id.address_id.partner_id.id or False) or False
@@ -1329,6 +1598,10 @@ class stock_move(osv.osv):
                         'ref': ref,
                     })
         self.write(cr, uid, ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S')})
+        for pick in self.pool.get('stock.picking').browse(cr, uid, picking_ids):
+            if all(move.state == 'done' for move in pick.move_lines):
+                self.pool.get('stock.picking').action_done(cr, uid, [pick.id])
+
         wf_service = netsvc.LocalService("workflow")
         for id in ids:
             wf_service.trg_trigger(uid, 'stock.move', id, cr)
@@ -1342,6 +1615,314 @@ class stock_move(osv.osv):
         return super(stock_move, self).unlink(
             cr, uid, ids, context=context)
 
+    def _create_lot(self, cr, uid, ids, product_id, prefix=False):
+        prodlot_obj = self.pool.get('stock.production.lot')
+        ir_sequence_obj = self.pool.get('ir.sequence')
+        sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial')
+        if not sequence:
+            raise osv.except_osv(_('Error!'), _('No production sequence defined'))
+        prodlot_id = prodlot_obj.create(cr, uid, {'name': sequence, 'prefix': prefix}, {'product_id': product_id})
+        prodlot = prodlot_obj.browse(cr, uid, prodlot_id)
+        ref = ','.join(map(lambda x:str(x),ids))
+        if prodlot.ref:
+            ref = '%s, %s' % (prodlot.ref, ref)
+        prodlot_obj.write(cr, uid, [prodlot_id], {'ref': ref})
+        return prodlot_id
+
+
+    def action_scrap(self, cr, uid, ids, quantity, location_id, context=None):
+        '''
+        Move the scrap/damaged product into scrap location
+
+        @ param cr: the database cursor
+        @ param uid: the user id
+        @ param ids: ids of stock move object to be scraped
+        @ param quantity : specify scrap qty
+        @ param location_id : specify scrap location
+        @ param context: context arguments
+
+        @ return: Scraped lines
+        '''
+        if quantity <= 0:
+            raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+        res = []
+        for move in self.browse(cr, uid, ids, context=context):
+            move_qty = move.product_qty
+            uos_qty = quantity / move_qty * move.product_uos_qty
+            default_val = {
+                    'product_qty': quantity,
+                    'product_uos_qty': uos_qty,
+                    'state': move.state,
+                    'scraped' : True,
+                    'location_dest_id': location_id
+                }
+            new_move = self.copy(cr, uid, move.id, default_val)
+            #self.write(cr, uid, [new_move], {'move_history_ids':[(4,move.id)]}) #TODO : to track scrap moves
+            res += [new_move]
+        self.action_done(cr, uid, res)
+        return res
+
+    def action_split(self, cr, uid, ids, quantity, split_by_qty=1, prefix=False, with_lot=True, context=None):
+        '''
+        Split Stock Move lines into production lot which specified split by quantity.
+
+        @ param cr: the database cursor
+        @ param uid: the user id
+        @ param ids: ids of stock move object to be splited
+        @ param split_by_qty : specify split by qty
+        @ param prefix : specify prefix of production lot
+        @ param with_lot : if true, prodcution lot will assign for split line otherwise not.
+        @ param context: context arguments
+
+        @ return: splited move lines
+        '''
+
+        if quantity <= 0:
+            raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+
+        res = []
+
+        for move in self.browse(cr, uid, ids):
+            if split_by_qty <= 0 or quantity == 0:
+                return res
+
+            uos_qty = split_by_qty / move.product_qty * move.product_uos_qty
+
+            quantity_rest = quantity % split_by_qty
+            uos_qty_rest = split_by_qty / move.product_qty * move.product_uos_qty
+
+            update_val = {
+                'product_qty': split_by_qty,
+                'product_uos_qty': uos_qty,
+            }
+            for idx in range(int(quantity//split_by_qty)):
+                if not idx and move.product_qty<=quantity:
+                    current_move = move.id
+                else:
+                    current_move = self.copy(cr, uid, move.id, {'state': move.state})
+                res.append(current_move)
+                if with_lot:
+                    update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
+
+                self.write(cr, uid, [current_move], update_val)
+
+
+            if quantity_rest > 0:
+                idx = int(quantity//split_by_qty)
+                update_val['product_qty'] = quantity_rest
+                update_val['product_uos_qty'] = uos_qty_rest
+                if not idx and move.product_qty<=quantity:
+                    current_move = move.id
+                else:
+                    current_move = self.copy(cr, uid, move.id, {'state': move.state})
+
+                res.append(current_move)
+
+
+                if with_lot:
+                    update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
+
+                self.write(cr, uid, [current_move], update_val)
+        return res
+
+    def action_consume(self, cr, uid, ids, quantity, location_id=False,  context=None):
+        '''
+        Consumed product with specific quatity from specific source location
+
+        @ param cr: the database cursor
+        @ param uid: the user id
+        @ param ids: ids of stock move object to be consumed
+        @ param quantity : specify consume quantity
+        @ param location_id : specify source location
+        @ param context: context arguments
+
+        @ return: Consumed lines
+        '''
+        if not context:
+            context = {}
+
+        if quantity <= 0:
+            raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+
+        res = []
+        for move in self.browse(cr, uid, ids, context=context):
+            move_qty = move.product_qty
+            quantity_rest = move.product_qty
+
+            quantity_rest -= quantity
+            uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty
+            if quantity_rest <= 0:
+                quantity_rest = 0
+                uos_qty_rest = 0
+                quantity = move.product_qty
+
+            uos_qty = quantity / move_qty * move.product_uos_qty
+
+            if quantity_rest > 0:
+                default_val = {
+                    'product_qty': quantity,
+                    'product_uos_qty': uos_qty,
+                    'state': move.state,
+                    'location_id': location_id
+                }
+                if move.product_id.track_production and location_id:
+                    # IF product has checked track for production lot, move lines will be split by 1
+                    res += self.action_split(cr, uid, [move.id], quantity, split_by_qty=1, context=context)
+                else:
+                    current_move = self.copy(cr, uid, move.id, default_val)
+                    res += [current_move]
+
+                update_val = {}
+                update_val['product_qty'] = quantity_rest
+                update_val['product_uos_qty'] = uos_qty_rest
+                self.write(cr, uid, [move.id], update_val)
+
+            else:
+                quantity_rest = quantity
+                uos_qty_rest =  uos_qty
+
+                if move.product_id.track_production and location_id:
+                    res += self.split_lines(cr, uid, [move.id], quantity_rest, split_by_qty=1, context=context)
+                else:
+                    res += [move.id]
+                    update_val = {
+                        'product_qty' : quantity_rest,
+                        'product_uos_qty' : uos_qty_rest,
+                        'location_id': location_id
+                    }
+
+                    self.write(cr, uid, [move.id], update_val)
+
+        self.action_done(cr, uid, res)
+        return res
+
+    def do_partial(self, cr, uid, ids, partial_datas, context={}):
+        """
+        @ partial_datas : dict. contain details of partial picking
+                          like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
+        """
+        res = {}
+        picking_obj = self.pool.get('stock.picking')
+        delivery_obj = self.pool.get('stock.delivery')
+        product_obj = self.pool.get('product.product')
+        currency_obj = self.pool.get('res.currency')
+        users_obj = self.pool.get('res.users')
+        uom_obj = self.pool.get('product.uom')
+        price_type_obj = self.pool.get('product.price.type')
+        sequence_obj = self.pool.get('ir.sequence')
+        wf_service = netsvc.LocalService("workflow")
+        partner_id = partial_datas.get('partner_id', False)
+        address_id = partial_datas.get('address_id', False)
+        delivery_date = partial_datas.get('delivery_date', False)
+
+        new_moves = []
+
+        complete, too_many, too_few = [], [], []
+        move_product_qty = {}
+        for move in self.browse(cr, uid, ids, context=context):
+            if move.state in ('done', 'cancel'):
+                continue
+            partial_data = partial_datas.get('move%s'%(move.id), False)
+            assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
+            product_qty = partial_data.get('product_qty',0.0)
+            move_product_qty[move.id] = product_qty
+            product_uom = partial_data.get('product_uom',False)
+            product_price = partial_data.get('product_price',0.0)
+            product_currency = partial_data.get('product_currency',False)
+            if move.product_qty == product_qty:
+                complete.append(move)
+            elif move.product_qty > product_qty:
+                too_few.append(move)
+            else:
+                too_many.append(move)
+
+            # Average price computation
+            if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
+                product = product_obj.browse(cr, uid, move.product_id.id)
+                user = users_obj.browse(cr, uid, uid)
+                context['currency_id'] = move.company_id.currency_id.id
+                qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+                pricetype = False
+                if user.company_id.property_valuation_price_type:
+                    pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
+                if pricetype and qty > 0:
+                    new_price = currency_obj.compute(cr, uid, product_currency,
+                            user.company_id.currency_id.id, product_price)
+                    new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
+                            product.uom_id.id)
+                    if product.qty_available <= 0:
+                        new_std_price = new_price
+                    else:
+                        # Get the standard price
+                        amount_unit = product.price_get(pricetype.field, context)[product.id]
+                        new_std_price = ((amount_unit * product.qty_available)\
+                            + (new_price * qty))/(product.qty_available + qty)
+
+                    # Write the field according to price type field
+                    product_obj.write(cr, uid, [product.id],
+                            {pricetype.field: new_std_price})
+                    self.write(cr, uid, [move.id], {'price_unit': new_price})
+
+        for move in too_few:
+            product_qty = move_product_qty[move.id]
+            if product_qty != 0:
+                new_move = self.copy(cr, uid, move.id,
+                    {
+                        'product_qty' : product_qty,
+                        'product_uos_qty': product_qty,
+                        'picking_id' : move.picking_id.id,
+                        'state': 'assigned',
+                        'move_dest_id': False,
+                        'price_unit': move.price_unit,
+                    })
+                complete.append(self.browse(cr, uid, new_move))
+            self.write(cr, uid, move.id,
+                    {
+                        'product_qty' : move.product_qty - product_qty,
+                        'product_uos_qty':move.product_qty - product_qty,
+                    })
+
+
+        for move in too_many:
+            self.write(cr, uid, move.id,
+                    {
+                        'product_qty': product_qty,
+                        'product_uos_qty': product_qty
+                    })
+            complete.append(move)
+
+        for move in complete:
+            self.action_done(cr, uid, [move.id])
+
+            # TOCHECK : Done picking if all moves are done
+            cr.execute("""
+                SELECT move.id FROM stock_picking pick
+                RIGHT JOIN stock_move move ON move.picking_id = pick.id AND move.state = %s
+                WHERE pick.id = %s""",
+                        ('done', move.picking_id.id))
+            res = cr.fetchall()
+            if len(res) == len(move.picking_id.move_lines):
+                picking_obj.action_move(cr, uid, [move.picking_id.id])
+                wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
+
+        ref = {}
+        done_move_ids = []
+        for move in complete:
+            done_move_ids.append(move.id)
+            if move.picking_id.id not in ref:
+                delivery_id = delivery_obj.create(cr, uid, {
+                    'partner_id': partner_id,
+                    'address_id': address_id,
+                    'date': delivery_date,
+                    'name' : move.picking_id.name,
+                    'picking_id':  move.picking_id.id
+                }, context=context)
+                ref[move.picking_id.id] = delivery_id
+            delivery_obj.write(cr, uid, ref[move.picking_id.id], {
+                'move_delivered' : [(4,move.id)]
+            })
+        return done_move_ids
+
 stock_move()
 
 
@@ -1352,33 +1933,40 @@ class stock_inventory(osv.osv):
         'name': fields.char('Inventory', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'date': fields.datetime('Date create', required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'date_done': fields.datetime('Date done'),
-        'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=True, states={'draft': [('readonly', False)]}),
+        'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', states={'done': [('readonly', True)]}),
         'move_ids': fields.many2many('stock.move', 'stock_inventory_move_rel', 'inventory_id', 'move_id', 'Created Moves'),
-        'state': fields.selection( (('draft', 'Draft'), ('done', 'Done')), 'Status', readonly=True),
+        'state': fields.selection( (('draft', 'Draft'), ('done', 'Done'), ('cancel','Cancelled')), 'State', readonly=True),
+        'company_id': fields.many2one('res.company','Company',required=True,select=1),
     }
     _defaults = {
         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
         'state': lambda *a: 'draft',
+        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c)
     }
 
-    #
-    # Update to support tracking
-    #
+
+    def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
+        '''Creates a stock move from an inventory line'''
+        return self.pool.get('stock.move').create(cr, uid, move_vals)
+
     def action_done(self, cr, uid, ids, context=None):
         for inv in self.browse(cr, uid, ids):
             move_ids = []
             move_line = []
             for line in inv.inventory_line_id:
                 pid = line.product_id.id
-                price = line.product_id.standard_price or 0.0
+
+                # price = line.product_id.standard_price or 0.0
                 amount = self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid]
                 change = line.product_qty - amount
+                lot_id = line.prod_lot_id.id
                 if change:
                     location_id = line.product_id.product_tmpl_id.property_stock_inventory.id
                     value = {
                         'name': 'INV:' + str(line.inventory_id.id) + ':' + line.inventory_id.name,
                         'product_id': line.product_id.id,
                         'product_uom': line.product_uom.id,
+                        'prodlot_id': lot_id,
                         'date': inv.date,
                         'date_planned': inv.date,
                         'state': 'assigned'
@@ -1395,7 +1983,12 @@ class stock_inventory(osv.osv):
                             'location_id': line.location_id.id,
                             'location_dest_id': location_id,
                         })
-                    move_ids.append(self.pool.get('stock.move').create(cr, uid, value))
+                    if lot_id:
+                        value.update({
+                            'prodlot_id': lot_id,
+                            'product_qty': line.product_qty
+                        })
+                    move_ids.append(self._inventory_line_hook(cr, uid, line, value))
             if len(move_ids):
                 self.pool.get('stock.move').action_done(cr, uid, move_ids,
                         context=context)
@@ -1408,6 +2001,12 @@ class stock_inventory(osv.osv):
             self.write(cr, uid, [inv.id], {'state': 'draft'})
         return True
 
+    def action_cancel_inventary(self, cr, uid, ids, context={}):
+        for inv in self.browse(cr,uid,ids):
+            self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context)
+            self.write(cr, uid, [inv.id], {'state':'cancel'})
+        return True
+
 stock_inventory()
 
 
@@ -1419,7 +2018,10 @@ class stock_inventory_line(osv.osv):
         'location_id': fields.many2one('stock.location', 'Location', required=True),
         'product_id': fields.many2one('product.product', 'Product', required=True),
         'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
-        'product_qty': fields.float('Quantity')
+        'product_qty': fields.float('Quantity'),
+        'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
+        'prod_lot_id': fields.many2one('stock.production.lot', 'Production Lot', domain="[('product_id','=',product_id)]"),
+        'state': fields.related('inventory_id','state',type='char',string='State',readonly=True),
     }
 
     def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False):
@@ -1444,12 +2046,15 @@ class stock_warehouse(osv.osv):
     _columns = {
         'name': fields.char('Name', size=60, required=True),
 #       'partner_id': fields.many2one('res.partner', 'Owner'),
+        'company_id': fields.many2one('res.company','Company',required=True,select=1),
         'partner_address_id': fields.many2one('res.partner.address', 'Owner Address'),
         'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True),
         'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True),
         'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True),
     }
-
+    _defaults = {
+        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c),
+    }
 stock_warehouse()
 
 
@@ -1473,9 +2078,9 @@ class stock_picking_move_wizard(osv.osv_memory):
     _columns = {
         'name': fields.char('Name', size=64, invisible=True),
         #'move_lines': fields.one2many('stock.move', 'picking_id', 'Move lines',readonly=True),
-        'move_ids': fields.many2many('stock.move', 'picking_move_wizard_rel', 'picking_move_wizard_id', 'move_id', 'Move lines', required=True),
+        'move_ids': fields.many2many('stock.move', 'picking_move_wizard_rel', 'picking_move_wizard_id', 'move_id', 'Entry lines', required=True),
         'address_id': fields.many2one('res.partner.address', 'Dest. Address', invisible=True),
-        'picking_id': fields.many2one('stock.picking', 'Packing list', select=True, invisible=True),
+        'picking_id': fields.many2one('stock.picking', 'Picking list', select=True, invisible=True),
     }
     _defaults = {
         'picking_id': _get_picking,
@@ -1491,7 +2096,6 @@ class stock_picking_move_wizard(osv.osv_memory):
                 if line.picking_id:
                     picking_obj.write(cr, uid, [line.picking_id.id], {'move_lines': [(1, line.id, {'picking_id': act['picking_id']})]})
                     picking_obj.write(cr, uid, [act['picking_id']], {'move_lines': [(1, line.id, {'picking_id': act['picking_id']})]})
-                    cr.commit()
                     old_picking = picking_obj.read(cr, uid, [line.picking_id.id])[0]
                     if not len(old_picking['move_lines']):
                         picking_obj.write(cr, uid, [old_picking['id']], {'state': 'done'})
@@ -1502,31 +2106,73 @@ class stock_picking_move_wizard(osv.osv_memory):
 
 stock_picking_move_wizard()
 
-
-class report_stock_lines_date(osv.osv):
-    _name = "report.stock.lines.date"
-    _description = "Dates of Inventories"
+class report_products_to_received_planned(osv.osv):
+    _name = "report.products.to.received.planned"
+    _description = "Product to Received Vs Planned"
     _auto = False
     _columns = {
-        'id': fields.integer('Inventory Line Id', readonly=True),
-        'product_id': fields.integer('Product Id', readonly=True),
-        'create_date': fields.datetime('Latest Date of Inventory'),
-        }
+        'date':fields.date('Date'),
+        'qty': fields.integer('Actual Qty'),
+        'planned_qty': fields.integer('Planned Qty'),
+
+    }
 
     def init(self, cr):
+        tools.drop_view_if_exists(cr, 'report_products_to_received_planned')
         cr.execute("""
-            create or replace view report_stock_lines_date as (
-                select
-                l.id as id,
-                p.id as product_id,
-                max(l.create_date) as create_date
-                from
-                product_product p
-                left outer join
-                stock_inventory_line l on (p.id=l.product_id)
-                where l.create_date is not null
-                group by p.id,l.id
-            )""")
+            create or replace view report_products_to_received_planned as (
+               select stock.date, min(stock.id), sum(stock.product_qty) as qty, 0 as planned_qty
+                   from stock_picking picking
+                    inner join stock_move stock
+                    on picking.id = stock.picking_id and picking.type = 'in'
+                    where stock.date between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
+                    group by stock.date
+
+                    union
+
+               select stock.date_planned, min(stock.id), 0 as actual_qty, sum(stock.product_qty) as planned_qty
+                    from stock_picking picking
+                    inner join stock_move stock
+                    on picking.id = stock.picking_id and picking.type = 'in'
+                    where stock.date_planned between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
+        group by stock.date_planned
+                )
+        """)
+report_products_to_received_planned()
+
+class report_delivery_products_planned(osv.osv):
+    _name = "report.delivery.products.planned"
+    _description = "Number of Delivery products vs planned"
+    _auto = False
+    _columns = {
+        'date':fields.date('Date'),
+        'qty': fields.integer('Actual Qty'),
+        'planned_qty': fields.integer('Planned Qty'),
 
-report_stock_lines_date()
+    }
 
+    def init(self, cr):
+        tools.drop_view_if_exists(cr, 'report_delivery_products_planned')
+        cr.execute("""
+            create or replace view report_delivery_products_planned as (
+                select stock.date, min(stock.id), sum(stock.product_qty) as qty, 0 as planned_qty
+                   from stock_picking picking
+                    inner join stock_move stock
+                    on picking.id = stock.picking_id and picking.type = 'out'
+                    where stock.date between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
+                    group by stock.date
+
+                    union
+
+               select stock.date_planned, min(stock.id), 0 as actual_qty, sum(stock.product_qty) as planned_qty
+                    from stock_picking picking
+                    inner join stock_move stock
+                    on picking.id = stock.picking_id and picking.type = 'out'
+                    where stock.date_planned between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
+        group by stock.date_planned
+
+
+                )
+        """)
+report_delivery_products_planned()
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: