[REF] mrp_subcontracting: manage backorder
closes odoo/odoo#37046 Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from odoo.tools.float_utils import float_compare, float_is_zero
|
||||
class StockMove(models.Model):
|
||||
_inherit = 'stock.move'
|
||||
|
||||
is_subcontract = fields.Boolean('The move is a subcontract receipt', copy=False)
|
||||
is_subcontract = fields.Boolean('The move is a subcontract receipt')
|
||||
show_subcontracting_details_visible = fields.Boolean(
|
||||
compute='_compute_show_subcontracting_details_visible'
|
||||
)
|
||||
@@ -38,12 +38,23 @@ class StockMove(models.Model):
|
||||
move.show_details_visible = True
|
||||
return res
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
if not self.is_subcontract or 'location_id' in default:
|
||||
return super(StockMove, self).copy(default=default)
|
||||
if not default:
|
||||
default = {}
|
||||
default['location_id'] = self.picking_id.location_id.id
|
||||
return super(StockMove, self).copy(default=default)
|
||||
|
||||
def write(self, values):
|
||||
""" If the initial demand is updated then also update the linked
|
||||
subcontract order to the new quantity.
|
||||
"""
|
||||
res = super(StockMove, self).write(values)
|
||||
if 'product_uom_qty' in values:
|
||||
if self.env.context.get('cancel_backorder') is False:
|
||||
return res
|
||||
self.filtered(lambda m: m.is_subcontract and
|
||||
m.state not in ['draft', 'cancel', 'done'])._update_subcontract_order_qty()
|
||||
return res
|
||||
@@ -88,11 +99,14 @@ class StockMove(models.Model):
|
||||
for move in self:
|
||||
if move.location_id.usage != 'supplier' or move.location_dest_id.usage == 'supplier':
|
||||
continue
|
||||
if not move.picking_id or self.env.context.get('do_not_create_subcontract_order'):
|
||||
if move.move_orig_ids.production_id:
|
||||
continue
|
||||
bom = move._get_subcontract_bom()
|
||||
if not bom:
|
||||
continue
|
||||
if float_is_zero(move.product_qty, precision_rounding=move.product_uom.rounding) and\
|
||||
move.picking_id.immediate_transfer is True:
|
||||
raise UserError(_("To subcontract, use a planned transfer."))
|
||||
subcontract_details_per_picking[move.picking_id].append((move, bom))
|
||||
move.write({
|
||||
'is_subcontract': True,
|
||||
@@ -154,6 +168,11 @@ operations.""") % ('\n'.join(overprocessed_moves.mapped('product_id.display_name
|
||||
self.ensure_one()
|
||||
return any(m.has_tracking != 'none' for m in self.move_orig_ids.production_id.move_raw_ids)
|
||||
|
||||
def _prepare_extra_move_vals(self, qty):
|
||||
vals = super(StockMove, self)._prepare_extra_move_vals(qty)
|
||||
vals['location_id'] = self.location_id.id
|
||||
return vals
|
||||
|
||||
def _should_bypass_reservation(self):
|
||||
""" If the move is subcontracted then ignore the reservation. """
|
||||
should_bypass_reservation = super(StockMove, self)._should_bypass_reservation()
|
||||
@@ -169,3 +188,4 @@ operations.""") % ('\n'.join(overprocessed_moves.mapped('product_id.display_name
|
||||
'mo_id': production.id,
|
||||
'product_qty': move.product_uom_qty
|
||||
}).change_prod_qty()
|
||||
|
||||
|
||||
@@ -43,12 +43,7 @@ class StockPicking(models.Model):
|
||||
return super(StockPicking, self).action_cancel()
|
||||
|
||||
def action_done(self):
|
||||
# action_done with an extra move will trigger two methods that
|
||||
# create/modify a subcontracting order, _action_confirm and a write on
|
||||
# product_uom_qty. This context will be used by _action_confirm and it
|
||||
# will not create a subcontract order. Instead the existing order will
|
||||
# be updated in the stock.move write during the merge move.
|
||||
res = super(StockPicking, self.with_context(do_not_create_subcontract_order=True)).action_done()
|
||||
res = super(StockPicking, self).action_done()
|
||||
productions = self.env['mrp.production']
|
||||
for picking in self:
|
||||
for move in picking.move_lines:
|
||||
@@ -56,8 +51,8 @@ class StockPicking(models.Model):
|
||||
continue
|
||||
production = move.move_orig_ids.production_id
|
||||
if move._has_tracked_subcontract_components():
|
||||
move.move_orig_ids.move_line_ids.unlink()
|
||||
move_finished_ids = move.move_orig_ids
|
||||
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:
|
||||
ml.copy({
|
||||
'picking_id': False,
|
||||
@@ -81,7 +76,10 @@ class StockPicking(models.Model):
|
||||
produce._record_production()
|
||||
productions |= production
|
||||
for subcontracted_production in productions:
|
||||
subcontracted_production.button_mark_done()
|
||||
if subcontracted_production.state == 'progress':
|
||||
subcontracted_production.post_inventory()
|
||||
else:
|
||||
subcontracted_production.button_mark_done()
|
||||
# For concistency, set the date on production move before the date
|
||||
# on picking. (Tracability report + Product Moves menu item)
|
||||
minimum_date = min(picking.move_line_ids.mapped('date'))
|
||||
|
||||
@@ -510,6 +510,36 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
|
||||
self.assertEqual(move_comp1.move_line_ids.filtered(lambda ml: not ml.product_uom_qty).lot_produced_ids.name, 'LOT F2')
|
||||
self.assertEqual(move_comp2.move_line_ids.filtered(lambda ml: not ml.product_uom_qty).lot_produced_ids.name, 'LOT F2')
|
||||
|
||||
def test_flow_8(self):
|
||||
resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
|
||||
(self.comp1 + self.comp2).write({'route_ids': [(4, resupply_sub_on_order_route.id, None)]})
|
||||
|
||||
# Create a receipt picking from the subcontractor
|
||||
picking_form = Form(self.env['stock.picking'])
|
||||
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
||||
picking_form.partner_id = self.subcontractor_partner1
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = self.finished
|
||||
move.product_uom_qty = 5
|
||||
picking_receipt = picking_form.save()
|
||||
picking_receipt.action_confirm()
|
||||
|
||||
picking_receipt.move_lines.quantity_done = 3
|
||||
backorder_wiz = picking_receipt.button_validate()
|
||||
backorder_wiz = self.env['stock.backorder.confirmation'].browse(backorder_wiz['res_id'])
|
||||
backorder_wiz.process()
|
||||
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_receipt.id)])
|
||||
self.assertTrue(backorder)
|
||||
self.assertEqual(backorder.move_lines.product_uom_qty, 2)
|
||||
subcontract_order = backorder.move_lines.move_orig_ids.production_id.filtered(lambda p: p.state != 'done')
|
||||
self.assertTrue(subcontract_order)
|
||||
self.assertEqual(subcontract_order.product_uom_qty, 5)
|
||||
self.assertEqual(subcontract_order.qty_produced, 3)
|
||||
backorder.move_lines.quantity_done = 2
|
||||
backorder.action_done()
|
||||
self.assertTrue(picking_receipt.move_lines.move_orig_ids.production_id.state == 'done')
|
||||
|
||||
class TestSubcontractingTracking(TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestSubcontractingTracking, self).setUp()
|
||||
|
||||
Reference in New Issue
Block a user