Files
odoo_source/addons/mrp_subcontracting/models/stock_picking.py
T
Simon Lejeune d91db5bde6 [ADD] mrp_subcontracting
In a few words, it allows to send components to a subcontractor partner
and receive a finished product. The production is managed through classical
manufacturing orders where the raw and finished location is the
subcontracting location. When creating a receipt picking for a
subcontractor partner, the location_id of the moves will be set to the
subcontracting locations by using the property_stock_customer and
property_stock_supplier of the partner. An override of _action_confirm
will create the manufacturing order.

When a subcontracted bom contains tracking, the produce wizard is
available from the picking to record which lots were produced/consumed.

New type on the BoM: subcontract
New type on res.partner: subcontactor
New location per company (we still own the components stored at the
subcontractor so they're still valued)
New routes created to resupply the subcontracting location on order or
not (through a reordering rule).
Override of res.partner to set the property_stock_customer/supplier on
subcontactor partners.

Proper tests were added too.

Joint work with: Arnaud Baes <arb@odoo.com>

task 1831382
2019-04-03 13:52:22 +00:00

134 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class StockPicking(models.Model):
_inherit = 'stock.picking'
display_action_record_components = fields.Boolean(compute='_compute_display_action_record_components')
display_view_subcontracted_move_lines = fields.Boolean(compute='_compute_display_view_subcontracted_move_lines')
@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'):
continue
if not picking._is_subcontract():
continue
# Hide if no move is tracked
subcontracted_productions = picking._get_subcontracted_productions()
subcontracted_moves = subcontracted_productions.mapped('move_raw_ids')
subcontracted_moves |= subcontracted_productions.mapped('move_finished_ids')
if all(subcontracted_move.has_tracking == 'none' for subcontracted_move in subcontracted_moves):
continue
# Hide if the production is to close
if not subcontracted_productions.filtered(lambda mo: mo.state not in ('to_close', 'done')):
continue
picking.display_action_record_components = True
@api.depends('state')
def _compute_display_view_subcontracted_move_lines(self):
for picking in self:
# Hide if not encoding state
if picking.state in ('draft', 'cancel'):
continue
if not picking._is_subcontract():
continue
# Hide until state done if no move is tracked, if tracked until something was produced
subcontracted_productions = picking._get_subcontracted_productions()
subcontracted_moves = subcontracted_productions.mapped('move_raw_ids')
subcontracted_moves |= subcontracted_productions.mapped('move_finished_ids')
if all(subcontracted_move.has_tracking == 'none' for subcontracted_move in subcontracted_moves):
if picking.state != 'done':
continue
# Hide if nothing was produced
if all(subcontracted_move.quantity_done == 0 for subcontracted_move in subcontracted_moves):
continue
picking.display_view_subcontracted_move_lines = True
# -------------------------------------------------------------------------
# Action methods
# -------------------------------------------------------------------------
def action_done(self):
for picking in self:
subcontracted_productions = picking._get_subcontracted_productions()
for subcontracted_production in subcontracted_productions:
subcontracted_production.button_mark_done()
return super(StockPicking, self).action_done()
def action_view_subcontracted_move_lines(self):
""" Returns a list view with the move lines of the subcontracted products. To find them, we
look on the origin moves of the move lines of the picking if there is a manufacturing order.
"""
self.ensure_one()
subcontracted_productions = self._get_subcontracted_productions()
subcontracted_move_lines = self.env['stock.move.line']
for subcontracted_production in subcontracted_productions:
subcontracted_move_lines |= subcontracted_production.move_raw_ids.mapped('move_line_ids')
subcontracted_move_lines |= subcontracted_production.move_finished_ids.mapped('move_line_ids')
action = self.env.ref('stock.stock_move_line_action').read()[0]
action['context'] = {}
action['domain'] = [('id', 'in', subcontracted_move_lines.ids)]
return action
def action_record_components(self):
self.ensure_one()
subcontracted_productions = self._get_subcontracted_productions()
to_register = subcontracted_productions.filtered(lambda mo: mo.state not in ('to_close', 'done'))
if to_register:
mo = to_register[0]
action = self.env.ref('mrp.act_mrp_product_produce').read()[0]
action['context'] = dict(self.env.context, active_id=mo.id)
return action
# -------------------------------------------------------------------------
# Subcontract helpers
# -------------------------------------------------------------------------
def _is_subcontract(self):
self.ensure_one()
if self.partner_id.type == 'subcontractor' and \
self.picking_type_id.code == 'incoming':
return True
return False
def _get_subcontracted_productions(self):
self.ensure_one()
return self.move_lines.mapped('move_orig_ids.production_id')
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
subcontract_move.ensure_one()
product = subcontract_move.product_id
warehouse = subcontract_move.warehouse_id or self.picking_type_id.warehouse_id
vals = {
'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.property_stock_supplier.id,
'location_dest_id': subcontract_move.location_id.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'].create(self._prepare_subcontract_mo_vals(move, bom))
self.env['stock.move'].create(mo._get_moves_raw_values())
mo.action_confirm()
# 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()
# Only skip the produce wizard if no moves is tracked
moves = mo.mapped('move_raw_ids') + mo.mapped('move_finished_ids')
if any(move.has_tracking != 'none' for move in moves):
continue
for m in moves:
m.quantity_done = m.product_uom_qty