Files
manufacture/mrp_subcontracting/models/stock_picking.py
Pedro M. Baeza 4875fd46eb [FIX] mrp_subcontracting: Don't perform subcontracting on 0 quantity
If there are several lines in the picking, and some of them are not
received, you've got an error due to the unconditional processing of
all lines.

Now we prevent those that are not received in the loop.

Closes #640
2021-05-20 18:31:31 +02:00

183 lines
8.1 KiB
Python

# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
# Copyright 2019 Odoo
# Copyright 2020 Tecnativa - Alexandre Díaz
# Copyright 2020 Tecnativa - Pedro M. Baeza
from datetime import timedelta
from odoo import api, fields, models
from odoo.tools import float_is_zero
class StockPicking(models.Model):
_inherit = 'stock.picking'
display_action_record_components = fields.Boolean(
compute='_compute_display_action_record_components')
@api.depends('state')
def _compute_display_action_record_components(self):
for picking in self:
# Hide if not encoding state
if picking.state in ('draft', 'cancel', 'done'):
picking.display_action_record_components = False
continue
if not picking._is_subcontract():
picking.display_action_record_components = False
continue
# Hide if no components are track
subcontracted_productions = picking\
._get_subcontracted_productions()
subcontracted_moves = subcontracted_productions.mapped(
'move_raw_ids')
if all(subcontracted_move.has_tracking == 'none'
for subcontracted_move in subcontracted_moves):
picking.display_action_record_components = False
continue
# Hide if the production is to close
if not subcontracted_productions.filtered(lambda mo: (
not mo.check_to_done and mo.state != 'done'
)):
picking.display_action_record_components = False
continue
picking.display_action_record_components = True
# -------------------------------------------------------------------------
# Action methods
# -------------------------------------------------------------------------
def action_done(self):
productions = self.env['mrp.production']
for picking in self:
for move in picking.move_lines:
production = move.move_orig_ids.mapped('production_id')
if not move.is_subcontract or production.state in ('done', 'cancel'):
continue
if move._has_tracked_subcontract_components():
move.move_orig_ids.filtered(
lambda m: m.state not in ('done', 'cancel')
).move_line_ids.unlink()
move_finished_ids = move.move_orig_ids.filtered(
lambda m: m.state not in ('done', 'cancel'))
for ml in move.move_line_ids:
if float_is_zero(
ml.qty_done,
precision_rounding=ml.product_uom_id.rounding
):
continue
ml.copy({
'picking_id': False,
'production_id':
move_finished_ids.production_id.id,
'move_id': move_finished_ids.id,
'qty_done': ml.qty_done,
'result_package_id': False,
'location_id': move_finished_ids.location_id.id,
'location_dest_id':
move_finished_ids.location_dest_id.id,
})
else:
for move_line in move.move_line_ids:
if float_is_zero(
move_line.qty_done,
precision_rounding=move_line.product_uom_id.rounding
):
continue
produce = self.env['mrp.product.produce'].with_context(
default_production_id=production.id,
active_id=production.id).create({
'production_id': production.id,
'product_id': production.product_id.id,
'product_qty': move_line.qty_done,
'product_uom_id': move_line.product_uom_id.id,
'lot_id': move_line.lot_id.id,
})
produce._onchange_product_qty()
produce.do_produce()
productions |= production
for subcontracted_production in productions:
if subcontracted_production.check_to_done:
subcontracted_production.button_mark_done()
else:
subcontracted_production.post_inventory()
res = super(StockPicking, self).action_done()
for subcontracted_production in productions:
# For consistency, set the date on production move before the
# date on picking. (Traceability report + Product Moves menu
# item)
minimum_date = min(picking.move_line_ids.mapped('date'))
production_moves = subcontracted_production.move_raw_ids\
| subcontracted_production.move_finished_ids
production_moves.write({
'date': minimum_date - timedelta(seconds=1),
})
production_moves.mapped('move_line_ids').write({
'date': minimum_date - timedelta(seconds=1),
})
return res
def action_record_components(self):
self.ensure_one()
for move in self.move_lines:
if not move._has_tracked_subcontract_components():
continue
production = move.move_orig_ids.mapped("production_id")
if not production or production.state in ('done', 'to_close'):
continue
return move._action_record_components()
# -------------------------------------------------------------------------
# Subcontract helpers
# -------------------------------------------------------------------------
def _is_subcontract(self):
self.ensure_one()
return self.picking_type_id.code == 'incoming' and any(
m.is_subcontract for m in self.move_lines)
def _get_subcontracted_productions(self):
self.ensure_one()
return self.move_lines.mapped('move_orig_ids.production_id')
def _get_warehouse(self, subcontract_move):
return subcontract_move.warehouse_id\
or self.picking_type_id.warehouse_id
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
subcontract_move.ensure_one()
group = self.env['procurement.group'].create({
'name': self.name,
'partner_id': self.partner_id.id,
})
product = subcontract_move.product_id
warehouse = self._get_warehouse(subcontract_move)
vals = {
'company_id': subcontract_move.company_id.id,
'procurement_group_id': group.id,
'product_id': product.id,
'product_uom_id': subcontract_move.product_uom.id,
'bom_id': bom.id,
'location_src_id':
subcontract_move.picking_id.partner_id.with_context(
force_company=subcontract_move.company_id.id)
.property_stock_subcontractor.id,
'location_dest_id':
subcontract_move.picking_id.partner_id.with_context(
force_company=subcontract_move.company_id.id)
.property_stock_subcontractor.id,
'product_qty': subcontract_move.product_uom_qty,
'picking_type_id': warehouse.subcontracting_type_id.id
}
return vals
def _subcontracted_produce(self, subcontract_details):
self.ensure_one()
for move, bom in subcontract_details:
mo = self.env['mrp.production'].with_context(
force_company=move.company_id.id
).create(self._prepare_subcontract_mo_vals(move, bom))
# Link the finished to the receipt move.
finished_move = mo.move_finished_ids.filtered(
lambda m: m.product_id == move.product_id)
finished_move.write({'move_dest_ids': [(4, move.id, False)]})
mo.action_assign()