For now, when you are selling products in the point of sales, the stock is affected once the order is validated or the session closed. But in some case, you don't have the product in your store, and you have to plan a delivery for the order. To be able to plan a delivery, you can now choose the warehouse in POS Config and the routes of your warehouse will be applied. You can also set a specific rule on your POS Config if you've enabled the multi-step-routes in the inventory module closes odoo/odoo#58506 Task-id: 2082104 Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
176 lines
8.4 KiB
Python
176 lines
8.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api,fields, models
|
|
from odoo.exceptions import UserError, ValidationError
|
|
from odoo.tools import float_is_zero, float_compare
|
|
|
|
from itertools import groupby
|
|
|
|
class StockPicking(models.Model):
|
|
_inherit='stock.picking'
|
|
|
|
pos_session_id = fields.Many2one('pos.session')
|
|
pos_order_id = fields.Many2one('pos.order')
|
|
|
|
def _prepare_picking_vals(self, partner, picking_type, location_id, location_dest_id):
|
|
return {
|
|
'partner_id': partner.id if partner else False,
|
|
'user_id': False,
|
|
'picking_type_id': picking_type.id,
|
|
'move_type': 'direct',
|
|
'location_id': location_id,
|
|
'location_dest_id': location_dest_id,
|
|
}
|
|
|
|
|
|
@api.model
|
|
def _create_picking_from_pos_order_lines(self, location_dest_id, lines, picking_type, partner=False):
|
|
"""We'll create some picking based on order_lines"""
|
|
|
|
pickings = self.env['stock.picking']
|
|
stockable_lines = lines.filtered(lambda l: l.product_id.type in ['product', 'consu'] and not float_is_zero(l.qty, precision_rounding=l.product_id.uom_id.rounding))
|
|
if not stockable_lines:
|
|
return pickings
|
|
positive_lines = stockable_lines.filtered(lambda l: l.qty > 0)
|
|
negative_lines = stockable_lines - positive_lines
|
|
|
|
if positive_lines:
|
|
location_id = picking_type.default_location_src_id.id
|
|
positive_picking = self.env['stock.picking'].create(
|
|
self._prepare_picking_vals(partner, picking_type, location_id, location_dest_id)
|
|
)
|
|
|
|
positive_picking._create_move_from_pos_order_lines(positive_lines)
|
|
try:
|
|
with self.env.cr.savepoint():
|
|
positive_picking._action_done()
|
|
except (UserError, ValidationError):
|
|
pass
|
|
|
|
pickings |= positive_picking
|
|
if negative_lines:
|
|
if picking_type.return_picking_type_id:
|
|
return_picking_type = picking_type.return_picking_type_id
|
|
return_location_id = return_picking_type.default_location_dest_id.id
|
|
else:
|
|
return_picking_type = picking_type
|
|
return_location_id = picking_type.default_location_src_id.id
|
|
|
|
negative_picking = self.env['stock.picking'].create(
|
|
self._prepare_picking_vals(partner, return_picking_type, location_dest_id, return_location_id)
|
|
)
|
|
negative_picking._create_move_from_pos_order_lines(negative_lines)
|
|
try:
|
|
with self.env.cr.savepoint():
|
|
negative_picking._action_done()
|
|
except (UserError, ValidationError):
|
|
pass
|
|
pickings |= negative_picking
|
|
return pickings
|
|
|
|
def _prepare_stock_move_vals(self, first_line, order_lines):
|
|
return {
|
|
'name': first_line.name,
|
|
'product_uom': first_line.product_id.uom_id.id,
|
|
'picking_id': self.id,
|
|
'picking_type_id': self.picking_type_id.id,
|
|
'product_id': first_line.product_id.id,
|
|
'product_uom_qty': abs(sum(order_lines.mapped('qty'))),
|
|
'state': 'draft',
|
|
'location_id': self.location_id.id,
|
|
'location_dest_id': self.location_dest_id.id,
|
|
'company_id': self.company_id.id,
|
|
}
|
|
|
|
def _create_move_from_pos_order_lines(self, lines):
|
|
self.ensure_one()
|
|
lines_by_product = groupby(sorted(lines, key=lambda l: l.product_id.id), key=lambda l: l.product_id.id)
|
|
for product, lines in lines_by_product:
|
|
order_lines = self.env['pos.order.line'].concat(*lines)
|
|
first_line = order_lines[0]
|
|
current_move = self.env['stock.move'].create(
|
|
self._prepare_stock_move_vals(first_line, order_lines)
|
|
)
|
|
confirmed_moves = current_move._action_confirm()
|
|
for move in confirmed_moves:
|
|
if first_line.product_id == move.product_id and first_line.product_id.tracking != 'none':
|
|
if self.picking_type_id.use_existing_lots or self.picking_type_id.use_create_lots:
|
|
for line in order_lines:
|
|
sum_of_lots = 0
|
|
for lot in line.pack_lot_ids.filtered(lambda l: l.lot_name):
|
|
if line.product_id.tracking == 'serial':
|
|
qty = 1
|
|
else:
|
|
qty = abs(line.qty)
|
|
ml_vals = move._prepare_move_line_vals()
|
|
ml_vals.update({'qty_done':qty})
|
|
if self.picking_type_id.use_existing_lots:
|
|
existing_lot = self.env['stock.production.lot'].search([
|
|
('company_id', '=', self.company_id.id),
|
|
('product_id', '=', line.product_id.id),
|
|
('name', '=', lot.lot_name)
|
|
])
|
|
if not existing_lot and self.picking_type_id.use_create_lots:
|
|
existing_lot = self.env['stock.production.lot'].create({
|
|
'company_id': self.company_id.id,
|
|
'product_id': line.product_id.id,
|
|
'name': lot.lot_name,
|
|
})
|
|
ml_vals.update({
|
|
'lot_id': existing_lot.id,
|
|
})
|
|
else:
|
|
ml_vals.update({
|
|
'lot_name': lot.lot_name,
|
|
})
|
|
self.env['stock.move.line'].create(ml_vals)
|
|
sum_of_lots += qty
|
|
if abs(line.qty) != sum_of_lots:
|
|
difference_qty = abs(line.qty) - sum_of_lots
|
|
ml_vals = current_move._prepare_move_line_vals()
|
|
if line.product_id.tracking == 'serial':
|
|
ml_vals.update({'qty_done': 1})
|
|
for i in range(int(difference_qty)):
|
|
self.env['stock.move.line'].create(ml_vals)
|
|
else:
|
|
ml_vals.update({'qty_done': difference_qty})
|
|
self.env['stock.move.line'].create(ml_vals)
|
|
else:
|
|
move._action_assign()
|
|
sum_of_lots = 0
|
|
for move_line in move.move_line_ids:
|
|
move_line.qty_done = move_line.product_uom_qty
|
|
sum_of_lots += move_line.product_uom_qty
|
|
if float_compare(move.product_uom_qty, move.quantity_done, precision_rounding=move.product_uom.rounding) > 0:
|
|
remaining_qty = move.product_uom_qty - move.quantity_done
|
|
ml_vals = move._prepare_move_line_vals()
|
|
ml_vals.update({'qty_done':remaining_qty})
|
|
self.env['stock.move.line'].create(ml_vals)
|
|
|
|
else:
|
|
move.quantity_done = move.product_uom_qty
|
|
|
|
def _send_confirmation_email(self):
|
|
# Avoid sending Mail/SMS for POS deliveries
|
|
pickings = self.filtered(lambda p: p.picking_type_id != p.picking_type_id.warehouse_id.pos_type_id)
|
|
return super(StockPicking, pickings)._send_confirmation_email()
|
|
|
|
class ProcurementGroup(models.Model):
|
|
_inherit = 'procurement.group'
|
|
|
|
pos_order_id = fields.Many2one('pos.order', 'POS Order')
|
|
|
|
class StockMove(models.Model):
|
|
_inherit = 'stock.move'
|
|
|
|
def _get_new_picking_values(self):
|
|
vals = super(StockMove, self)._get_new_picking_values()
|
|
vals['pos_session_id'] = self.mapped('group_id.pos_order_id.session_id').id
|
|
vals['pos_order_id'] = self.mapped('group_id.pos_order_id').id
|
|
return vals
|
|
|
|
def _key_assign_picking(self):
|
|
keys = super(StockMove, self)._key_assign_picking()
|
|
return keys + (self.group_id.pos_order_id,)
|