Files
odoo_source/addons/sale_stock/models/stock.py
T
svs-odoo c71b4e13a9 [FIX] sale_stock: fix exceeding SO line
Fix a bug introduced by #62032 and which causing the creation of
unwanted additional SO line with multistep delivery.

closes odoo/odoo#63057

X-original-commit: 076966fda2edcf1e5225c09b03c6700aa1b1f6e9
Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com>
Signed-off-by: Steve Van Essche <svs-odoo@users.noreply.github.com>
2020-12-08 17:58:34 +00:00

174 lines
7.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
class StockLocationRoute(models.Model):
_inherit = "stock.location.route"
sale_selectable = fields.Boolean("Selectable on Sales Order Line")
class StockMove(models.Model):
_inherit = "stock.move"
sale_line_id = fields.Many2one('sale.order.line', 'Sale Line', index=True)
@api.model
def _prepare_merge_moves_distinct_fields(self):
distinct_fields = super(StockMove, self)._prepare_merge_moves_distinct_fields()
distinct_fields.append('sale_line_id')
return distinct_fields
@api.model
def _prepare_merge_move_sort_method(self, move):
move.ensure_one()
keys_sorted = super(StockMove, self)._prepare_merge_move_sort_method(move)
keys_sorted.append(move.sale_line_id.id)
return keys_sorted
def _get_related_invoices(self):
""" Overridden from stock_account to return the customer invoices
related to this stock move.
"""
rslt = super(StockMove, self)._get_related_invoices()
invoices = self.mapped('picking_id.sale_id.invoice_ids').filtered(lambda x: x.state == 'posted')
rslt += invoices
#rslt += invoices.mapped('reverse_entry_ids')
return rslt
def _get_source_document(self):
res = super()._get_source_document()
return self.sale_line_id.order_id or res
def _assign_picking_post_process(self, new=False):
super(StockMove, self)._assign_picking_post_process(new=new)
if new:
picking_id = self.mapped('picking_id')
sale_order_ids = self.mapped('sale_line_id.order_id')
for sale_order_id in sale_order_ids:
picking_id.message_post_with_view(
'mail.message_origin_link',
values={'self': picking_id, 'origin': sale_order_id},
subtype_id=self.env.ref('mail.mt_note').id)
class ProcurementGroup(models.Model):
_inherit = 'procurement.group'
sale_id = fields.Many2one('sale.order', 'Sale Order')
class StockRule(models.Model):
_inherit = 'stock.rule'
def _get_custom_move_fields(self):
fields = super(StockRule, self)._get_custom_move_fields()
fields += ['sale_line_id', 'partner_id']
return fields
class StockPicking(models.Model):
_inherit = 'stock.picking'
sale_id = fields.Many2one(related="group_id.sale_id", string="Sales Order", store=True, readonly=False)
def _action_done(self):
res = super()._action_done()
sale_order_lines_vals = []
for move in self.move_lines:
sale_order = move.picking_id.sale_id
# Creates new SO line only when pickings linked to a sale order and
# for moves with qty. done and not already linked to a SO line.
if not sale_order or move.location_dest_id.usage != 'customer' or move.sale_line_id or not move.quantity_done:
continue
product = move.product_id
so_line_vals = {
'move_ids': [(4, move.id, 0)],
'name': product.display_name,
'order_id': sale_order.id,
'product_id': product.id,
'product_uom_qty': 0,
'qty_delivered': move.quantity_done,
}
if product.invoice_policy == 'delivery':
# Check if there is already a SO line for this product to get
# back its unit price (in case it was manually updated).
so_line = sale_order.order_line.filtered(lambda sol: sol.product_id == product)
if so_line:
so_line_vals['price_unit'] = so_line[0].price_unit
elif product.invoice_policy == 'order':
# No unit price if the product is invoiced on the ordered qty.
so_line_vals['price_unit'] = 0
sale_order_lines_vals.append(so_line_vals)
if sale_order_lines_vals:
self.env['sale.order.line'].create(sale_order_lines_vals)
return res
def _log_less_quantities_than_expected(self, moves):
""" Log an activity on sale order that are linked to moves. The
note summarize the real proccessed quantity and promote a
manual action.
:param dict moves: a dict with a move as key and tuple with
new and old quantity as value. eg: {move_1 : (4, 5)}
"""
def _keys_in_sorted(sale_line):
""" sort by order_id and the sale_person on the order """
return (sale_line.order_id.id, sale_line.order_id.user_id.id)
def _keys_in_groupby(sale_line):
""" group by order_id and the sale_person on the order """
return (sale_line.order_id, sale_line.order_id.user_id)
def _render_note_exception_quantity(moves_information):
""" Generate a note with the picking on which the action
occurred and a summary on impacted quantity that are
related to the sale order where the note will be logged.
:param moves_information dict:
{'move_id': ['sale_order_line_id', (new_qty, old_qty)], ..}
:return: an html string with all the information encoded.
:rtype: str
"""
origin_moves = self.env['stock.move'].browse([move.id for move_orig in moves_information.values() for move in move_orig[0]])
origin_picking = origin_moves.mapped('picking_id')
values = {
'origin_moves': origin_moves,
'origin_picking': origin_picking,
'moves_information': moves_information.values(),
}
return self.env.ref('sale_stock.exception_on_picking')._render(values=values)
documents = self._log_activity_get_documents(moves, 'sale_line_id', 'DOWN', _keys_in_sorted, _keys_in_groupby)
self._log_activity(_render_note_exception_quantity, documents)
return super(StockPicking, self)._log_less_quantities_than_expected(moves)
class ProductionLot(models.Model):
_inherit = 'stock.production.lot'
sale_order_ids = fields.Many2many('sale.order', string="Sales Orders", compute='_compute_sale_order_ids')
sale_order_count = fields.Integer('Sale order count', compute='_compute_sale_order_ids')
@api.depends('name')
def _compute_sale_order_ids(self):
for lot in self:
stock_moves = self.env['stock.move.line'].search([
('lot_id', '=', lot.id),
('state', '=', 'done')
]).mapped('move_id')
stock_moves = stock_moves.search([('id', 'in', stock_moves.ids)]).filtered(
lambda move: move.picking_id.location_dest_id.usage == 'customer' and move.state == 'done')
lot.sale_order_ids = stock_moves.mapped('sale_line_id.order_id')
lot.sale_order_count = len(lot.sale_order_ids)
def action_view_so(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders")
action['domain'] = [('id', 'in', self.mapped('sale_order_ids.id'))]
action['context'] = dict(self._context, create=False)
return action