[IMP] stock: Fixed keyerror by checking type of ids for int and long(case:18253)
[odoo/odoo.git] / addons / stock / stock.py
index bf5347f..e61c3d9 100644 (file)
@@ -61,6 +61,7 @@ class stock_journal(osv.osv):
     }
 
 stock_journal()
+
 #----------------------------------------------------------
 # Stock Location
 #----------------------------------------------------------
@@ -69,7 +70,7 @@ class stock_location(osv.osv):
     _description = "Location"
     _parent_name = "location_id"
     _parent_store = True
-    _parent_order = 'name'
+    _parent_order = 'posz,name'
     _order = 'parent_left'
 
     def name_get(self, cr, uid, ids, context=None):
@@ -207,6 +208,8 @@ class stock_location(osv.osv):
         'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock", digits_compute=dp.get_precision('Account')),
         'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared between all companies'),
         'scrap_location': fields.boolean('Scrap Location', help='Check this box to allow using this location to put scrapped/damaged goods.'),
+        'valuation_in_account_id': fields.many2one('account.account', 'Stock Input Account',domain = [('type','=','other')], help='This account will be used to value stock moves that have this location as destination, instead of the stock output account from the product.'),
+        'valuation_out_account_id': fields.many2one('account.account', 'Stock Output Account',domain = [('type','=','other')], help='This account will be used to value stock moves that have this location as source, instead of the stock input account from the product.'),
     }
     _defaults = {
         'active': True,
@@ -271,7 +274,7 @@ class stock_location(osv.osv):
         context['compute_child'] = False
 
         if not product_ids:
-            product_ids = product_obj.search(cr, uid, [])
+            product_ids = product_obj.search(cr, uid, [], context={'active_test': False})
 
         products = product_obj.browse(cr, uid, product_ids, context=context)
         products_by_uom = {}
@@ -356,105 +359,118 @@ class stock_location(osv.osv):
     def _product_virtual_get(self, cr, uid, id, product_ids=False, context=None, states=['done']):
         return self._product_all_get(cr, uid, id, product_ids, context, ['confirmed', 'waiting', 'assigned', 'done'])
 
+    def _try_lock_product_reserve(self, cr, uid, location_ids, product_id, product_qty, context=None):
+        try:
+            # Must lock with a separate select query than the ones used in _product_reserve
+            # because FOR UPDATE can't be used with aggregation/group by's
+            # (i.e. when individual rows aren't identifiable).
+            # We use a SAVEPOINT to be able to rollback this part of the transaction without
+            # failing the whole transaction in case the LOCK cannot be acquired.
+            cr.execute("SAVEPOINT stock_location_product_reserve")
+            # We lock all stock moves in states we are going to consider in the
+            # calculation. By locking all DONE move we prevent other transactions
+            # from reserving the same products, as they won't be allowed to SELECT
+            # them until we're done.
+            cr.execute("""SELECT id FROM stock_move
+                          WHERE product_id=%s
+                          AND (
+                                (location_dest_id IN %s AND state = 'done')
+                                OR
+                                (location_id IN %s AND state in ('done', 'assigned'))
+                               )
+                          FOR UPDATE of stock_move NOWAIT""",
+                       (product_id, location_ids, location_ids), log_exceptions=False)
+        except Exception:
+            # Here it's likely that the FOR UPDATE NOWAIT failed to get the LOCK,
+            # so we ROLLBACK to the SAVEPOINT to restore the transaction to its earlier
+            # state, we return False as if the products were not available, and log it:
+            cr.execute("ROLLBACK TO stock_location_product_reserve")
+            logger = logging.getLogger('stock.location')
+            logger.warn("Failed attempt to reserve %s x product %s, likely due to another transaction already in progress. Next attempt is likely to work. Detailed error available at DEBUG level.", product_qty, product_id)
+            logger.debug("Trace of the failed product reservation attempt: ", exc_info=True)
+            return False
+        return True
+
     def _product_reserve(self, cr, uid, ids, product_id, product_qty, context=None, lock=False):
         """
         Attempt to find a quantity ``product_qty`` (in the product's default uom or the uom passed in ``context``) of product ``product_id``
         in locations with id ``ids`` and their child locations. If ``lock`` is True, the stock.move lines
         of product with id ``product_id`` in the searched location will be write-locked using Postgres's
-        "FOR UPDATE NOWAIT" option until the transaction is committed or rolled back, to prevent reservin
+        "FOR UPDATE NOWAIT" option until the transaction is committed or rolled back, to prevent reserving
         twice the same products.
         If ``lock`` is True and the lock cannot be obtained (because another transaction has locked some of
         the same stock.move lines), a log line will be output and False will be returned, as if there was
         not enough stock.
 
         :param product_id: Id of product to reserve
-        :param product_qty: Quantity of product to reserve (in the product's default uom or the uom passed in ``context``)
+        :param product_qty: Quantity of product to reserve (in the uom passed in ``context``)
         :param lock: if True, the stock.move lines of product with id ``product_id`` in all locations (and children locations) with ``ids`` will
                      be write-locked using postgres's "FOR UPDATE NOWAIT" option until the transaction is committed or rolled back. This is
                      to prevent reserving twice the same products.
-        :param context: optional context dictionary: it a 'uom' key is present it will be used instead of the default product uom to
-                        compute the ``product_qty`` and in the return value.
-        :return: List of tuples in the form (qty, location_id) with the (partial) quantities that can be taken in each location to
-                 reach the requested product_qty (``qty`` is expressed in the default uom of the product), of False if enough
-                 products could not be found, or the lock could not be obtained (and ``lock`` was True).
+        :param context: context dictionary with 'uom' key mapped to the ID of the UoM to use to compute the product quantities
+        :return: List of pairs (qty, location_id) with the (partial) quantities that can be taken in each location to
+                 reach the requested product_qty (expressed in the requested uom), or False if not enough
+                 products could be found, or the lock could not be obtained (and ``lock`` was True).
+                 sum(qty) == ``product_qty``.
         """
-        result = []
-        amount = 0.0
         if context is None:
             context = {}
-        for id in self.search(cr, uid, [('location_id', 'child_of', ids)]):
-            if lock:
-                try:
-                    # Must lock with a separate select query because FOR UPDATE can't be used with
-                    # aggregation/group by's (when individual rows aren't identifiable).
-                    # We use a SAVEPOINT to be able to rollback this part of the transaction without
-                    # failing the whole transaction in case the LOCK cannot be acquired.
-                    cr.execute("SAVEPOINT stock_location_product_reserve")
-                    cr.execute("""SELECT id FROM stock_move
-                                  WHERE product_id=%s AND
-                                          (
-                                            (location_dest_id=%s AND
-                                             location_id<>%s AND
-                                             state='done')
-                                            OR
-                                            (location_id=%s AND
-                                             location_dest_id<>%s AND
-                                             state in ('done', 'assigned'))
-                                          )
-                                  FOR UPDATE of stock_move NOWAIT""", (product_id, id, id, id, id), log_exceptions=False)
-                except Exception:
-                    # Here it's likely that the FOR UPDATE NOWAIT failed to get the LOCK,
-                    # so we ROLLBACK to the SAVEPOINT to restore the transaction to its earlier
-                    # state, we return False as if the products were not available, and log it:
-                    cr.execute("ROLLBACK TO stock_location_product_reserve")
-                    logger = logging.getLogger('stock.location')
-                    logger.warn("Failed attempt to reserve %s x product %s, likely due to another transaction already in progress. Next attempt is likely to work. Detailed error available at DEBUG level.", product_qty, product_id)
-                    logger.debug("Trace of the failed product reservation attempt: ", exc_info=True)
-                    return False
+        location_ids = self.search(cr, uid, [('location_id', 'child_of', ids)])
+        locations_tuple = tuple(location_ids)
+        if lock and not self._try_lock_product_reserve(cr, uid, locations_tuple, product_id, product_qty, context=context):
+            return False
 
-            # XXX TODO: rewrite this with one single query, possibly even the quantity conversion
-            cr.execute("""SELECT product_uom, sum(product_qty) AS product_qty
-                          FROM stock_move
-                          WHERE location_dest_id=%s AND
-                                location_id<>%s AND
-                                product_id=%s AND
-                                state='done'
-                          GROUP BY product_uom
-                       """,
-                       (id, id, product_id))
-            results = cr.dictfetchall()
-            cr.execute("""SELECT product_uom,-sum(product_qty) AS product_qty
-                          FROM stock_move
-                          WHERE location_id=%s AND
-                                location_dest_id<>%s AND
-                                product_id=%s AND
-                                state in ('done', 'assigned')
-                          GROUP BY product_uom
-                       """,
-                       (id, id, product_id))
-            results += cr.dictfetchall()
-
-            total = 0.0
-            results2 = 0.0
-            for r in results:
-                amount = self.pool.get('product.uom')._compute_qty(cr, uid, r['product_uom'], r['product_qty'], context.get('uom', False))
-                results2 += amount
-                total += amount
-
-            if total <= 0.0:
+        # Giant query to obtain triplets of (product_uom, product_qty, location_id) summing all relevant
+        # stock moves quantities per location,  with incoming quantities taken positive,
+        # and outgoing taken negative.
+        cr.execute("""SELECT x.product_uom, SUM(x.coeff * x.product_qty) as product_qty, x.loc_id as location_id
+                      FROM (
+                          SELECT 1.0 as coeff, product_uom, location_dest_id as loc_id,
+                                 sum(product_qty) AS product_qty
+                              FROM stock_move
+                              WHERE location_dest_id in %s AND
+                                    location_id != location_dest_id AND
+                                    product_id = %s AND
+                                    state = 'done'
+                              GROUP BY location_dest_id, product_uom
+                          UNION
+                          SELECT -1.0 as coeff, product_uom, location_id as loc_id,
+                                 sum(product_qty) AS product_qty
+                              FROM stock_move
+                              WHERE location_id in %s AND
+                                    location_id != location_dest_id AND
+                                    product_id = %s AND
+                                    state in ('done', 'assigned')
+                              GROUP BY location_id, product_uom
+                      ) AS x
+                      GROUP BY x.loc_id, x.product_uom
+                   """,
+                   (locations_tuple, product_id, locations_tuple, product_id))
+        sum_rows = cr.fetchall()
+
+        qty_by_location = {}
+        ProductUom = self.pool.get('product.uom')
+        target_uom = context.get('uom')
+        # Convert all UoMs into target UoM
+        for uom_id, qty, loc_id in sum_rows:
+            qty_by_location.setdefault(loc_id,0.0)
+            qty_by_location[loc_id] += ProductUom._compute_qty(cr, uid, uom_id, qty, target_uom)
+
+        # to compute final result we handle locations in the
+        # order in which they were returned by the original search().
+        result = []
+        for loc_id in location_ids:
+            if loc_id not in qty_by_location:
+                #skip location without this product
                 continue
-
-            amount = results2
-            if amount > 0:
-                if amount > min(total, product_qty):
-                    amount = min(product_qty, total)
-                result.append((amount, id))
-                product_qty -= amount
-                total -= amount
-                if product_qty <= 0.0:
-                    return result
-                if total <= 0.0:
-                    continue
+            qty = qty_by_location[loc_id]
+            if qty <= 0.0:
+                continue
+            qty = min(product_qty, qty)
+            result.append((qty, loc_id))
+            product_qty -= qty
+            if product_qty <= 0.0:
+                return result
         return False
 
 stock_location()
@@ -474,7 +490,10 @@ class stock_tracking(osv.osv):
 
     def make_sscc(self, cr, uid, context=None):
         sequence = self.pool.get('ir.sequence').get(cr, uid, 'stock.lot.tracking')
-        return sequence + str(self.checksum(sequence))
+        try:
+            return sequence + str(self.checksum(sequence))
+        except Exception:
+            return sequence
 
     _columns = {
         'name': fields.char('Pack Reference', size=64, required=True, select=True),
@@ -486,7 +505,7 @@ class stock_tracking(osv.osv):
     _defaults = {
         'active': 1,
         'name': make_sscc,
-        'date': time.strftime('%Y-%m-%d %H:%M:%S'),
+        '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=100):
@@ -514,8 +533,8 @@ class stock_tracking(osv.osv):
         @param context: A standard dictionary
         @return: A dictionary of values
         """
-        value={}
-        value=self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
+        value = {}
+        value = self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
         return value
 stock_tracking()
 
@@ -649,24 +668,25 @@ class stock_picking(osv.osv):
         'move_type': 'direct',
         'type': 'in',
         'invoice_state': 'none',
-        'date': 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.picking', context=c)
     }
     def action_process(self, cr, uid, ids, context=None):
+        if context is None: context = {}
+        partial_id = self.pool.get("stock.partial.picking").create(
+            cr, uid, {}, context=dict(context, active_ids=ids))
         return {
             'name':_("Products to Process"),
             'view_mode': 'form',
             'view_id': False,
             'view_type': 'form',
             'res_model': 'stock.partial.picking',
+            'res_id': partial_id,
             'type': 'ir.actions.act_window',
             'nodestroy': True,
             'target': 'new',
             'domain': '[]',
-            'context': {
-                'active_id': ids[0],
-                'active_ids':ids
-            }
+            'context': dict(context, active_ids=ids)
         }
 
     def copy(self, cr, uid, id, default=None, context=None):
@@ -674,13 +694,13 @@ class stock_picking(osv.osv):
             default = {}
         default = default.copy()
         picking_obj = self.browse(cr, uid, id, context=context)
-        move_obj=self.pool.get('stock.move')
+        move_obj = self.pool.get('stock.move')
         if ('name' not in default) or (picking_obj.name=='/'):
             seq_obj_name =  'stock.picking.' + picking_obj.type
             default['name'] = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name)
             default['origin'] = ''
             default['backorder_id'] = False
-        res=super(stock_picking, self).copy(cr, uid, id, default, context)
+        res = super(stock_picking, self).copy(cr, uid, id, default, context)
         if res:
             picking_obj = self.browse(cr, uid, res, context=context)
             for move in picking_obj.move_lines:
@@ -719,11 +739,12 @@ class stock_picking(osv.osv):
         """ Changes state of picking to available if all moves are confirmed.
         @return: True
         """
+        move_obj = self.pool.get('stock.move')
         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 enough stock, unable to reserve the products.'))
-            self.pool.get('stock.move').action_assign(cr, uid, move_ids)
+            move_obj.action_assign(cr, uid, move_ids)
         return True
 
     def force_assign(self, cr, uid, ids, *args):
@@ -731,9 +752,10 @@ class stock_picking(osv.osv):
         @return: True
         """
         wf_service = netsvc.LocalService("workflow")
+        move_obj = self.pool.get('stock.move')
         for pick in self.browse(cr, uid, ids):
             move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed','waiting']]
-            self.pool.get('stock.move').force_assign(cr, uid, move_ids)
+            move_obj.force_assign(cr, uid, move_ids)
             wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
         return True
 
@@ -753,33 +775,24 @@ class stock_picking(osv.osv):
         """ Validates picking directly from draft state.
         @return: True
         """
-        if context is None:
-            context = {}
         wf_service = netsvc.LocalService("workflow")
+        move_obj = self.pool.get('stock.move')
         self.draft_force_assign(cr, uid, ids)
         for pick in self.browse(cr, uid, ids, context=context):
             move_ids = [x.id for x in pick.move_lines]
-            self.pool.get('stock.move').force_assign(cr, uid, move_ids)
+            move_obj.force_assign(cr, uid, move_ids)
             wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
-        context.update({'active_ids':ids})
-        return {
-                'name': 'Make Picking',
-                'view_type': 'form',
-                'view_mode': 'form',
-                'res_model': 'stock.partial.picking',
-                'type': 'ir.actions.act_window',
-                'target': 'new',
-                'nodestroy': True,
-                'context':context
-            }
+        return self.action_process(
+            cr, uid, ids, context=context)
     def cancel_assign(self, cr, uid, ids, *args):
         """ Cancels picking and moves.
         @return: True
         """
         wf_service = netsvc.LocalService("workflow")
+        move_obj = self.pool.get('stock.move')
         for pick in self.browse(cr, uid, ids):
             move_ids = [x.id for x in pick.move_lines]
-            self.pool.get('stock.move').cancel_assign(cr, uid, move_ids)
+            move_obj.cancel_assign(cr, uid, move_ids)
             wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
         return True
 
@@ -795,13 +808,15 @@ class stock_picking(osv.osv):
         """ Tests whether the move is in done or cancel state or not.
         @return: True or False
         """
-        move_ids = self.pool.get('stock.move').search(cr, uid, [('picking_id', 'in', ids)])
-        for move in self.pool.get('stock.move').browse(cr, uid, move_ids):
+        move_obj = self.pool.get('stock.move')
+        move_ids = move_obj.search(cr, uid, [('picking_id', 'in', ids)])
+        for move in move_obj.browse(cr, uid, move_ids):
             if move.state not in ('done', 'cancel'):
+
                 if move.product_qty != 0.0:
                     return False
                 else:
-                    move.write(cr, uid, [move.id], {'state': 'done'})
+                    move.write({'state': 'done'})
         return True
 
     def test_assigned(self, cr, uid, ids):
@@ -823,9 +838,10 @@ class stock_picking(osv.osv):
         """ Changes picking state to cancel.
         @return: True
         """
+        move_obj = self.pool.get('stock.move')
         for pick in self.browse(cr, uid, ids, context=context):
             ids2 = [move.id for move in pick.move_lines]
-            self.pool.get('stock.move').action_cancel(cr, uid, ids2, context)
+            move_obj.action_cancel(cr, uid, ids2, context)
         self.write(cr, uid, ids, {'state': 'cancel', 'invoice_state': 'none'})
         self.log_picking(cr, uid, ids, context=context)
         return True
@@ -844,13 +860,14 @@ class stock_picking(osv.osv):
         """ Changes move state to assigned.
         @return: True
         """
+        move_obj = self.pool.get('stock.move')
         for pick in self.browse(cr, uid, ids, context=context):
             todo = []
             for move in pick.move_lines:
                 if move.state == 'assigned':
                     todo.append(move.id)
             if len(todo):
-                self.pool.get('stock.move').action_done(cr, uid, todo,
+                move_obj.action_done(cr, uid, todo,
                         context=context)
         return True
 
@@ -870,7 +887,6 @@ class stock_picking(osv.osv):
         """
         partner_obj = self.pool.get('res.partner')
         partner = picking.address_id.partner_id
-
         return partner_obj.address_get(cr, uid, [partner.id],
                 ['contact', 'invoice'])
 
@@ -965,6 +981,7 @@ class stock_picking(osv.osv):
 
         invoice_obj = self.pool.get('account.invoice')
         invoice_line_obj = self.pool.get('account.invoice.line')
+        address_obj = self.pool.get('res.partner.address')
         invoices_group = {}
         res = {}
         inv_type = type
@@ -972,7 +989,7 @@ class stock_picking(osv.osv):
             if picking.invoice_state != '2binvoiced':
                 continue
             payment_term_id = False
-            partner = picking.address_id and picking.address_id.partner_id
+            partner =  picking.address_id and picking.address_id.partner_id
             if not partner:
                 raise osv.except_osv(_('Error, no partner !'),
                     _('Please put a partner on the picking list if you want to generate invoice.'))
@@ -988,6 +1005,7 @@ class stock_picking(osv.osv):
 
             address_contact_id, address_invoice_id = \
                     self._get_address_invoice(cr, uid, picking).values()
+            address = address_obj.browse(cr, uid, address_contact_id, context=context)
 
             comment = self._get_comment_invoice(cr, uid, picking)
             if group and partner.id in invoices_group:
@@ -1007,7 +1025,7 @@ class stock_picking(osv.osv):
                     'origin': (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
                     'type': inv_type,
                     'account_id': account_id,
-                    'partner_id': partner.id,
+                    'partner_id': address.partner_id.id,
                     'address_invoice_id': address_invoice_id,
                     'address_contact_id': address_contact_id,
                     'comment': comment,
@@ -1060,7 +1078,6 @@ class stock_picking(osv.osv):
                 uos_id = move_line.product_uos and move_line.product_uos.id or False
                 if not uos_id and inv_type in ('out_invoice', 'out_refund'):
                     uos_id = move_line.product_uom.id
-
                 account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id)
                 invoice_line_id = invoice_line_obj.create(cr, uid, {
                     'name': name,
@@ -1164,17 +1181,19 @@ class stock_picking(osv.osv):
             complete, too_many, too_few = [], [], []
             move_product_qty = {}
             prodlot_ids = {}
+            product_avail = {}
             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, _('Missing partial picking data for move #%s') % (move.id)
-                product_qty = partial_data.get('product_qty',0.0)
+                partial_data = partial_datas.get('move%s'%(move.id), {})
+                #Commented in order to process the less number of stock moves from partial picking wizard
+                #assert partial_data, _('Missing partial picking data for move #%s') % (move.id)
+                product_qty = partial_data.get('product_qty') or 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)
-                prodlot_id = partial_data.get('prodlot_id')
+                product_uom = partial_data.get('product_uom') or False
+                product_price = partial_data.get('product_price') or 0.0
+                product_currency = partial_data.get('product_currency') or False
+                prodlot_id = partial_data.get('prodlot_id') or False
                 prodlot_ids[move.id] = prodlot_id
                 if move.product_qty == product_qty:
                     complete.append(move)
@@ -1189,6 +1208,12 @@ class stock_picking(osv.osv):
                     move_currency_id = move.company_id.currency_id.id
                     context['currency_id'] = move_currency_id
                     qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+
+                    if product.id in product_avail:
+                        product_avail[product.id] += qty
+                    else:
+                        product_avail[product.id] = product.qty_available
+
                     if qty > 0:
                         new_price = currency_obj.compute(cr, uid, product_currency,
                                 move_currency_id, product_price)
@@ -1199,9 +1224,8 @@ class stock_picking(osv.osv):
                         else:
                             # Get the standard price
                             amount_unit = product.price_get('standard_price', context)[product.id]
-                            new_std_price = ((amount_unit * product.qty_available)\
-                                + (new_price * qty))/(product.qty_available + qty)
-
+                            new_std_price = ((amount_unit * product_avail[product.id])\
+                                + (new_price * qty))/(product_avail[product.id] + qty)
                         # Write the field according to price type field
                         product_obj.write(cr, uid, [product.id], {'standard_price': new_std_price})
 
@@ -1244,9 +1268,9 @@ class stock_picking(osv.osv):
 
             if new_picking:
                 move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
-                for move in complete:
-                    if prodlot_ids.get(move.id):
-                        move_obj.write(cr, uid, move.id, {'prodlot_id': prodlot_ids[move.id]})
+            for move in complete:
+                if prodlot_ids.get(move.id):
+                    move_obj.write(cr, uid, [move.id], {'prodlot_id': prodlot_ids[move.id]})
             for move in too_many:
                 product_qty = move_product_qty[move.id]
                 defaults = {
@@ -1287,6 +1311,9 @@ class stock_picking(osv.osv):
         @param ids: List of Picking Ids
         @param context: A standard dictionary for contextual values
         """
+        if context is None:
+            context = {}
+        data_obj = self.pool.get('ir.model.data')
         for pick in self.browse(cr, uid, ids, context=context):
             msg=''
             if pick.auto_picking:
@@ -1296,6 +1323,11 @@ class stock_picking(osv.osv):
                 'in':_('Reception'),
                 'internal': _('Internal picking'),
             }
+            view_list = {
+                'out': 'view_picking_out_form',
+                'in': 'view_picking_in_form',
+                'internal': 'view_picking_form',
+            }
             message = type_list.get(pick.type, _('Document')) + " '" + (pick.name or '?') + "' "
             if pick.min_date:
                 msg= _(' for the ')+ datetime.strptime(pick.min_date, '%Y-%m-%d %H:%M:%S').strftime('%m/%d/%Y')
@@ -1306,8 +1338,10 @@ class stock_picking(osv.osv):
                 'done': _('is done.'),
                 'draft':_('is in draft state.'),
             }
+            res = data_obj.get_object_reference(cr, uid, 'stock', view_list.get(pick.type, 'view_picking_form'))
+            context.update({'view_id': res and res[1] or False})
             message += state_list[pick.state]
-            self.log(cr, uid, pick.id, message)
+            self.log(cr, uid, pick.id, message, context=context)
         return True
 
 stock_picking()
@@ -1390,7 +1424,7 @@ class stock_production_lot(osv.osv):
         'move_ids': fields.one2many('stock.move', 'prodlot_id', 'Moves for this production lot', readonly=True),
     }
     _defaults = {
-        'date':  time.strftime('%Y-%m-%d %H:%M:%S'),
+        'date':  lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
         'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'stock.lot.serial'),
         'product_id': lambda x, y, z, c: c.get('product_id', False),
     }
@@ -1406,7 +1440,7 @@ class stock_production_lot(osv.osv):
         @param context: A standard dictionary
         @return: A dictionary of values
         """
-        value=self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
+        value = self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
         return value
 stock_production_lot()
 
@@ -1421,12 +1455,12 @@ class stock_production_lot_revision(osv.osv):
         'indice': fields.char('Revision Number', 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),
+        'company_id': fields.related('lot_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
     }
 
     _defaults = {
         'author_id': lambda x, y, z, c: z,
-        'date': time.strftime('%Y-%m-%d'),
+        'date': lambda *a: time.strftime('%Y-%m-%d'),
     }
 
 stock_production_lot_revision()
@@ -1450,6 +1484,25 @@ class stock_move(osv.osv):
     _order = 'date_expected desc, id'
     _log_create = False
 
+    def action_partial_move(self, cr, uid, ids, context=None):
+        if context is None: context = {}
+        partial_id = self.pool.get("stock.partial.move").create(
+            cr, uid, {}, context=context)
+        return {
+            'name':_("Products to Process"),
+            'view_mode': 'form',
+            'view_id': False,
+            'view_type': 'form',
+            'res_model': 'stock.partial.move',
+            'res_id': partial_id,
+            'type': 'ir.actions.act_window',
+            'nodestroy': True,
+            'target': 'new',
+            'domain': '[]',
+            'context': context
+        }
+
+
     def name_get(self, cr, uid, ids, context=None):
         res = []
         for line in self.browse(cr, uid, ids, context=context):
@@ -1484,9 +1537,9 @@ class stock_move(osv.osv):
     _columns = {
         'name': fields.char('Name', size=64, required=True, select=True),
         'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
-        'create_date': fields.datetime('Creation Date', readonly=True),
-        'date': fields.datetime('Date', required=True, help="Move date: scheduled date until move is done, then date of actual move processing", readonly=True),
-        'date_expected': fields.datetime('Scheduled Date', states={'done': [('readonly', True)]},required=True, help="Scheduled date for the processing of this move"),
+        'create_date': fields.datetime('Creation Date', readonly=True, select=True),
+        'date': fields.datetime('Date', required=True, select=True, help="Move date: scheduled date until move is done, then date of actual move processing", readonly=True),
+        'date_expected': fields.datetime('Scheduled Date', states={'done': [('readonly', True)]},required=True, select=True, help="Scheduled date for the processing of this move"),
         'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type','<>','service')],states={'done': [('readonly', True)]}),
 
         'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM'), required=True,states={'done': [('readonly', True)]}),
@@ -1509,9 +1562,9 @@ class stock_move(osv.osv):
         'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History (parent moves)'),
         'picking_id': fields.many2one('stock.picking', 'Reference', select=True,states={'done': [('readonly', True)]}),
         'note': fields.text('Notes'),
-        '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 \'Available\'.\n When the picking is done the state is \'Done\'.\
-                                  \nThe state is \'Waiting\' if the move is waiting for another one.'),
+        'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Not Available'), ('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 \'Not Available\' state if the scheduler did not find the products.\n When products are reserved it is set to \'Available\'.\n When the picking is done the state is \'Done\'.\
+              \nThe state is \'Waiting\' if the move is waiting for another one.'),
         'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Account'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"),
         'price_currency_id': fields.many2one('res.currency', 'Currency for average price', help="Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used)"),
         'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
@@ -1520,7 +1573,7 @@ class stock_move(osv.osv):
         'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Origin", store=True),
 
         # used for colors in tree views:
-        'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped'),
+        'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped', readonly=True),
     }
     _constraints = [
         (_check_tracking,
@@ -1560,7 +1613,9 @@ class stock_move(osv.osv):
             except:
                 pass
         if context.get('address_in_id', False):
-            return self.pool.get('res.partner.address').browse(cr, uid, context['address_in_id'], context).partner_id.property_stock_supplier.id
+            part_obj_add = self.pool.get('res.partner.address').browse(cr, uid, context['address_in_id'], context=context)
+            if part_obj_add.partner_id:
+                return part_obj_add.partner_id.property_stock_supplier.id
         return False
 
     _defaults = {
@@ -1570,12 +1625,14 @@ class stock_move(osv.osv):
         'priority': '1',
         'product_qty': 1.0,
         'scrapped' :  False,
-        'date': 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),
-        'date_expected': time.strftime('%Y-%m-%d %H:%M:%S'),
+        'date_expected': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
     }
 
     def write(self, cr, uid, ids, vals, context=None):
+        if isinstance(ids, (int, long)):
+            ids = [ids]
         if uid != 1:
             frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id'])
             for move in self.browse(cr, uid, ids, context=context):
@@ -1602,7 +1659,7 @@ class stock_move(osv.osv):
         return res
 
     def onchange_lot_id(self, cr, uid, ids, prodlot_id=False, product_qty=False,
-                        loc_id=False, product_id=False, context=None):
+                        loc_id=False, product_id=False, uom_id=False, context=None):
         """ On change of production lot gives a warning message.
         @param prodlot_id: Changed production lot id
         @param product_qty: Quantity of product
@@ -1614,13 +1671,19 @@ class stock_move(osv.osv):
             return {}
         ctx = context and context.copy() or {}
         ctx['location_id'] = loc_id
-        prodlot = self.pool.get('stock.production.lot').browse(cr, uid, prodlot_id, ctx)
-        location = self.pool.get('stock.location').browse(cr, uid, loc_id)
+        ctx.update({'raise-exception': True})
+        uom_obj = self.pool.get('product.uom')
+        product_obj = self.pool.get('product.product')
+        product_uom = product_obj.browse(cr, uid, product_id, context=ctx).uom_id
+        prodlot = self.pool.get('stock.production.lot').browse(cr, uid, prodlot_id, context=ctx)
+        location = self.pool.get('stock.location').browse(cr, uid, loc_id, context=ctx)
+        uom = uom_obj.browse(cr, uid, uom_id, context=ctx)
+        amount_actual = uom_obj._compute_qty_obj(cr, uid, product_uom, prodlot.stock_available, uom, context=ctx)
         warning = {}
-        if (location.usage == 'internal') and (product_qty > (prodlot.stock_available or 0.0)):
+        if (location.usage == 'internal') and (product_qty > (amount_actual or 0.0)):
             warning = {
                 'title': _('Insufficient Stock in Lot !'),
-                'message': _('You are moving %.2f products but only %.2f available in this lot.') % (product_qty, prodlot.stock_available or 0.0)
+                'message': _('You are moving %.2f %s products but only %.2f %s available in this lot.') % (product_qty, uom.name, amount_actual, uom.name)
             }
         return {'warning': warning}
 
@@ -1745,12 +1808,13 @@ class stock_move(osv.osv):
                     result.setdefault(m.picking_id, [])
                     result[m.picking_id].append( (m, dest) )
         return result
-    def _create_chained_picking(self, cr, uid, pick_name,picking,ptype,move, context=None):
+
+    def _create_chained_picking(self, cr, uid, pick_name, picking, ptype, move, context=None):
         res_obj = self.pool.get('res.company')
         picking_obj = self.pool.get('stock.picking')
         pick_id= picking_obj.create(cr, uid, {
                                 'name': pick_name,
-                                'origin': str(picking.origin or ''),
+                                'origin': tools.ustr(picking.origin or ''),
                                 'type': ptype,
                                 'note': picking.note,
                                 'move_type': picking.move_type,
@@ -1762,50 +1826,60 @@ class stock_move(osv.osv):
                                 'date': picking.date,
                             })
         return pick_id
+    def create_chained_picking(self, cr, uid, moves, context=None):
+        res_obj = self.pool.get('res.company')
+        location_obj = self.pool.get('stock.location')
+        move_obj = self.pool.get('stock.move')
+        picking_obj = self.pool.get('stock.picking')
+        wf_service = netsvc.LocalService("workflow")
+        new_moves = []
+        if context is None:
+            context = {}
+        seq_obj = self.pool.get('ir.sequence')
+        for picking, todo in self._chain_compute(cr, uid, moves, context=context).items():
+            ptype = todo[0][1][5] and todo[0][1][5] or location_obj.picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0])
+            if picking:
+                # name of new picking according to its type
+                new_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + ptype)
+                pickid = self._create_chained_picking(cr, uid, new_pick_name, picking, ptype, todo, context=context)
+                # Need to check name of old picking because it always considers picking as "OUT" when created from Sale Order
+                old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id)
+                if old_ptype != picking.type:
+                    old_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + old_ptype)
+                    picking_obj.write(cr, uid, [picking.id], {'name': old_pick_name}, context=context)
+            else:
+                pickid = False
+            for move, (loc, dummy, delay, dummy, company_id, ptype) in todo:
+                new_id = move_obj.copy(cr, uid, move.id, {
+                    'location_id': move.location_dest_id.id,
+                    'location_dest_id': loc.id,
+                    'date_moved': time.strftime('%Y-%m-%d'),
+                    'picking_id': pickid,
+                    'state': 'waiting',
+                    'company_id': company_id or res_obj._company_default_get(cr, uid, 'stock.company', context=context)  ,
+                    'move_history_ids': [],
+                    'date': (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
+                    'move_history_ids2': []}
+                )
+                move_obj.write(cr, uid, [move.id], {
+                    'move_dest_id': new_id,
+                    'move_history_ids': [(4, new_id)]
+                })
+                new_moves.append(self.browse(cr, uid, [new_id])[0])
+            if pickid:
+                wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
+        if new_moves:
+            new_moves += self.create_chained_picking(cr, uid, new_moves, context)
+        return new_moves
+
     def action_confirm(self, cr, uid, ids, context=None):
         """ Confirms stock move.
         @return: List of ids.
         """
         moves = self.browse(cr, uid, ids, context=context)
         self.write(cr, uid, ids, {'state': 'confirmed'})
-        res_obj = self.pool.get('res.company')
-        location_obj = self.pool.get('stock.location')
-        move_obj = self.pool.get('stock.move')
-        wf_service = netsvc.LocalService("workflow")
 
-        def create_chained_picking(self, cr, uid, moves, context=None):
-            new_moves = []
-            if context is None:
-                context = {}
-            for picking, todo in self._chain_compute(cr, uid, moves, context=context).items():
-                ptype = todo[0][1][5] and todo[0][1][5] or location_obj.picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0])
-                pick_name = picking.name or ''
-                if picking:
-                    pickid = self._create_chained_picking(cr, uid, pick_name,picking,ptype,todo,context)
-                else:
-                    pickid = False
-                for move, (loc, dummy, delay, dummy, company_id, ptype) in todo:
-                    new_id = move_obj.copy(cr, uid, move.id, {
-                        'location_id': move.location_dest_id.id,
-                        'location_dest_id': loc.id,
-                        'date_moved': time.strftime('%Y-%m-%d'),
-                        'picking_id': pickid,
-                        'state': 'waiting',
-                        'company_id': company_id or res_obj._company_default_get(cr, uid, 'stock.company', context=context)  ,
-                        'move_history_ids': [],
-                        'date': (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
-                        'move_history_ids2': []}
-                    )
-                    move_obj.write(cr, uid, [move.id], {
-                        'move_dest_id': new_id,
-                        'move_history_ids': [(4, new_id)]
-                    })
-                    new_moves.append(self.browse(cr, uid, [new_id])[0])
-                if pickid:
-                    wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
-            if new_moves:
-                create_chained_picking(self, cr, uid, new_moves, context)
-        create_chained_picking(self, cr, uid, moves, context)
+        self.create_chained_picking(cr, uid, moves, context)
         return []
 
     def action_assign(self, cr, uid, ids, *args):
@@ -1863,11 +1937,11 @@ class stock_move(osv.osv):
                     done.append(move.id)
                     pickings[move.picking_id.id] = 1
                     r = res.pop(0)
-                    cr.execute('update stock_move set location_id=%s, product_qty=%s where id=%s', (r[1], r[0], move.id))
+                    cr.execute('update stock_move set location_id=%s, product_qty=%s, product_uos_qty=%s where id=%s', (r[1], r[0], r[0] * move.product_id.uos_coeff, move.id))
 
                     while res:
                         r = res.pop(0)
-                        move_id = self.copy(cr, uid, move.id, {'product_qty': r[0], 'location_id': r[1]})
+                        move_id = self.copy(cr, uid, move.id, {'product_qty': r[0],'product_uos_qty': r[0] * move.product_id.uos_coeff,'location_id': r[1]})
                         done.append(move_id)
         if done:
             count += len(done)
@@ -1904,20 +1978,21 @@ class stock_move(osv.osv):
         if context is None:
             context = {}
         pickings = {}
+        picking_obj = self.pool.get('stock.picking')
         for move in self.browse(cr, uid, ids, context=context):
             if move.state in ('confirmed', 'waiting', 'assigned', 'draft'):
                 if move.picking_id:
                     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'})
+                self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'})
                 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})
         if not context.get('call_unlink',False):
