Skip to Content
Menu
This question has been flagged
3 Replies
4616 Views

Setup:

  • 2 locations:

    • Retail - Front of house, contains some products

    • Warehouse - contains larger products

  • Odoo CE 10

  • PoS in retail location

Issue:

    When creating a sale from PoS, some items need to be moved from the warehouse to the retail location for the     client. I cannot find a way of automatically generating a (picking?) for this.

    Alternatively:

    Some way of creating a form/report/(picking?) to notify the warehouse of what should be loaded for the client

The issue we are currently facing is that we sell things from the PoS(in retail location), however the stock actually comes from the warehouse. This causes the retail location to have negative stock.

If anyone has any suggestions as to how I can resolve this issue, please let me know.    

Avatar
Discard

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.

Best Answer

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})


Avatar
Discard
Best Answer

I have the usual problem do you have any help? 

Sorry my English!

Avatar
Discard
Related Posts Replies Views Activity
2
Feb 18
3350
1
Jul 22
1636
0
May 20
2048
2
Jul 19
5080
0
Nov 18
1959