Files
odoo_source/addons/mrp_subcontracting/models/mrp_production.py
T
Rémy Voet (ryv) 09ce16861b [FIX] mrp_subcontracting: fix subcontracting + MTO
Issue:
In case of MTO on the component tracked by serial of the subcontracted
MO, if the picking generated by the MTO is complete
before record component on the subcontracted picking, when we
record the component, move lines is duplicated, one for the
reservation (due to the MTO chained move) and a other only with the
quantity done. Then it is painful to register component.

Fixes:
Don't the call of `_set_qty_producing()` when we click on
'Record Component', the user should it self applied the `qty_producing`.

closes odoo/odoo#68151

X-original-commit: e61ae06572db8aa5d1c3ecea754d3be6719eb55b
Signed-off-by: Steve Van Essche <svs-odoo@users.noreply.github.com>
Signed-off-by: Rémy Voet <ryv-odoo@users.noreply.github.com>
2021-03-19 14:34:05 +00:00

141 lines
7.0 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import fields, models, _, api
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_compare, float_is_zero
class MrpProduction(models.Model):
_inherit = 'mrp.production'
move_line_raw_ids = fields.One2many(
'stock.move.line', string="Detail Component", readonly=False,
inverse='_inverse_move_line_raw_ids', compute='_compute_move_line_raw_ids'
)
@api.depends('move_raw_ids.move_line_ids')
def _compute_move_line_raw_ids(self):
for production in self:
production.move_line_raw_ids = production.move_raw_ids.move_line_ids
def _inverse_move_line_raw_ids(self):
for production in self:
line_by_product = defaultdict(lambda: self.env['stock.move.line'])
for line in production.move_line_raw_ids:
line_by_product[line.product_id] |= line
for move in production.move_raw_ids:
move.move_line_ids = line_by_product.pop(move.product_id, self.env['stock.move.line'])
for product_id, lines in line_by_product.items():
qty = sum(line.product_uom_id._compute_quantity(line.qty_done, product_id.uom_id) for line in lines)
move = production._get_move_raw_values(product_id, qty, product_id.uom_id)
move['additional'] = True
production.move_raw_ids = [(0, 0, move)]
production.move_raw_ids.filtered(lambda m: m.product_id == product_id)[:1].move_line_ids = lines
def subcontracting_record_component(self):
self.ensure_one()
assert self.env.context.get('subcontract_move_id')
if float_is_zero(self.qty_producing, precision_rounding=self.product_uom_id.rounding):
return {'type': 'ir.actions.act_window_close'}
for sml in self.move_raw_ids.move_line_ids:
if sml.tracking != 'none' and not sml.lot_id:
raise UserError(_('You must enter a serial number for each line of %s') % sml.product_id.name)
self._update_finished_move()
quantity_issues = self._get_quantity_produced_issues()
if quantity_issues:
backorder = self._generate_backorder_productions(close_mo=False)
# No qty to consume to avoid propagate additional move
# TODO avoid : stock move created in backorder with 0 as qty
backorder.move_raw_ids.filtered(lambda m: m.additional).product_uom_qty = 0.0
backorder.qty_producing = backorder.product_qty
backorder._set_qty_producing()
self.product_qty = self.qty_producing
subcontract_move_id = self.env['stock.move'].browse(self.env.context.get('subcontract_move_id'))
action = subcontract_move_id._action_record_components()
action.update({'res_id': backorder.id})
return action
return {'type': 'ir.actions.act_window_close'}
def _pre_button_mark_done(self):
if self.env.context.get('subcontract_move_id'):
return True
return super()._pre_button_mark_done()
def _update_finished_move(self):
""" After producing, set the move line on the subcontract picking. """
self.ensure_one()
subcontract_move_id = self.env.context.get('subcontract_move_id')
if subcontract_move_id:
subcontract_move_id = self.env['stock.move'].browse(subcontract_move_id)
quantity = self.qty_producing
if self.lot_producing_id:
move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: ml.lot_id == self.lot_producing_id or not ml.lot_id)
else:
move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: not ml.lot_id)
# Update reservation and quantity done
for ml in move_lines:
rounding = ml.product_uom_id.rounding
if float_compare(quantity, 0, precision_rounding=rounding) <= 0:
break
quantity_to_process = min(quantity, ml.product_uom_qty - ml.qty_done)
quantity -= quantity_to_process
new_quantity_done = (ml.qty_done + quantity_to_process)
# on which lot of finished product
if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0:
ml.write({
'qty_done': new_quantity_done,
'lot_id': self.lot_producing_id and self.lot_producing_id.id,
})
else:
new_qty_reserved = ml.product_uom_qty - new_quantity_done
default = {
'product_uom_qty': new_quantity_done,
'qty_done': new_quantity_done,
'lot_id': self.lot_producing_id and self.lot_producing_id.id,
}
ml.copy(default=default)
ml.with_context(bypass_reservation_update=True).write({
'product_uom_qty': new_qty_reserved,
'qty_done': 0
})
if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) > 0:
self.env['stock.move.line'].create({
'move_id': subcontract_move_id.id,
'picking_id': subcontract_move_id.picking_id.id,
'product_id': self.product_id.id,
'location_id': subcontract_move_id.location_id.id,
'location_dest_id': subcontract_move_id.location_dest_id.id,
'product_uom_qty': 0,
'product_uom_id': self.product_uom_id.id,
'qty_done': quantity,
'lot_id': self.lot_producing_id and self.lot_producing_id.id,
})
if not self._get_quantity_to_backorder():
ml_reserved = subcontract_move_id.move_line_ids.filtered(lambda ml:
float_is_zero(ml.qty_done, precision_rounding=ml.product_uom_id.rounding) and
not float_is_zero(ml.product_uom_qty, precision_rounding=ml.product_uom_id.rounding))
ml_reserved.unlink()
for ml in subcontract_move_id.move_line_ids:
ml.product_uom_qty = ml.qty_done
subcontract_move_id._recompute_state()
def _subcontracting_filter_to_done(self):
""" Filter subcontracting production where composant is already recorded and should be consider to be validate """
def filter_in(mo):
if mo.state in ('done', 'cancel'):
return False
if float_is_zero(mo.qty_producing, precision_rounding=mo.product_uom_id.rounding):
return False
if not all(line.lot_id for line in mo.move_raw_ids.filtered(lambda sm: sm.has_tracking != 'none').move_line_ids):
return False
return True
return self.filtered(filter_in)