-            for pick in self.pool.get('stock.picking').browse(cr, uid, pickings.keys()):
+            for pick in picking_obj.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'})
+                    picking_obj.write(cr, uid, [pick.id], {'state': 'cancel'})
 
         wf_service = netsvc.LocalService("workflow")
         for id in ids:
@@ -1934,16 +2009,24 @@ class stock_move(osv.osv):
         """
         product_obj=self.pool.get('product.product')
         accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context)
-        acc_src = accounts['stock_account_input']
-        acc_dest = accounts['stock_account_output']
+        if move.location_id.valuation_out_account_id:
+            acc_src = move.location_id.valuation_out_account_id.id
+        else:
+            acc_src = accounts['stock_account_input']
+
+        if move.location_dest_id.valuation_in_account_id:
+            acc_dest = move.location_dest_id.valuation_in_account_id.id
+        else:
+            acc_dest = accounts['stock_account_output']
+
         acc_variation = accounts.get('property_stock_variation', False)
         journal_id = accounts['stock_journal']
 
         if acc_dest == acc_variation:
-            raise osv.except_osv(_('Error!'),  _('Can not create Journal Entry, Output Account defined on this product and Variant account on category of this product is same.'))
+            raise osv.except_osv(_('Error!'),  _('Can not create Journal Entry, Output Account defined on this product and Variant account on category of this product are same.'))
 
         if acc_src == acc_variation:
-            raise osv.except_osv(_('Error!'),  _('Can not create Journal Entry, Input Account defined on this product and Variant account on category of this product is same.'))
+            raise osv.except_osv(_('Error!'),  _('Can not create Journal Entry, Input Account defined on this product and Variant account on category of this product are same.'))
 
         if not acc_src:
             raise osv.except_osv(_('Error!'),  _('There is no stock input account defined for this product or its category: "%s" (id: %d)') % \
@@ -1957,7 +2040,6 @@ class stock_move(osv.osv):
         if not acc_variation:
             raise osv.except_osv(_('Error!'), _('There is no inventory variation account defined on the product category: "%s" (id: %d)') % \
                                     (move.product_id.categ_id.name, move.product_id.categ_id.id,))
-
         return journal_id, acc_src, acc_dest, acc_variation
 
     def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
@@ -2050,6 +2132,7 @@ class stock_move(osv.osv):
                 todo.append(move.id)
         if todo:
             self.action_confirm(cr, uid, todo, context=context)
+            todo = []
 
         for move in self.browse(cr, uid, ids, context=context):
             if move.state in ['done','cancel']:
@@ -2062,7 +2145,9 @@ class stock_move(osv.osv):
                 self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
                 #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'):
-                    self.write(cr, uid, [move.move_dest_id.id], {'state': 'assigned'})
+                    if move.prodlot_id.id and move.product_id.id == move.move_dest_id.product_id.id:
+                        self.write(cr, uid, [move.move_dest_id.id], {'prodlot_id':move.prodlot_id.id})
+                    self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
                     if move.move_dest_id.picking_id:
                         wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
                     if move.move_dest_id.auto_validate:
@@ -2072,8 +2157,13 @@ class stock_move(osv.osv):
             prodlot_id = partial_datas and partial_datas.get('move%s_prodlot_id' % (move.id), False)
             if prodlot_id:
                 self.write(cr, uid, [move.id], {'prodlot_id': prodlot_id}, context=context)
+            if move.state not in ('confirmed','done','assigned'):
+                todo.append(move.id)
+
+        if todo:
+            self.action_confirm(cr, uid, todo, context=context)
 
-        self.write(cr, uid, move_ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
+        self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
         for id in move_ids:
              wf_service.trg_trigger(uid, 'stock.move', id, cr)
 
@@ -2164,6 +2254,7 @@ class stock_move(osv.osv):
         if quantity <= 0:
             raise osv.except_osv(_('Warning!'), _('Please provide a positive quantity to scrap!'))
         res = []
+        product_obj = self.pool.get('product.product')
         for move in self.browse(cr, uid, ids, context=context):
             move_qty = move.product_qty
             uos_qty = quantity / move_qty * move.product_uos_qty
@@ -2176,10 +2267,12 @@ class stock_move(osv.osv):
                 'tracking_id': move.tracking_id.id,
                 'prodlot_id': move.prodlot_id.id,
             }
+            if move.location_id.usage <> 'internal':
+                default_val.update({'location_id': move.location_dest_id.id})
             new_move = self.copy(cr, uid, move.id, default_val)
 
             res += [new_move]
-            product_obj = self.pool.get('product.product')
+
             for (id, name) in product_obj.name_get(cr, uid, [move.product_id.id]):
                 self.log(cr, uid, move.id, "%s x %s %s" % (move.product_qty, name, _("were scrapped")))
 
@@ -2263,8 +2356,11 @@ class stock_move(osv.osv):
         if quantity <= 0:
             raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
         res = []
+        product_obj = self.pool.get('product.product')
         for move in self.browse(cr, uid, ids, context=context):
             move_qty = move.product_qty
+            if move_qty <= 0:
+                raise osv.except_osv(_('Error!'), _('Can not consume a move with negative or zero quantity !'))
             quantity_rest = move.product_qty
             quantity_rest -= quantity
             uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty
@@ -2307,7 +2403,6 @@ class stock_move(osv.osv):
                     }
                     self.write(cr, uid, [move.id], update_val)
 
-            product_obj = self.pool.get('product.product')
             for new_move in self.browse(cr, uid, res, context=context):
                 for (id, name) in product_obj.name_get(cr, uid, [new_move.product_id.id]):
                     message = _('Product ') + " '" + name + "' "+ _("is consumed with") + " '" + str(new_move.product_qty) + "' "+ _("quantity.")
@@ -2398,7 +2493,7 @@ class stock_move(osv.osv):
                     defaults.update(prodlot_id=prodlot_id)
                 new_move = self.copy(cr, uid, move.id, defaults)
                 complete.append(self.browse(cr, uid, new_move))
-            self.write(cr, uid, move.id,
+            self.write(cr, uid, [move.id],
                     {
                         'product_qty' : move.product_qty - product_qty,
                         'product_uos_qty':move.product_qty - product_qty,
@@ -2406,7 +2501,7 @@ class stock_move(osv.osv):
 
 
         for move in too_many:
-            self.write(cr, uid, move.id,
+            self.write(cr, uid, [move.id],
                     {
                         'product_qty': move.product_qty,
                         'product_uos_qty': move.product_qty,
@@ -2447,7 +2542,7 @@ class stock_inventory(osv.osv):
 
     }
     _defaults = {
-        'date': time.strftime('%Y-%m-%d %H:%M:%S'),
+        'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
         'state': 'draft',
         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c)
     }
@@ -2461,10 +2556,9 @@ class stock_inventory(osv.osv):
         return self.pool.get('stock.move').create(cr, uid, move_vals)
 
     def action_done(self, cr, uid, ids, context=None):
-        """ Finished the inventory
+        """ Finish the inventory
         @return: True
         """
