[IMP] mrp: don't require consumed widget when multi-location

Before this commit, it was required to always use the widget to edit the
consumed quantity of a component when multi-location or packages setting
is active. This was not user friendly so this commit relaxes the
requirement to only tracked products and gives users the option to still
edit the consumed move lines directly if desired. We also distinguish
between when using the widget is required or not with the widget button
color. Note that if quantity consumed is greater than amount reserved in
different locations, the difference will default to the move's source
location (this is consistent with what happens when the `qty_producing`
is changed in the MO and the consumed quantities are scaled to match.)

Task: 2518523
Upgrade PR: odoo/upgrade#2655

closes odoo/odoo#73939

Related: odoo/enterprise#19732
Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com>
This commit is contained in:
Tiffany Chang (tic)
2021-07-26 12:04:30 +00:00
parent 1e2c03a47d
commit 0b60c863ef
6 changed files with 45 additions and 23 deletions
+7
View File
@@ -419,3 +419,10 @@ class StockMove(models.Model):
candidate_moves_list.append(production.move_raw_ids)
for production in self.mapped('production_id'):
candidate_moves_list.append(production.move_finished_ids)
def _multi_line_quantity_done_set(self, quantity_done):
if self.raw_material_production_id:
self.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0
self.move_line_ids = self._set_quantity_done_prepare_vals(quantity_done)
else:
super()._multi_line_quantity_done_set(quantity_done)
@@ -9,3 +9,11 @@
.o_should_consume{
padding-left: 0.3em;
}
.btn-link.o_optional_button{
color: gray;
&:hover {
filter: brightness(0.9);
}
}
+11 -8
View File
@@ -94,7 +94,7 @@
<button name="button_scrap" type="object" string="Scrap" attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="z"/>
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,progress,done"/>
<button name="action_toggle_is_locked" attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', False)]}" string="Unlock" groups="mrp.group_mrp_manager" type="object" help="Unlock the manufacturing order to adjust what has been consumed or produced." data-hotkey="l"/>
<button name="action_toggle_is_locked" attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}" string="Lock" groups="mrp.group_mrp_manager" type="object" help="Lock the manufacturing order to prevent changes to what has been consumed or produced." data-hotkey="l"/>/>
<button name="action_toggle_is_locked" attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}" string="Lock" groups="mrp.group_mrp_manager" type="object" help="Lock the manufacturing order to prevent changes to what has been consumed or produced." data-hotkey="l"/>
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', True)]}"/>
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
@@ -232,8 +232,8 @@
context="{'default_date': date_planned_start, 'default_date_deadline': date_deadline, 'default_location_id': location_src_id, 'default_location_dest_id': production_location_id, 'default_state': 'draft', 'default_raw_material_production_id': id, 'default_picking_type_id': picking_type_id, 'default_company_id': company_id}"
attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&amp;', ('state', '=', 'done'), ('is_locked', '=', True)]}" options="{'delete': [('state', '=', 'draft')]}">
<tree default_order="is_done,sequence" editable="bottom">
<field name="product_id" force_save="1" required="1" context="{'default_type': 'product'}" attrs="{'readonly': ['|', '|', ('has_move_lines', '=', True), ('state', '=', 'cancel'), '&amp;', ('state', '!=', 'draft'), ('additional', '=', False) ]}"/>
<field name="product_id" force_save="1" required="1" context="{'default_type': 'product'}" attrs="{'readonly': ['|', '|', ('move_lines_count', '&gt;', 0), ('state', '=', 'cancel'), '&amp;', ('state', '!=', 'draft'), ('additional', '=', False) ]}"/>
<field name="location_id" string="From" readonly="1" groups="stock.group_stock_multi_locations"/>
<field name="move_line_ids" invisible="1">
<tree>
<field name="lot_id" invisible="1"/>
@@ -271,7 +271,7 @@
<field name="location_id" invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="is_locked" invisible="1"/>
<field name="has_move_lines" invisible="1"/>
<field name="move_lines_count" invisible="1"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="state" invisible="1" force_save="1"/>
<field name="should_consume_qty" invisible="1"/>
@@ -285,7 +285,7 @@
<field name="quantity_done" string="Consumed"
decoration-success="not is_done and (quantity_done - should_consume_qty == 0)"
decoration-warning="not is_done and (quantity_done - should_consume_qty &gt; 0.0001)"
attrs="{'column_invisible': [('parent.state', '=', 'draft')], 'readonly': [('show_details_visible', '=', True)]}"/>
attrs="{'column_invisible': [('parent.state', '=', 'draft')], 'readonly': [('has_tracking', '!=','none')]}"/>
<field name="product_uom" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/>
<field name="show_details_visible" invisible="1"/>
<field name="lot_ids" widget="many2many_tags"
@@ -296,7 +296,8 @@
domain="[('product_id','=',product_id)]"
/>
<field name="group_id" invisible="1"/>
<button name="action_show_details" type="object" icon="fa-list" context="{'default_product_uom_qty': 0}" attrs="{'invisible': [('show_details_visible', '=', False)]}" options="{&quot;warn&quot;: true}"/>
<button name="action_show_details" type="object" icon="fa-list" context="{'default_product_uom_qty': 0}" attrs="{'invisible': ['|', ('show_details_visible', '=', False), ('has_tracking', '=','none')]}" options="{&quot;warn&quot;: true}"/>
<button class="o_optional_button" name="action_show_details" type="object" icon="fa-list" context="{'default_product_uom_qty': 0}" attrs="{'invisible': ['|', ('has_tracking', '!=','none'), ('show_details_visible', '=', False)]}" options="{&quot;warn&quot;: true}"/>
</tree>
</field>
</page>
@@ -308,6 +309,7 @@
<tree default_order="is_done,sequence" decoration-muted="is_done" editable="bottom">
<field name="byproduct_id" invisible="1"/>
<field name="product_id" context="{'default_type': 'product'}" domain="[('id', '!=', parent.product_id)]" required="1"/>
<field name="location_dest_id" string="To" readonly="1" groups="stock.group_stock_multi_locations"/>
<field name="move_line_ids" invisible="1">
<tree>
@@ -343,7 +345,7 @@
<field name="location_id" invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="is_locked" invisible="1"/>
<field name="has_move_lines" invisible="1"/>
<field name="move_lines_count" invisible="1"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="state" invisible="1" force_save="1"/>
<field name="product_uom_qty" string="To Produce" attrs="{'readonly': ['&amp;', ('parent.state', '!=', 'draft'), '|', '&amp;', ('parent.state', 'not in', ('confirmed', 'progress', 'to_close')), ('parent.is_planned', '!=', True), ('parent.is_locked', '=', True)]}"/>
@@ -358,7 +360,8 @@
context="{'default_company_id': company_id, 'default_product_id': product_id}"
domain="[('product_id','=',product_id)]"
/>
<button name="action_show_details" type="object" icon="fa-list" attrs="{'invisible': [('show_details_visible', '=', False)]}" options="{&quot;warn&quot;: true}"/>
<button name="action_show_details" type="object" icon="fa-list" attrs="{'invisible': ['|', ('has_tracking', '=','none'), ('show_details_visible', '=', False)]}" options="{&quot;warn&quot;: true}"/>
<button class="o_optional_button" name="action_show_details" type="object" icon="fa-list" attrs="{'invisible': ['|', ('has_tracking', '!=','none'), ('show_details_visible', '=', False)]}" options="{&quot;warn&quot;: true}"/>
</tree>
</field>
</page>
+13 -9
View File
@@ -163,7 +163,7 @@ class StockMove(models.Model):
is_initial_demand_editable = fields.Boolean('Is initial demand editable', compute='_compute_is_initial_demand_editable')
is_quantity_done_editable = fields.Boolean('Is quantity done editable', compute='_compute_is_quantity_done_editable')
reference = fields.Char(compute='_compute_reference', string="Reference", store=True)
has_move_lines = fields.Boolean(compute='_compute_has_move_lines')
move_lines_count = fields.Integer(compute='_compute_move_lines_count')
package_level_id = fields.Many2one('stock.package_level', 'Package Level', check_company=True, copy=False)
picking_type_entire_packs = fields.Boolean(related='picking_type_id.show_entire_packs', readonly=True)
display_assign_serial = fields.Boolean(compute='_compute_display_assign_serial')
@@ -268,9 +268,9 @@ class StockMove(models.Model):
move.reference = move.picking_id.name if move.picking_id else move.name
@api.depends('move_line_ids')
def _compute_has_move_lines(self):
def _compute_move_lines_count(self):
for move in self:
move.has_move_lines = bool(move.move_line_ids)
move.move_lines_count = len(move.move_line_ids)
@api.depends('product_id', 'product_uom', 'product_uom_qty')
def _compute_product_qty(self):
@@ -352,12 +352,16 @@ class StockMove(models.Model):
elif len(move_lines) == 1:
move_lines[0].qty_done = quantity_done
else:
# Bypass the error if we're trying to write the same value.
ml_quantity_done = 0
for move_line in move_lines:
ml_quantity_done += move_line.product_uom_id._compute_quantity(move_line.qty_done, move.product_uom, round=False)
if float_compare(quantity_done, ml_quantity_done, precision_rounding=move.product_uom.rounding) != 0:
raise UserError(_("Cannot set the done quantity from this stock move, work directly with the move lines."))
move._multi_line_quantity_done_set(quantity_done)
def _multi_line_quantity_done_set(self, quantity_done):
move_lines = self._get_move_lines()
# Bypass the error if we're trying to write the same value.
ml_quantity_done = 0
for move_line in move_lines:
ml_quantity_done += move_line.product_uom_id._compute_quantity(move_line.qty_done, self.product_uom, round=False)
if float_compare(quantity_done, ml_quantity_done, precision_rounding=self.product_uom.rounding) != 0:
raise UserError(_("Cannot set the done quantity from this stock move, work directly with the move lines."))
def _set_product_qty(self):
""" The meaning of product_qty field changed lately and is now a functional field computing the quantity
+2 -2
View File
@@ -66,10 +66,10 @@
<field name="show_reserved_availability" invisible="1"/>
<field name="show_operations" invisible="1" readonly="1"/>
<field name="additional" invisible="1"/>
<field name="has_move_lines" invisible="1"/>
<field name="move_lines_count" invisible="1"/>
<field name="is_locked" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="product_id" required="1" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('has_move_lines', '=', True)]}"/>
<field name="product_id" required="1" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('move_lines_count', '&gt;', 0)]}"/>
<field name="is_initial_demand_editable" invisible="1"/>
<field name="is_quantity_done_editable" invisible="1"/>
<field name="product_uom_qty" string="Demand" attrs="{'readonly': [('is_initial_demand_editable', '=', False)]}"/>
+4 -4
View File
@@ -351,12 +351,12 @@
<field name="show_reserved_availability" invisible="1"/>
<field name="show_operations" invisible="1" readonly="1"/>
<field name="additional" invisible="1"/>
<field name="has_move_lines" invisible="1"/>
<field name="move_lines_count" invisible="1"/>
<field name="is_locked" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="has_tracking" invisible="1"/>
<field name="display_assign_serial" invisible="1"/>
<field name="product_id" required="1" context="{'default_type': 'product'}" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('has_move_lines', '=', True)]}"/>
<field name="product_id" required="1" context="{'default_type': 'product'}" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('move_lines_count', '&gt;', 0)]}"/>
<field name="description_picking" string="Description" optional="hide"/>
<field name="date" invisible="1"/>
<field name="date_deadline" optional="hide"/>
@@ -395,9 +395,9 @@
<group>
<field name="product_uom_category_id" invisible="1"/>
<field name="additional" invisible="1"/>
<field name="has_move_lines" invisible="1"/>
<field name="move_lines_count" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id" required="1" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('has_move_lines', '=', True)]}"/>
<field name="product_id" required="1" attrs="{'readonly': ['|', '&amp;', ('state', '!=', 'draft'), ('additional', '=', False), ('move_lines_count', '&gt;', 0)]}"/>
<field name="is_initial_demand_editable" invisible="1"/>
<field name="is_quantity_done_editable" invisible="1"/>
<field name="product_uom_qty" attrs="{'invisible': [('parent.immediate_transfer', '=', True)], 'readonly': [('is_initial_demand_editable', '=', False)]}"/>