here we design a .py code to implement custom picking
the user can automattically select stock location and shelf and row the product stocked its not completed it is use-ful to you
import logging
from datetime import timedelta
from functools import partial
import psycopg2
from collections import namedtuple
import json
import time
import ast
from odoo.tools.float_utils import float_compare
from odoo import api, fields, models, tools, _
from odoo.tools import float_is_zero
from odoo.exceptions import UserError
from odoo.http import request
import odoo.addons.decimal_precision as dp
from collections import OrderedDict
_logger = logging.getLogger(__name__)
class PosOrderLine(models.Model):
_inherit = "pos.order.line"
dict_value= fields.Char()
class PosOrder(models.Model):
_inherit = "pos.order"
def create_picking(self):
"""Create a picking for each order and validate it."""
Picking = self.env['stock.picking']
Move = self.env['stock.move']
StockWarehouse = self.env['stock.warehouse']
for order in self:
print "order %s" % order.picking_type_id.id
address = order.partner_id.address_get(['delivery']) or {}
picking_type = order.picking_type_id
print "picking_type %s" % picking_type
return_pick_type = order.picking_type_id.return_picking_type_id or order.picking_type_id
order_picking = Picking
return_picking = Picking
moves = Move
location_id = order.location_id.id
print "====================================%s" % order.location_id.name
if order.partner_id:
destination_id = order.partner_id.property_stock_customer.id
else:
if (not picking_type) or (not picking_type.default_location_dest_id):
customerloc, supplierloc = StockWarehouse._get_partner_locations()
destination_id = customerloc.id
else:
destination_id = picking_type.default_location_dest_id.id
if picking_type:
print "picking_type"
message = _(
"This transfer has been created from the point of sale session: <a href=# data-oe-model=pos.order data-oe-id=%d>%s</a>") % (
order.id, order.name)
picking_vals = {
'origin': order.name,
'partner_id': address.get('delivery', False),
'date_done': order.date_order,
'picking_type_id': picking_type.id,
'company_id': order.company_id.id,
'move_type': 'direct',
'note': order.note or "",
'location_id': location_id,
'location_dest_id': destination_id,
}
print(picking_vals)
pos_qty = any([x.qty >= 0 for x in order.lines])
print "pos_qty %s" % pos_qty
if pos_qty:
print "entered into pos_qty"
order_picking = Picking.create(picking_vals)
order_picking.message_post(body=message)
neg_qty = any([x.qty < 0 for x in order.lines])
if neg_qty:
return_vals = picking_vals.copy()
return_vals.update({
'location_id': destination_id,
'location_dest_id': return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
'picking_type_id': return_pick_type.id
})
return_picking = Picking.create(return_vals)
return_picking.message_post(body=message)
for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu']):
print "--------------- dic_value-------------%s" %line.dict_value
if line.x_location:
if line.dict_value:
locations = ast.literal_eval(line.dict_value)
line_qty = line.qty
loc_id = self.env['stock.location'].search([('name', '=', line.x_location)])
print "loc id == %s" % loc_id
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': abs(float(locations[line.x_location])),
'state': 'draft',
'location_id': loc_id.id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
line_qty -= float(locations[line.x_location])
if line_qty > 0:
for loc, qty in locations.iteritems():
print "==============LINE QTY================%s"%line_qty
print "==============QTY=====================%s"%qty
if loc != line.x_location and float(qty)>0 and line_qty > 0:
if float(qty) >= line_qty:
# line_qty = float(qty) - line_qty
print "qty diff =%s"%line_qty
loc_id = self.env['stock.location'].search([('name', '=', loc)])
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': line_qty,
'state': 'draft',
'location_id': loc_id.id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
print "executed move create"
break
else:
print "ENETERD ELSE"
line_qty = abs(float(qty)-line_qty)
loc_id = self.env['stock.location'].search([('name', '=', loc)])
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': float(qty),
'state': 'draft',
'location_id': loc_id.id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
else:
loc_id = self.env['stock.location'].search([('name', '=', line.x_location)])
print "loc id == %s" % loc_id
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': abs(line.qty),
'state': 'draft',
'location_id': loc_id.id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
else:
moves |= Move.create({
'name': line.name,
'product_uom': line.product_id.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': abs(line.qty),
'state': 'draft',
'location_id': location_id if line.qty >= 0 else destination_id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
print "=========================== order picking is =%s" % order_picking.id
# prefer associating the regular order picking, not the return
order.write({'picking_id': order_picking.id or return_picking.id})
if return_picking:
order._force_picking_done(return_picking)
if order_picking:
print("1")
order_picking.action_confirm()
print("2")
order_picking.force_assign()
# order_picking.do_new_transfer()
print("before")
order.set_pack_operation_lot()
print("after")
order_picking.action_done()
# when the pos.config has no picking_type_id set only the moves will be created
if moves and not return_picking and not order_picking:
moves.action_confirm()
moves.force_assign()
moves.filtered(lambda m: m.product_id.tracking == 'none').action_done()
return True
class StockPick(models.Model):
_inherit = 'stock.picking'
def _prepare_pack_ops(self, quants, forced_qties):
""" Prepare pack_operations, returns a list of dict to give at create """
# TDE CLEANME: oh dear ...
valid_quants = quants.filtered(lambda quant: quant.qty > 0)
_Mapping = namedtuple('Mapping', ('product', 'package', 'owner', 'location', 'location_dst_id'))
all_products = valid_quants.mapped('product_id') | self.env['product.product'].browse(
p.id for p in forced_qties.keys()) | self.move_lines.mapped('product_id')
computed_putaway_locations = dict(
(product, self.location_dest_id.get_putaway_strategy(product) or self.location_dest_id.id) for product in
all_products)
product_to_uom = dict((product.id, product.uom_id) for product in all_products)
picking_moves = self.move_lines.filtered(lambda move: move.state not in ('done', 'cancel'))
for move in picking_moves:
# If we encounter an UoM that is smaller than the default UoM or the one already chosen, use the new one instead.
if move.product_uom != product_to_uom[move.product_id.id] and move.product_uom.factor > product_to_uom[
move.product_id.id].factor:
product_to_uom[move.product_id.id] = move.product_uom
# if len(picking_moves.mapped('location_id')) > 1:
# raise UserError(_('The source location must be the same for all the moves of the picking.'))
# if len(picking_moves.mapped('location_dest_id')) > 1:
# raise UserError(_('The destination location must be the same for all the moves of the picking.'))
pack_operation_values = []
# find the packages we can move as a whole, create pack operations and mark related quants as done
top_lvl_packages = valid_quants._get_top_level_packages(computed_putaway_locations)
for pack in top_lvl_packages:
pack_quants = pack.get_content()
pack_operation_values.append({
'picking_id': self.id,
'package_id': pack.id,
'product_qty': 1.0,
'location_id': pack.location_id.id,
'location_dest_id': computed_putaway_locations[pack_quants[0].product_id],
'owner_id': pack.owner_id.id,
})
valid_quants -= pack_quants
# Go through all remaining reserved quants and group by product, package, owner, source location and dest location
# Lots will go into pack operation lot object
qtys_grouped = {}
lots_grouped = {}
for quant in valid_quants:
key = _Mapping(quant.product_id, quant.package_id, quant.owner_id, quant.location_id,
computed_putaway_locations[quant.product_id])
qtys_grouped.setdefault(key, 0.0)
qtys_grouped[key] += quant.qty
if quant.product_id.tracking != 'none' and quant.lot_id:
lots_grouped.setdefault(key, dict()).setdefault(quant.lot_id.id, 0.0)
lots_grouped[key][quant.lot_id.id] += quant.qty
# Do the same for the forced quantities (in cases of force_assign or incomming shipment for example)
for product, qty in forced_qties.items():
if qty <= 0.0:
continue
key = _Mapping(product, self.env['stock.quant.package'], self.owner_id, self.location_id,
computed_putaway_locations[product])
qtys_grouped.setdefault(key, 0.0)
qtys_grouped[key] += qty
# Create the necessary operations for the grouped quants and remaining qtys
Uom = self.env['product.uom']
product_id_to_vals = {} # use it to create operations using the same order as the picking stock moves
for mapping, qty in qtys_grouped.items():
uom = product_to_uom[mapping.product.id]
val_dict = {
'picking_id': self.id,
'product_qty': mapping.product.uom_id._compute_quantity(qty, uom),
'product_id': mapping.product.id,
'package_id': mapping.package.id,
'owner_id': mapping.owner.id,
'location_id': mapping.location.id,
'location_dest_id': mapping.location_dst_id,
'product_uom_id': uom.id,
'pack_lot_ids': [
(0, 0, {'lot_id': lot, 'qty': 0.0, 'qty_todo': lots_grouped[mapping][lot]})
for lot in lots_grouped.get(mapping, {}).keys()],
}
product_id_to_vals.setdefault(mapping.product.id, list()).append(val_dict)
for move in self.move_lines.filtered(lambda move: move.state not in ('done', 'cancel')):
print(move.product_uom_qty)
values = product_id_to_vals.pop(move.product_id.id, [])
values[0]['location_id'] = move.location_id.id
pack_operation_values += values
print("pac ops vals %s"%pack_operation_values)
return pack_operation_values
@api.multi
def do_prepare_partial(self):
# TDE CLEANME: oh dear ...
PackOperation = self.env['stock.pack.operation']
# get list of existing operations and delete them
existing_packages = PackOperation.search([('picking_id', 'in', self.ids)]) # TDE FIXME: o2m / m2o ?
if existing_packages:
existing_packages.unlink()
for picking in self:
forced_qties = {} # Quantity remaining after calculating reserved quants
picking_quants = self.env['stock.quant']
# Calculate packages, reserved quants, qtys of this picking's moves
for move in picking.move_lines:
value = {}
if move.state not in ('assigned', 'confirmed', 'waiting'):
continue
move_quants = move.reserved_quant_ids
picking_quants += move_quants
forced_qty = (move.state == 'assigned') and move.product_qty - sum([x.qty for x in move_quants]) or 0
# if we used force_assign() on the move, or if the move is incoming, forced_qty > 0
if float_compare(forced_qty, 0, precision_rounding=move.product_id.uom_id.rounding) > 0:
if forced_qties.get(move.product_id):
value['fresh_record'] = False
value['location_dest_id']=move.location_dest_id.id
value['product_id'] = move.product_id.id
value['product_qty'] = forced_qty
value['product_uom_id'] = move.product_uom.id
value['location_id'] = move.location_id.id
value['picking_id'] = move.picking_id.id
PackOperation.create(value)
# print("inside11")
# forced_qties[move.product_id] += forced_qty
else:
# forced_qties[move.product_id] = forced_qty
value['fresh_record'] = False
value['location_dest_id']=move.location_dest_id.id
value['product_id'] = move.product_id.id
value['product_qty'] = forced_qty
value['product_uom_id'] = move.product_uom.id
value['location_id'] = move.location_id.id
value['picking_id'] = move.picking_id.id
PackOperation.create(value)
# for vals in picking._prepare_pack_ops(picking_quants, forced_qties):
# vals['fresh_record'] = False
# PackOperation.create(vals)
# recompute the remaining quantities all at once
self.do_recompute_remaining_quantities()
self.write({'recompute_pack_op': False})
As there are two locations you mentioned, practically / in reality, what exactly are you doing when you suppose to transfer goods from one location / warehouse to another location / warehouse for retail store? If you can describe that, I can suggest you how we can achieve this in Odoo.