-
         if context is None:
             context = {}
         move_obj = self.pool.get('stock.move')
@@ -2488,8 +2582,9 @@ class stock_inventory(osv.osv):
             move_ids = []
             for line in inv.inventory_line_id:
                 pid = line.product_id.id
-                product_context.update(uom=line.product_uom.id)
+                product_context.update(uom=line.product_uom.id, date=inv.date, prodlot_id=line.prod_lot_id.id)
                 amount = location_obj._product_get(cr, uid, line.location_id.id, [pid], product_context)[pid]
+
                 change = line.product_qty - amount
                 lot_id = line.prod_lot_id.id
                 if change:
@@ -2500,7 +2595,6 @@ class stock_inventory(osv.osv):
                         'product_uom': line.product_uom.id,
                         'prodlot_id': lot_id,
                         'date': inv.date,
-                        'date': inv.date,
                     }
                     if change > 0:
                         value.update( {
@@ -2514,33 +2608,39 @@ class stock_inventory(osv.osv):
                             'location_id': line.location_id.id,
                             'location_dest_id': location_id,
                         })
-                    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))
             message = _('Inventory') + " '" + inv.name + "' "+ _("is done.")
             self.log(cr, uid, inv.id, message)
             self.write(cr, uid, [inv.id], {'state': 'confirm', 'move_ids': [(6, 0, move_ids)]})
         return True
 
-    def action_cancel(self, cr, uid, ids, context=None):
+    def action_cancel_draft(self, cr, uid, ids, context=None):
         """ Cancels the stock move and change inventory state to draft.
         @return: True
         """
         for inv in self.browse(cr, uid, ids, context=context):
-            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': 'draft'})
+            self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
+            self.write(cr, uid, [inv.id], {'state':'draft'}, context=context)
         return True
 
     def action_cancel_inventary(self, cr, uid, ids, context=None):
         """ Cancels both stock move and inventory
         @return: True
         """
+        move_obj = self.pool.get('stock.move')
+        account_move_obj = self.pool.get('account.move')
         for inv in self.browse(cr, uid, ids, context=context):
-            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'})
+            move_obj.action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
+            for move in inv.move_ids:
+                 account_move_ids = account_move_obj.search(cr, uid, [('name', '=', move.name)])
+                 if account_move_ids:
+                     account_move_data_l = account_move_obj.read(cr, uid, account_move_ids, ['state'], context=context)
+                     for account_move in account_move_data_l:
+                         if account_move['state'] == 'posted':
+                             raise osv.except_osv(_('UserError'),
+                                                  _('You can not cancel inventory which has any account move with posted state.'))
+                         account_move_obj.unlink(cr, uid, [account_move['id']], context=context)
+            self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
         return True
 
 stock_inventory()
@@ -2554,12 +2654,12 @@ class stock_inventory_line(osv.osv):
         'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
         'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
         'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM')),
-        'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True),
+        'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True, readonly=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):
+    def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False, to_date=False):
         """ Changes UoM and name if product_id changes.
         @param location_id: Location id
         @param product: Changed product_id
@@ -2567,11 +2667,10 @@ class stock_inventory_line(osv.osv):
         @return:  Dictionary of changed values
         """
         if not product:
-            return {}
-        if not uom:
-            prod = self.pool.get('product.product').browse(cr, uid, [product], {'uom': uom})[0]
-            uom = prod.uom_id.id
-        amount = self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], {'uom': uom})[product]
+            return {'value': {'product_qty': 0.0, 'product_uom': False}}
+        obj_product = self.pool.get('product.product').browse(cr, uid, product)
+        uom = uom or obj_product.uom_id.id
+        amount = self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], {'uom': uom, 'to_date': to_date})[product]
         result = {'product_qty': amount, 'product_uom': uom}
         return {'value': result}