From 7dda6bb92715ea25b2818a62fec5e646f3678b81 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Thu, 5 Oct 2023 16:28:19 +0200 Subject: [PATCH] [REF] stock: quantity pocalypse The rational is: Currently we have 2 columns. One for reservation, the other for quantity picked. However in real time, either you follow the reservation and everything goes well. Otherwise you pick something else. In the case where you pick somewhere else than reserved, you would like to modify the reservation to have something similar and free the quantity you didn't pick and expect the system to not suggest the ones you took. In other hand, we always want to have the reserve quantity similar to the done. On top, having two columns could be confusing for the end user. The cons: -The qty_done column could be use during the picking, to remember if something has been pick or still to pick. - For some flow (put in pack), it's easier to write a part of the quantity to pack and still want to reserve the full amount of product. We goes back and choose a ligther interface over complex feature. Changes: Qty done and reserved qty are merged into a single column. A new checkbox on the move exists to mark it as picked or not Since the reservation always follow the quantity, it's now possible to have more reserved quantity than stock. However the system will never propose it and the inventory showing reserved > quantity should be a warning. The system should never modify a move that has been picked. We don't want to overide the user action. Regression: Not able to pick a single stock.move.line closes odoo/odoo#137864 Related: odoo/enterprise#48709 Related: odoo/upgrade#5310 Signed-off-by: Arnold Moyaux (arm) --- .../report/l10n_it_ddt_report.xml | 8 +- addons/l10n_it_stock_ddt/tests/test_ddt.py | 10 +- addons/l10n_it_stock_ddt/tests/test_edi.py | 2 +- addons/mrp/data/mrp_demo.xml | 2 +- addons/mrp/models/mrp_production.py | 122 +-- addons/mrp/models/mrp_unbuild.py | 52 +- addons/mrp/models/mrp_workorder.py | 11 +- addons/mrp/models/product.py | 2 +- addons/mrp/models/stock_move.py | 43 +- addons/mrp/models/stock_scrap.py | 3 +- .../mrp/report/mrp_production_templates.xml | 8 +- addons/mrp/report/mrp_report_mo_overview.py | 10 +- .../report/mrp_zebra_production_templates.xml | 2 +- .../mrp_production_components_x2many.js | 2 +- .../tests/tours/mrp_manual_consumption.js | 10 +- addons/mrp/tests/test_backorder.py | 40 +- addons/mrp/tests/test_bom.py | 2 +- addons/mrp/tests/test_byproduct.py | 7 +- addons/mrp/tests/test_consume_component.py | 31 +- addons/mrp/tests/test_manual_consumption.py | 32 +- addons/mrp/tests/test_multicompany.py | 3 +- addons/mrp/tests/test_order.py | 307 ++++--- addons/mrp/tests/test_procurement.py | 26 +- addons/mrp/tests/test_smp.py | 8 +- addons/mrp/tests/test_stock.py | 25 +- addons/mrp/tests/test_stock_report.py | 6 +- addons/mrp/tests/test_traceability.py | 63 +- addons/mrp/tests/test_unbuild.py | 47 +- .../test_warehouse_multistep_manufacturing.py | 4 +- addons/mrp/views/mrp_production_views.xml | 18 +- addons/mrp/views/mrp_workorder_views.xml | 4 +- addons/mrp/views/stock_move_views.xml | 10 +- addons/mrp/wizard/change_production_qty.py | 14 +- addons/mrp/wizard/mrp_consumption_warning.py | 7 +- .../mrp/wizard/stock_assign_serial_numbers.py | 4 - addons/mrp_account/data/mrp_account_demo.xml | 4 +- addons/mrp_account/models/mrp_production.py | 14 +- addons/mrp_account/models/stock_move.py | 10 +- .../tests/test_analytic_account.py | 2 +- .../tests/test_valuation_layers.py | 18 +- .../tests/test_mrp_product_expiry.py | 4 +- addons/mrp_repair/tests/test_tracability.py | 6 +- .../models/mrp_production.py | 44 +- .../mrp_subcontracting/models/stock_move.py | 18 +- .../models/stock_picking.py | 19 +- .../tests/test_subcontracting.py | 128 +-- .../tests/test_subcontracting_portal_ui.py | 4 +- .../views/stock_move_views.xml | 27 +- .../views/subcontracting_portal_views.xml | 2 +- .../models/mrp_production.py | 2 +- .../tests/test_subcontracting_account.py | 31 +- .../tests/test_anglo_saxon_valuation.py | 9 +- .../tests/test_purchase_subcontracting.py | 12 +- .../tests/test_sale_dropshipping.py | 37 +- .../tests/test_mrp_subcontracting_purchase.py | 26 +- addons/point_of_sale/models/pos_session.py | 4 +- addons/point_of_sale/models/stock_picking.py | 68 +- .../point_of_sale/tests/test_anglo_saxon.py | 2 +- addons/point_of_sale/tests/test_pos_margin.py | 12 +- addons/pos_sale/tests/test_pos_sale_flow.py | 15 +- .../tests/test_generate_serial_numbers.py | 30 +- addons/product_expiry/tests/test_stock_lot.py | 30 +- .../product_expiry/views/stock_move_views.xml | 11 - addons/purchase/models/purchase.py | 2 +- .../tests/test_anglo_saxon_valuation.py | 10 +- .../tests/test_purchase_mrp_flow.py | 15 +- .../tests/test_purchase_requisition_stock.py | 7 +- addons/purchase_stock/models/purchase.py | 10 +- addons/purchase_stock/models/res_partner.py | 8 +- addons/purchase_stock/models/stock_move.py | 2 +- .../report/vendor_delay_report.py | 2 +- ...st_anglo_saxon_valuation_reconciliation.py | 3 +- .../tests/test_average_price.py | 17 +- .../tests/test_create_picking.py | 48 +- .../purchase_stock/tests/test_fifo_price.py | 42 +- .../purchase_stock/tests/test_fifo_returns.py | 9 +- .../tests/test_move_cancel_propagation.py | 6 - .../tests/test_purchase_order.py | 93 +-- .../test_purchase_stock_accrued_entries.py | 11 +- .../tests/test_purchase_stock_report.py | 24 +- .../tests/test_reordering_rule.py | 16 +- .../tests/test_stockvaluation.py | 123 +-- addons/purchase_stock/tests/test_uninstall.py | 3 +- addons/repair/models/product.py | 4 +- addons/repair/models/repair.py | 37 +- addons/repair/models/sale_order.py | 2 +- addons/repair/models/stock_move.py | 2 +- addons/repair/tests/test_repair.py | 55 +- addons/repair/views/repair_views.xml | 8 +- addons/sale_mrp/models/sale_order_line.py | 4 +- addons/sale_mrp/models/stock_move.py | 2 +- .../test_sale_mrp_anglo_saxon_valuation.py | 27 +- addons/sale_mrp/tests/test_sale_mrp_flow.py | 76 +- .../sale_mrp/tests/test_sale_mrp_kit_bom.py | 12 +- addons/sale_mrp/tests/test_sale_mrp_report.py | 6 +- .../tests/test_sale_purchase_stock_flow.py | 6 +- addons/sale_stock/models/account_move.py | 18 +- addons/sale_stock/models/sale_order_line.py | 12 +- addons/sale_stock/models/stock.py | 4 +- .../tests/test_anglo_saxon_valuation.py | 111 +-- ...st_anglo_saxon_valuation_reconciliation.py | 4 +- .../sale_stock/tests/test_sale_order_dates.py | 3 +- addons/sale_stock/tests/test_sale_stock.py | 157 ++-- .../tests/test_sale_stock_accrued_entries.py | 8 +- .../tests/test_sale_stock_report.py | 57 +- .../tests/test_sale_stock_margin.py | 17 +- addons/stock/__manifest__.py | 1 - addons/stock/data/stock_demo2.xml | 30 +- addons/stock/i18n/stock.pot | 2 +- addons/stock/models/product.py | 2 +- addons/stock/models/stock_location.py | 21 +- addons/stock/models/stock_move.py | 437 ++++------ addons/stock/models/stock_move_line.py | 472 +++++------ addons/stock/models/stock_package_level.py | 25 +- addons/stock/models/stock_picking.py | 255 ++---- addons/stock/models/stock_quant.py | 69 +- addons/stock/models/stock_scrap.py | 25 +- addons/stock/populate/stock.py | 10 +- addons/stock/report/picking_templates.xml | 12 +- addons/stock/report/report_deliveryslip.xml | 16 +- addons/stock/report/report_stock_reception.py | 40 +- .../report/report_stockpicking_operations.xml | 7 +- addons/stock/report/stock_forecasted.py | 8 +- addons/stock/report/stock_traceability.py | 2 +- addons/stock/security/ir.model.access.csv | 2 - .../static/src/widgets/forecast_widget.js | 4 +- .../static/src/widgets/forecast_widget.xml | 4 +- .../static/src/widgets/generate_serial.js | 6 +- .../static/tests/tours/stock_picking_tour.js | 76 +- addons/stock/tests/__init__.py | 1 - .../tests/test_generate_serial_numbers.py | 78 +- addons/stock/tests/test_immediate.py | 7 +- addons/stock/tests/test_inventory.py | 18 +- addons/stock/tests/test_move.py | 782 ++++++++---------- addons/stock/tests/test_move2.py | 409 +++++---- addons/stock/tests/test_move_lines.py | 16 +- addons/stock/tests/test_multicompany.py | 32 +- addons/stock/tests/test_packing.py | 178 ++-- addons/stock/tests/test_packing_neg.py | 19 +- addons/stock/tests/test_picking.py | 1 - addons/stock/tests/test_picking_tours.py | 26 +- addons/stock/tests/test_proc_rule.py | 4 +- addons/stock/tests/test_product.py | 7 +- addons/stock/tests/test_quant.py | 86 +- .../stock/tests/test_quant_inventory_mode.py | 5 +- addons/stock/tests/test_report.py | 67 +- .../stock/tests/test_report_stock_quantity.py | 5 +- addons/stock/tests/test_robustness.py | 8 +- addons/stock/tests/test_stock_flow.py | 386 ++++----- .../stock/tests/test_stock_return_picking.py | 24 +- addons/stock/tests/test_warehouse.py | 39 +- addons/stock/tests/test_wise_operator.py | 196 ----- addons/stock/views/stock_move_line_views.xml | 17 +- addons/stock/views/stock_move_views.xml | 116 +-- .../stock/views/stock_package_level_views.xml | 7 +- addons/stock/views/stock_picking_views.xml | 34 +- addons/stock/views/stock_template.xml | 4 +- addons/stock/wizard/__init__.py | 1 - addons/stock/wizard/product_label_layout.py | 12 +- .../wizard/stock_backorder_confirmation.py | 4 +- .../stock/wizard/stock_immediate_transfer.py | 64 -- .../wizard/stock_immediate_transfer_views.xml | 36 - addons/stock/wizard/stock_lot_label_layout.py | 2 +- .../stock/wizard/stock_package_destination.py | 2 +- .../stock_package_destination_views.xml | 6 +- addons/stock/wizard/stock_picking_return.py | 6 +- addons/stock_account/models/product.py | 2 +- addons/stock_account/models/stock_move.py | 24 +- .../stock_account/models/stock_move_line.py | 10 +- .../models/stock_valuation_layer.py | 4 +- ...o_saxon_valuation_reconciliation_common.py | 8 +- .../tests/test_stockvaluation.py | 555 +++++++------ .../tests/test_stockvaluationlayer.py | 81 +- addons/stock_delivery/data/delivery_demo.xml | 6 +- .../stock_delivery/models/delivery_carrier.py | 4 +- addons/stock_delivery/models/stock_move.py | 6 +- addons/stock_delivery/models/stock_picking.py | 8 +- .../models/stock_quant_package.py | 6 +- .../tests/test_delivery_cost.py | 6 +- .../tests/test_delivery_stock_move.py | 14 +- .../tests/test_packing_delivery.py | 10 +- .../views/report_deliveryslip.xml | 4 +- .../wizard/choose_delivery_package.py | 10 +- .../stock_dropshipping/tests/test_dropship.py | 5 +- .../tests/test_lifo_price.py | 19 +- .../tests/test_stockvaluation.py | 13 +- .../models/stock_landed_cost.py | 6 +- .../tests/test_stock_landed_costs.py | 6 +- .../tests/test_stock_landed_costs_purchase.py | 16 +- .../tests/test_stock_landed_costs_rounding.py | 8 +- .../tests/test_stockvaluationlayer.py | 18 +- .../data/stock_picking_batch_demo.xml | 4 - .../models/stock_move_line.py | 6 +- .../models/stock_picking.py | 3 +- .../models/stock_picking_batch.py | 45 +- .../report/report_picking_batch.xml | 4 +- .../tests/test_batch_picking.py | 87 +- .../tests/test_wave_picking.py | 22 +- .../views/stock_picking_batch_views.xml | 12 +- .../wizard/stock_package_destination.py | 2 +- .../static/tests/tours/main_flow.js | 25 +- 201 files changed, 3300 insertions(+), 4607 deletions(-) delete mode 100644 addons/stock/tests/test_wise_operator.py delete mode 100644 addons/stock/wizard/stock_immediate_transfer.py delete mode 100644 addons/stock/wizard/stock_immediate_transfer_views.xml diff --git a/addons/l10n_it_stock_ddt/report/l10n_it_ddt_report.xml b/addons/l10n_it_stock_ddt/report/l10n_it_ddt_report.xml index 37abefa86b5..bc62a64054d 100644 --- a/addons/l10n_it_stock_ddt/report/l10n_it_ddt_report.xml +++ b/addons/l10n_it_stock_ddt/report/l10n_it_ddt_report.xml @@ -110,21 +110,21 @@ - + - + - + - + diff --git a/addons/l10n_it_stock_ddt/tests/test_ddt.py b/addons/l10n_it_stock_ddt/tests/test_ddt.py index 71395904dad..1ed0a66430e 100644 --- a/addons/l10n_it_stock_ddt/tests/test_ddt.py +++ b/addons/l10n_it_stock_ddt/tests/test_ddt.py @@ -72,7 +72,7 @@ class TestDDT(TestSaleCommon): # deliver partially pick = self.so.picking_ids - pick.move_ids.write({'quantity_done': 1}) + pick.move_ids.write({'quantity': 1, 'picked': True}) wiz_act = pick.button_validate() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz.process() @@ -84,14 +84,14 @@ class TestDDT(TestSaleCommon): # deliver partially pickx1 = self.so.picking_ids.filtered(lambda p: p.state != 'done') - pickx1.move_ids.write({'quantity_done': 1}) + pickx1.move_ids.write({'quantity': 1, 'picked': True}) wiz_act = pickx1.button_validate() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz.process() # and again pickx2 = self.so.picking_ids.filtered(lambda p: p.state != 'done') - pickx2.move_ids.write({'quantity_done': 2}) + pickx2.move_ids.write({'quantity': 2, 'picked': True}) wiz_act = pickx2.button_validate() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz.process() @@ -129,7 +129,7 @@ class TestDDT(TestSaleCommon): # deliver partially picking_1 = so.picking_ids - picking_1.move_ids.write({'quantity_done': 1}) + picking_1.move_ids.write({'quantity': 1, 'picked': True}) wiz_act = picking_1.button_validate() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz.process() @@ -139,7 +139,7 @@ class TestDDT(TestSaleCommon): invoice_1.action_post() picking_2 = so.picking_ids.filtered(lambda p: p.state != 'done') - picking_2.move_ids.write({'quantity_done': 2}) + picking_2.move_ids.write({'quantity': 2, 'picked': True}) picking_2.button_validate() invoice_2 = so._create_invoices() diff --git a/addons/l10n_it_stock_ddt/tests/test_edi.py b/addons/l10n_it_stock_ddt/tests/test_edi.py index 49b378c5ffa..a85ae725d1f 100644 --- a/addons/l10n_it_stock_ddt/tests/test_edi.py +++ b/addons/l10n_it_stock_ddt/tests/test_edi.py @@ -143,7 +143,7 @@ class TestItEdiDDT(TestItEdi): def _create_delivery(self, sale_order, qty=1): """ Create a picking of a limited quantity and create a backorder """ pickings = sale_order.picking_ids.filtered(lambda picking: picking.state != 'done') - pickings.move_ids.write({'quantity_done': qty}) + pickings.move_ids.write({'quantity': qty}) wizard_action = pickings.button_validate() context = wizard_action['context'] wizard = Form(self.env['stock.backorder.confirmation'].with_context(context)) diff --git a/addons/mrp/data/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml index 6112555c3a9..e64b1fc29d8 100644 --- a/addons/mrp/data/mrp_demo.xml +++ b/addons/mrp/data/mrp_demo.xml @@ -718,7 +718,7 @@ - + diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index f616ccf03cf..8e8308b4f4b 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -533,8 +533,8 @@ class MrpProduction(models.Model): production.finished_move_line_ids = production.move_finished_ids.mapped('move_line_ids') @api.depends( - 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', - 'workorder_ids.state', 'product_qty', 'qty_producing') + 'move_raw_ids.state', 'move_raw_ids.quantity', 'move_finished_ids.state', + 'workorder_ids.state', 'product_qty', 'qty_producing', 'move_raw_ids.picked') def _compute_state(self): """ Compute the production state. This uses a similar process to stock picking, but has been adapted to support having no moves. This adaption @@ -564,7 +564,7 @@ class MrpProduction(models.Model): production.state = 'progress' elif production.product_uom_id and not float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding): production.state = 'progress' - elif any(not float_is_zero(move.quantity_done, precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding) for move in production.move_raw_ids if move.product_id): + elif any(production.move_raw_ids.mapped('picked')): production.state = 'progress' @api.depends('bom_id', 'product_id', 'product_qty', 'product_uom_id') @@ -633,16 +633,16 @@ class MrpProduction(models.Model): def _compute_unreserve_visible(self): for order in self: already_reserved = order.state not in ('done', 'cancel') and order.mapped('move_raw_ids.move_line_ids') - any_quantity_done = any(m.quantity_done > 0 for m in order.move_raw_ids) + any_quantity_done = any(order.move_raw_ids.mapped('picked')) order.unreserve_visible = not any_quantity_done and already_reserved order.reserve_visible = order.state in ('confirmed', 'progress', 'to_close') and any(move.product_uom_qty and move.state in ['confirmed', 'partially_available'] for move in order.move_raw_ids) - @api.depends('workorder_ids.state', 'move_finished_ids', 'move_finished_ids.quantity_done') + @api.depends('workorder_ids.state', 'move_finished_ids', 'move_finished_ids.quantity') def _get_produced_qty(self): for production in self: done_moves = production.move_finished_ids.filtered(lambda x: x.state != 'cancel' and x.product_id.id == production.product_id.id) - qty_produced = sum(done_moves.mapped('quantity_done')) + qty_produced = sum(done_moves.filtered(lambda m: m.picked).mapped('quantity')) production.qty_produced = qty_produced return True @@ -890,7 +890,7 @@ class MrpProduction(models.Model): if 'lot_producing_id' in vals: finished_move_lines.write({'lot_id': vals.get('lot_producing_id')}) if 'qty_producing' in vals: - finished_move_lines.write({'qty_done': vals.get('qty_producing')}) + finished_move_lines.write({'quantity': vals.get('qty_producing')}) if self._has_workorders() and not production.workorder_ids.operation_id and vals.get('date_start') and not vals.get('date_finished'): new_date_start = fields.Datetime.to_datetime(vals.get('date_start')) if not production.date_finished or new_date_start >= production.date_finished: @@ -1030,7 +1030,7 @@ class MrpProduction(models.Model): target_uom = (move.bom_line_id or move.byproduct_id).product_uom_id or move.product_uom # In order to be able to multiply the move quantity by the ratio, we # have to be sure they both express in the same UoM. - qty = move.quantity_done or move.product_uom_qty + qty = move.quantity or move.product_uom_qty qty = move.product_uom._compute_quantity(qty * ratio, target_uom) return (target_uom, qty) @@ -1195,20 +1195,18 @@ class MrpProduction(models.Model): qty_producing_uom = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') if qty_producing_uom != 1: self.qty_producing = self.product_id.uom_id._compute_quantity(1, self.product_uom_id, rounding_method='HALF-UP') - for move in (self.move_raw_ids | self.move_finished_ids.filtered(lambda m: m.product_id != self.product_id)): - if move._should_bypass_set_qty_producing() or not move.product_uom: + # picked + manual means the user set the quantity manually + if move.manual_consumption and move.picked: + continue + + if move._should_bypass_set_qty_producing(): continue new_qty = float_round((self.qty_producing - self.qty_produced) * move.unit_factor, precision_rounding=move.product_uom.rounding) - if self.use_auto_consume_components_lots and move.has_tracking in ('lot', 'serial'): - if float_compare(move.reserved_availability, 0, precision_rounding=move.product_uom.rounding) <= 0: - continue - else: - new_qty = min(new_qty, move.reserved_availability) - - move.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0 move._set_quantity_done(new_qty) + if (not move.manual_consumption and not move._origin.manual_consumption): + move.picked = True def _update_raw_moves(self, factor): self.ensure_one() @@ -1518,17 +1516,17 @@ class MrpProduction(models.Model): done_qty_by_product = defaultdict(float) for move in order.move_raw_ids: - qty_done = move.product_uom._compute_quantity(move.quantity_done, move.product_id.uom_id) + quantity = move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id) rounding = move.product_id.uom_id.rounding - if not (move.product_id in expected_qty_by_product or float_is_zero(qty_done, precision_rounding=rounding)): - issues.append((order, move.product_id, qty_done, 0.0)) + if not (move.product_id in expected_qty_by_product or float_is_zero(quantity, precision_rounding=rounding)) or not move.picked: + issues.append((order, move.product_id, quantity, 0.0)) continue - done_qty_by_product[move.product_id] += qty_done + done_qty_by_product[move.product_id] += quantity for product, qty_to_consume in expected_qty_by_product.items(): - qty_done = done_qty_by_product.get(product, 0.0) - if float_compare(qty_to_consume, qty_done, precision_rounding=product.uom_id.rounding) != 0: - issues.append((order, product, qty_done, qty_to_consume)) + quantity = done_qty_by_product.get(product, 0.0) + if float_compare(qty_to_consume, quantity, precision_rounding=product.uom_id.rounding) != 0: + issues.append((order, product, quantity, qty_to_consume)) return issues @@ -1632,15 +1630,17 @@ class MrpProduction(models.Model): return True def _post_inventory(self, cancel_backorder=False): - moves_to_do, moves_not_to_do = set(), set() + moves_to_do, moves_not_to_do, moves_to_cancel = set(), set(), set() for move in self.move_raw_ids: if move.state == 'done': moves_not_to_do.add(move.id) + elif not move.picked: + moves_to_cancel.add(move.id) elif move.state != 'cancel': moves_to_do.add(move.id) - if move.product_qty == 0.0 and move.quantity_done > 0: - move.product_uom_qty = move.quantity_done + self.with_context(skip_mo_check=True).env['stock.move'].browse(moves_to_do)._action_done(cancel_backorder=cancel_backorder) + self.with_context(skip_mo_check=True).env['stock.move'].browse(moves_to_cancel)._action_cancel() moves_to_do = self.move_raw_ids.filtered(lambda x: x.state == 'done') - self.env['stock.move'].browse(moves_not_to_do) # Create a dict to avoid calling filtered inside for loops. moves_to_do_by_order = defaultdict(lambda: self.env['stock.move'], [ @@ -1651,8 +1651,7 @@ class MrpProduction(models.Model): finish_moves = order.move_finished_ids.filtered(lambda m: m.product_id == order.product_id and m.state not in ('done', 'cancel')) # the finish move can already be completed by the workorder. for move in finish_moves: - if not move.quantity_done: - move._set_quantity_done(float_round(order.qty_producing - order.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP')) + move.quantity = float_round(order.qty_producing - order.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP') if move.has_tracking != 'none' and order.lot_producing_id: move.move_line_ids.lot_id = order.lot_producing_id # workorder duration need to be set to calculate the price of the product @@ -1660,12 +1659,12 @@ class MrpProduction(models.Model): if workorder.state not in ('done', 'cancel'): workorder.duration_expected = workorder._get_duration_expected() if workorder.duration == 0.0: - workorder.duration = workorder.duration_expected * order.qty_produced/order.product_qty + workorder.duration = workorder.duration_expected * order.qty_producing / order.product_qty workorder.duration_unit = round(workorder.duration / max(workorder.qty_produced, 1), 2) order._cal_price(moves_to_do_by_order[order.id]) moves_to_finish = self.move_finished_ids.filtered(lambda x: x.state not in ('done', 'cancel')) + moves_to_finish.picked = True moves_to_finish = moves_to_finish._action_done(cancel_backorder=cancel_backorder) - self.action_assign() for order in self: consume_move_lines = moves_to_do_by_order[order.id].mapped('move_line_ids') order.move_finished_ids.move_line_ids.consume_line_ids = [(6, 0, consume_move_lines.ids)] @@ -1707,7 +1706,7 @@ class MrpProduction(models.Model): and a new backorder with product_qty=2. :param bool cancel_remaining_qty: whether to cancel remaining quantities or generate an additional backorder, e.g. having product_qty=5 if mrp.production(1,) product_qty was 10. - :param bool set_consumed_qty: whether to set qty_done on move lines to the reserved quantity + :param bool set_consumed_qty: whether to set quantity on move lines to the reserved quantity or the initial demand if no reservation, except for the remaining backorder. :return: mrp.production records in order of [orig_prod_1, backorder_prod_1, backorder_prod_2, orig_prod_2, backorder_prod_2, etc.] @@ -1796,6 +1795,7 @@ class MrpProduction(models.Model): moves.append(move) backorder_moves = self.env['stock.move'].create(new_moves_vals) + move_to_assign = backorder_moves # Split `stock.move.line`s. 2 options for this: # - do_unreserve -> action_assign # - Split the reserved amounts manually @@ -1810,7 +1810,6 @@ class MrpProduction(models.Model): assigned_moves = set() partially_assigned_moves = set() move_lines_to_unlink = set() - for initial_move, backorder_moves in move_to_backorder_moves.items(): # Create `stock.move.line` for consumed but non-reserved components if initial_move.raw_material_production_id and not initial_move.move_line_ids and set_consumed_qty: @@ -1819,7 +1818,7 @@ class MrpProduction(models.Model): for move in list(initial_move + backorder_moves - backorder_move_to_ignore): new_ml_vals = dict( ml_vals, - qty_done=move.product_uom_qty, + quantity=move.product_uom_qty, move_id=move.id ) move_lines_vals.append(new_ml_vals) @@ -1828,23 +1827,27 @@ class MrpProduction(models.Model): ml_by_move = [] product_uom = initial_move.product_id.uom_id for move_line in initial_move.move_line_ids: - available_qty = move_line.product_uom_id._compute_quantity(move_line.reserved_uom_qty, product_uom) + if initial_move.picked: + continue + available_qty = move_line.product_uom_id._compute_quantity(move_line.quantity, product_uom) if float_compare(available_qty, 0, precision_rounding=move_line.product_uom_id.rounding) <= 0: continue ml_by_move.append((available_qty, move_line, move_line.copy_data()[0])) - initial_move.move_line_ids.with_context(bypass_reservation_update=True).write({'reserved_uom_qty': 0}) moves = list(initial_move | backorder_moves) move = moves and moves.pop(0) move_qty_to_reserve = move.product_qty for index, (quantity, move_line, ml_vals) in enumerate(ml_by_move): - taken_qty = min(quantity, move_qty_to_reserve, move_line.product_uom_id._compute_quantity(move_line.qty_done, product_uom)) + taken_qty = min(quantity, move_qty_to_reserve) taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id) if float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding): continue - move_line.with_context(bypass_reservation_update=True).reserved_uom_qty = taken_qty_uom + move_line.write({ + 'quantity': taken_qty_uom, + 'move_id': move.id, + }) move_qty_to_reserve -= taken_qty ml_by_move[index] = (quantity - taken_qty, move_line, ml_vals) @@ -1859,17 +1862,13 @@ class MrpProduction(models.Model): taken_qty = min(move_qty_to_reserve, quantity) taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id) if move == initial_move: - move_line.with_context(bypass_reservation_update=True).reserved_uom_qty += taken_qty_uom - if set_consumed_qty: - move_line.qty_done += taken_qty_uom + move_line.quantity += taken_qty_uom elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding): new_ml_vals = dict( ml_vals, - reserved_uom_qty=taken_qty_uom, + quantity=taken_qty_uom, move_id=move.id ) - if set_consumed_qty: - new_ml_vals['qty_done'] = taken_qty_uom move_lines_vals.append(new_ml_vals) quantity -= taken_qty move_qty_to_reserve -= taken_qty @@ -1891,15 +1890,22 @@ class MrpProduction(models.Model): if move and move_qty_to_reserve != move.product_qty: partially_assigned_moves.add(move.id) - move_lines_to_unlink.update(initial_move.move_line_ids.filtered( - lambda ml: not ml.reserved_uom_qty and not ml.qty_done).ids) + move_lines_to_unlink.update(initial_move.move_line_ids.filtered(lambda ml: not ml.quantity).ids) + # reserve new backorder moves depending on the picking type self.env['stock.move'].browse(assigned_moves).write({'state': 'assigned'}) self.env['stock.move'].browse(partially_assigned_moves).write({'state': 'partially_available'}) + move_to_assign = move_to_assign.filtered( + lambda move: move.state in ('confirmed', 'partially_available') + and (move._should_bypass_reservation() + or move.picking_type_id.reservation_method == 'at_confirm' + or (move.reservation_date and move.reservation_date <= fields.Date.today()))) + move_to_assign._action_assign() + # Avoid triggering a useless _recompute_state self.env['stock.move.line'].browse(move_lines_to_unlink).write({'move_id': False}) self.env['stock.move.line'].browse(move_lines_to_unlink).unlink() - self.env['stock.move.line'].with_context(bypass_reservation_update=True).create(move_lines_vals) + self.env['stock.move.line'].create(move_lines_vals) workorders_to_cancel = self.env['mrp.workorder'] for production in self: @@ -1993,7 +1999,7 @@ class MrpProduction(models.Model): } elif self.user_has_groups('mrp.group_mrp_reception_report'): mos_to_show = self.filtered(lambda mo: mo.picking_type_id.auto_show_reception_report) - lines = mos_to_show.move_finished_ids.filtered(lambda m: m.product_id.type == 'product' and m.state != 'cancel' and m.quantity_done and not m.move_dest_ids) + lines = mos_to_show.move_finished_ids.filtered(lambda m: m.product_id.type == 'product' and m.state != 'cancel' and m.picked and not m.move_dest_ids) if lines: if any(mo.show_allocation for mo in mos_to_show): another_action = mos_to_show.action_view_reception_report() @@ -2187,7 +2193,7 @@ class MrpProduction(models.Model): if self.product_id.tracking != 'serial': return if self.state == 'confirmed' and self.reservation_state != 'assigned': - missing_components = {move.product_id for move in self.move_raw_ids if float_compare(move.reserved_availability, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0} + missing_components = {move.product_id for move in self.move_raw_ids if float_compare(move.quantity, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0} message = _("Make sure enough quantities of these components are reserved to do the production:\n") message += "\n".join(component.name for component in missing_components) raise UserError(message) @@ -2474,17 +2480,17 @@ class MrpProduction(models.Model): if move.has_tracking != 'serial' or move.product_id == self.product_id: continue for move_line in move.move_line_ids: - if float_is_zero(move_line.qty_done, precision_rounding=move_line.product_uom_id.rounding): + if float_is_zero(move_line.quantity, precision_rounding=move_line.product_uom_id.rounding): continue if self._is_finished_sn_already_produced(move_line.lot_id, excluded_sml=move_line): raise UserError(_('The serial number %(number)s used for byproduct %(product_name)s has already been produced', number=move_line.lot_id.name, product_name=move_line.product_id.name)) for move in self.move_raw_ids: - if move.has_tracking != 'serial': + if move.has_tracking != 'serial' or not move.picked: continue for move_line in move.move_line_ids: - if float_is_zero(move_line.qty_done, precision_rounding=move_line.product_uom_id.rounding): + if float_is_zero(move_line.quantity, precision_rounding=move_line.product_uom_id.rounding): continue message = _('The serial number %(number)s used for component %(component)s has already been consumed', number=move_line.lot_id.name, @@ -2494,7 +2500,7 @@ class MrpProduction(models.Model): # Check presence of same sn in previous productions duplicates = self.env['stock.move.line'].search_count([ ('lot_id', '=', move_line.lot_id.id), - ('qty_done', '=', 1), + ('quantity', '=', 1), ('state', '=', 'done'), ('location_dest_id.usage', '=', 'production'), ('production_id', '!=', False), @@ -2517,7 +2523,7 @@ class MrpProduction(models.Model): if not ((duplicates_returned or removed) and duplicates - duplicates_returned - removed + unremoved == 0): raise UserError(message) # Check presence of same sn in current production - duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == move_line.lot_id) - move_line + duplicates = co_prod_move_lines.filtered(lambda ml: ml.quantity and ml.lot_id == move_line.lot_id) - move_line if duplicates: raise UserError(message) @@ -2527,7 +2533,7 @@ class MrpProduction(models.Model): excluded_sml = excluded_sml or self.env['stock.move.line'] domain = [ ('lot_id', '=', lot.id), - ('qty_done', '=', 1), + ('quantity', '=', 1), ('state', '=', 'done') ] co_prod_move_lines = self.move_finished_ids.move_line_ids - excluded_sml @@ -2554,7 +2560,7 @@ class MrpProduction(models.Model): if not ((duplicates_unbuild or removed) and duplicates - duplicates_unbuild - removed == 0): return True # Check presence of same sn in current production - duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == lot) + duplicates = co_prod_move_lines.filtered(lambda ml: ml.quantity and ml.lot_id == lot) return bool(duplicates) def _pre_action_split_merge_hook(self, merge=False, split=False): @@ -2612,10 +2618,10 @@ class MrpProduction(models.Model): continue rounding = move.product_uom.rounding if move.manual_consumption: - if move.has_tracking in ('serial', 'lot') and float_is_zero(move.quantity_done, precision_rounding=rounding): + if move.has_tracking in ('serial', 'lot') and (any(not line.lot_id for line in move.move_line_ids if line.quantity) or not move.picked): missing_lot_id_products += "\n - %s" % move.product_id.display_name if missing_lot_id_products: - error_msg = _('You need to supply Lot/Serial Number for products:') + missing_lot_id_products + error_msg = _("You need to supply Lot/Serial Number for products and 'picked' them:") + missing_lot_id_products raise UserError(error_msg) def _get_autoprint_done_report_actions(self): diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py index 8f816f351a7..e795176be51 100644 --- a/addons/mrp/models/mrp_unbuild.py +++ b/addons/mrp/models/mrp_unbuild.py @@ -1,12 +1,12 @@ -# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models, _ +from collections import defaultdict + +from odoo import _, api, fields, models from odoo.exceptions import UserError from odoo.tools import float_compare, float_round -from odoo.osv import expression +from odoo.tools.misc import clean_context -from collections import defaultdict class MrpUnbuild(models.Model): _name = "mrp.unbuild" @@ -131,17 +131,19 @@ class MrpUnbuild(models.Model): def action_unbuild(self): self.ensure_one() self._check_company() + # remove the default_* keys that was only needed in the unbuild wizard + self.env.context = dict(clean_context(self.env.context)) if self.product_id.tracking != 'none' and not self.lot_id.id: raise UserError(_('You should provide a lot number for the final product.')) - if self.mo_id: - if self.mo_id.state != 'done': - raise UserError(_('You cannot unbuild a undone manufacturing order.')) + if self.mo_id and self.mo_id.state != 'done': + raise UserError(_('You cannot unbuild a undone manufacturing order.')) consume_moves = self._generate_consume_moves() consume_moves._action_confirm() produce_moves = self._generate_produce_moves() - produce_moves.with_context(default_lot_id=False)._action_confirm() + produce_moves._action_confirm() + produce_moves.quantity = 0 finished_moves = consume_moves.filtered(lambda m: m.product_id == self.product_id) consume_moves -= finished_moves @@ -153,18 +155,15 @@ class MrpUnbuild(models.Model): raise UserError(_('Some of your byproducts are tracked, you have to specify a manufacturing order in order to retrieve the correct byproducts.')) for finished_move in finished_moves: - if finished_move.has_tracking != 'none': - self.env['stock.move.line'].create({ - 'move_id': finished_move.id, - 'lot_id': self.lot_id.id, - 'qty_done': finished_move.product_uom_qty, - 'product_id': finished_move.product_id.id, - 'product_uom_id': finished_move.product_uom.id, - 'location_id': finished_move.location_id.id, - 'location_dest_id': finished_move.location_dest_id.id, - }) - else: - finished_move.quantity_done = finished_move.product_uom_qty + self.env['stock.move.line'].create({ + 'move_id': finished_move.id, + 'lot_id': self.lot_id.id, + 'quantity': finished_move.product_uom_qty, + 'product_id': finished_move.product_id.id, + 'product_uom_id': finished_move.product_uom.id, + 'location_id': finished_move.location_id.id, + 'location_dest_id': finished_move.location_dest_id.id, + }) # TODO: Will fail if user do more than one unbuild with lot on the same MO. Need to check what other unbuild has aready took qty_already_used = defaultdict(float) @@ -178,12 +177,12 @@ class MrpUnbuild(models.Model): moves_lines = moves_lines.filtered(lambda ml: self.lot_id in ml.produce_line_ids.lot_id) # FIXME sle: double check with arm for move_line in moves_lines: # Iterate over all move_lines until we unbuilded the correct quantity. - taken_quantity = min(needed_quantity, move_line.qty_done - qty_already_used[move_line]) + taken_quantity = min(needed_quantity, move_line.quantity - qty_already_used[move_line]) if taken_quantity: self.env['stock.move.line'].create({ 'move_id': move.id, 'lot_id': move_line.lot_id.id, - 'qty_done': taken_quantity, + 'quantity': taken_quantity, 'product_id': move.product_id.id, 'product_uom_id': move_line.product_uom_id.id, 'location_id': move.location_id.id, @@ -192,12 +191,13 @@ class MrpUnbuild(models.Model): needed_quantity -= taken_quantity qty_already_used[move_line] += taken_quantity else: - move.quantity_done = float_round(move.product_uom_qty, precision_rounding=move.product_uom.rounding) + move.quantity = float_round(move.product_uom_qty, precision_rounding=move.product_uom.rounding) + (finished_moves | consume_moves | produce_moves).picked = True finished_moves._action_done() consume_moves._action_done() produce_moves._action_done() - produced_move_line_ids = produce_moves.mapped('move_line_ids').filtered(lambda ml: ml.qty_done > 0) + produced_move_line_ids = produce_moves.mapped('move_line_ids').filtered(lambda ml: ml.quantity > 0) consume_moves.mapped('move_line_ids').write({'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]}) if self.mo_id: unbuild_msg = _("%(qty)s %(measure)s unbuilt in %(order)s", @@ -300,7 +300,7 @@ class MrpUnbuild(models.Model): 'default_location_id': self.location_id.id, 'default_unbuild_id': self.id, 'default_quantity': unbuild_qty, - 'default_product_uom_name': self.product_id.uom_name + 'default_product_uom_name': self.product_id.uom_name, }, - 'target': 'new' + 'target': 'new', } diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 34d9137bcdc..f159d6879af 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -830,8 +830,8 @@ class MrpWorkorder(models.Model): if move_line: if self.product_id.tracking == 'serial': raise UserError(_('You cannot produce the same serial number twice.')) - move_line.reserved_uom_qty += self.qty_producing - move_line.qty_done += self.qty_producing + move_line.picked = True + move_line.quantity += self.qty_producing else: quantity = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') putaway_location = production_move.location_dest_id._get_putaway_strategy(self.product_id, quantity) @@ -839,17 +839,14 @@ class MrpWorkorder(models.Model): 'move_id': production_move.id, 'product_id': production_move.product_id.id, 'lot_id': self.finished_lot_id.id, - 'reserved_uom_qty': self.qty_producing, 'product_uom_id': self.product_uom_id.id, - 'qty_done': self.qty_producing, + 'quantity': self.qty_producing, 'location_id': production_move.location_id.id, 'location_dest_id': putaway_location.id, }) else: rounding = production_move.product_uom.rounding - production_move._set_quantity_done( - float_round(self.qty_producing, precision_rounding=rounding) - ) + production_move.quantity = float_round(self.qty_producing, precision_rounding=rounding) def _should_start_timer(self): return True diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index ea7c96d19e2..682d83b7fdb 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -314,7 +314,7 @@ class ProductProduct(models.Model): def _count_returned_sn_products(self, sn_lot): res = self.env['stock.move.line'].search_count([ ('lot_id', '=', sn_lot.id), - ('qty_done', '=', 1), + ('quantity', '=', 1), ('state', '=', 'done'), ('production_id', '=', False), ('location_id.usage', '=', 'production'), diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 9ce77a986dc..c327ffa03ff 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -69,19 +69,10 @@ class StockMoveLine(models.Model): lines |= raw_moves_lines.filtered(lambda ml: ml.product_id == self.product_id and (ml.lot_id or ml.lot_name)) return lines - def _reservation_is_updatable(self, quantity, reserved_quant): - self.ensure_one() - if self.produce_line_ids.lot_id: - ml_remaining_qty = self.qty_done - self.reserved_uom_qty - ml_remaining_qty = self.product_uom_id._compute_quantity(ml_remaining_qty, self.product_id.uom_id, rounding_method="HALF-UP") - if float_compare(ml_remaining_qty, quantity, precision_rounding=self.product_id.uom_id.rounding) < 0: - return False - return super(StockMoveLine, self)._reservation_is_updatable(quantity, reserved_quant) - def write(self, vals): for move_line in self: production = move_line.move_id.production_id or move_line.move_id.raw_material_production_id - if production and move_line.state == 'done' and any(field in vals for field in ('lot_id', 'location_id', 'qty_done')): + if production and move_line.state == 'done' and any(field in vals for field in ('lot_id', 'location_id', 'quantity')): move_line._log_message(production, move_line, 'mrp.track_production_move_template', vals) return super(StockMoveLine, self).write(vals) @@ -229,7 +220,8 @@ class StockMove(models.Model): def _onchange_product_uom_qty(self): if self.raw_material_production_id and self.has_tracking == 'none': mo = self.raw_material_production_id - self._update_quantity_done(mo) + new_qty = float_round((mo.qty_producing - mo.qty_produced) * self.unit_factor, precision_rounding=self.product_uom.rounding) + self.quantity = new_qty @api.model def default_get(self, fields_list): @@ -362,12 +354,12 @@ class StockMove(models.Model): moves_ids_to_return.add(move.id) continue if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): - factor = move.product_uom._compute_quantity(move.quantity_done, bom.product_uom_id) / bom.product_qty + factor = move.product_uom._compute_quantity(move.quantity, bom.product_uom_id) / bom.product_qty else: factor = move.product_uom._compute_quantity(move.product_uom_qty, bom.product_uom_id) / bom.product_qty boms, lines = bom.sudo().explode(move.product_id, factor, picking_type=bom.picking_type_id) for bom_line, line_data in lines: - if move.picking_id.immediate_transfer or float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding) or self.env.context.get('is_scrap'): + if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding) or self.env.context.get('is_scrap'): phantom_moves_vals_list += move._generate_move_phantom(bom_line, 0, line_data['qty']) else: phantom_moves_vals_list += move._generate_move_phantom(bom_line, line_data['qty'], 0) @@ -375,7 +367,7 @@ class StockMove(models.Model): moves_ids_to_unlink.add(move.id) move_to_unlink = self.env['stock.move'].browse(moves_ids_to_unlink).sudo() - move_to_unlink.quantity_done = 0 + move_to_unlink.quantity = 0 move_to_unlink._action_cancel() move_to_unlink.unlink() if phantom_moves_vals_list: @@ -423,9 +415,9 @@ class StockMove(models.Model): 'product_id': bom_line.product_id.id, 'product_uom': bom_line.product_uom_id.id, 'product_uom_qty': product_qty, - 'quantity_done': quantity_done, - 'state': 'draft', # will be confirmed below + 'quantity': quantity_done, 'name': self.name, + 'picked': self.picked, 'bom_line_id': bom_line.id, } @@ -478,8 +470,6 @@ class StockMove(models.Model): # Do not update extra product quantities if float_is_zero(self.product_uom_qty, precision_rounding=self.product_uom.rounding): return True - if (not self.raw_material_production_id.use_auto_consume_components_lots and self.has_tracking != 'none') or self.manual_consumption or self._origin.manual_consumption: - return True return False def _key_assign_picking(self): @@ -542,15 +532,6 @@ class StockMove(models.Model): else: return 0.0 - def _update_quantity_done(self, mo): - self.ensure_one() - new_qty = float_round((mo.qty_producing - mo.qty_produced) * self.unit_factor, precision_rounding=self.product_uom.rounding) - if not self.is_quantity_done_editable: - 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(new_qty) - else: - self.quantity_done = new_qty - def _update_candidate_moves_list(self, candidate_moves_set): super()._update_candidate_moves_list(candidate_moves_set) for production in self.mapped('raw_material_production_id'): @@ -586,3 +567,11 @@ class StockMove(models.Model): def _determine_is_manual_consumption(self, product, production, bom_line): return (product.product_tmpl_id.tracking != 'none' and not production.use_auto_consume_components_lots) or \ (product.product_tmpl_id.tracking == 'none' and bom_line and bom_line.manual_consumption) + + def _get_relevant_state_among_moves(self): + res = super()._get_relevant_state_among_moves() + if res == 'partially_available'\ + and self.raw_material_production_id\ + and all(float_compare(move.quantity, move.should_consume_qty, precision_rounding=move.product_uom.rounding) == 0 for move in self): + res = 'assigned' + return res diff --git a/addons/mrp/models/stock_scrap.py b/addons/mrp/models/stock_scrap.py index 5484e9a2ca0..ec5da7bcbbe 100644 --- a/addons/mrp/models/stock_scrap.py +++ b/addons/mrp/models/stock_scrap.py @@ -3,6 +3,7 @@ from odoo import _, api, fields, models + class StockScrap(models.Model): _inherit = 'stock.scrap' @@ -55,7 +56,7 @@ class StockScrap(models.Model): else: return super()._onchange_serial_number() - @api.depends('move_ids', 'move_ids.move_line_ids.qty_done', 'product_id') + @api.depends('move_ids', 'move_ids.move_line_ids.quantity', 'product_id') def _compute_scrap_qty(self): self.scrap_qty = 1 for scrap in self: diff --git a/addons/mrp/report/mrp_production_templates.xml b/addons/mrp/report/mrp_production_templates.xml index 669afe0e958..656a247ff1c 100644 --- a/addons/mrp/report/mrp_production_templates.xml +++ b/addons/mrp/report/mrp_production_templates.xml @@ -115,7 +115,7 @@
- +
@@ -137,12 +137,12 @@ - + - + @@ -173,7 +173,7 @@
Quantity: 1.0 - +
diff --git a/addons/mrp/report/mrp_report_mo_overview.py b/addons/mrp/report/mrp_report_mo_overview.py index db09b65cb1a..36495820ebd 100644 --- a/addons/mrp/report/mrp_report_mo_overview.py +++ b/addons/mrp/report/mrp_report_mo_overview.py @@ -199,8 +199,8 @@ class ReportMoOverview(models.AbstractModel): for move in record.move_raw_ids: if move.product_id.detailed_type != 'product': continue - components_qty_to_produce[move.product_id] += move.product_uom._compute_quantity(move.product_uom_qty, move.product_id.uom_id) - components_qty_reserved[move.product_id] += move.product_uom._compute_quantity(move.reserved_availability, move.product_id.uom_id) + components_qty_to_produce[move.product_id] += move.product_qty + components_qty_reserved[move.product_id] += move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id) producible_qty = record.product_qty for product_id, comp_qty_to_produce in components_qty_to_produce.items(): if float_is_zero(comp_qty_to_produce, precision_rounding=product_id.uom_id.rounding): @@ -419,7 +419,7 @@ class ReportMoOverview(models.AbstractModel): def _format_component_move(self, production, move_raw, replenishments, replenish_data, level, index): currency = (production.company_id or self.env.company).currency_id product = move_raw.product_id - quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity_done + quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity replenish_mo_cost, dummy_real_cost = self._compute_cost_sums(replenishments) replenish_quantity = sum(rep.get('summary', {}).get('quantity', 0.0) for rep in replenishments) missing_quantity = quantity - replenish_quantity @@ -495,7 +495,7 @@ class ReportMoOverview(models.AbstractModel): def _get_replenishment_lines(self, production, move_raw, replenish_data, level, current_index): product = move_raw.product_id - quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity_done + quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity currency = (production.company_id or self.env.company).currency_id forecast = replenish_data['products'][product.id].get('forecast', []) current_lines = filter(lambda line: line.get('document_in', False) and line.get('document_out', False) @@ -848,7 +848,7 @@ class ReportMoOverview(models.AbstractModel): if move.state not in ('partially_available', 'assigned'): continue # count reserved stock in move_raw's uom - reserved = move.product_uom._compute_quantity(move.reserved_availability, move_raw.product_uom) + reserved = move.product_uom._compute_quantity(move.quantity, move_raw.product_uom) # check if the move reserved qty was counted before (happens if multiple outs share pick/pack) reserved = min(reserved - move.product_uom._compute_quantity(replenish_data['qty_already_reserved'][move], move_raw.product_uom), move_raw.product_uom_qty) total_reserved += reserved diff --git a/addons/mrp/report/mrp_zebra_production_templates.xml b/addons/mrp/report/mrp_zebra_production_templates.xml index 0190a8be527..fb6fb2e6e40 100644 --- a/addons/mrp/report/mrp_zebra_production_templates.xml +++ b/addons/mrp/report/mrp_zebra_production_templates.xml @@ -7,7 +7,7 @@ - + diff --git a/addons/mrp/static/src/widgets/mrp_production_components_x2many.js b/addons/mrp/static/src/widgets/mrp_production_components_x2many.js index d82200e8b07..bd60946c5eb 100644 --- a/addons/mrp/static/src/widgets/mrp_production_components_x2many.js +++ b/addons/mrp/static/src/widgets/mrp_production_components_x2many.js @@ -7,7 +7,7 @@ import { StockMoveX2ManyField, MovesListRenderer } from "@stock/views/picking_fo export class MrpProductionComponentsListRenderer extends MovesListRenderer { getCellClass(column, record) { let classNames = super.getCellClass(...arguments); - if (column.name == "quantity_done" && !record.data.manual_consumption) { + if (column.name == "quantity" && !record.data.manual_consumption) { classNames += ' o_non_manual_consumption'; } return classNames; diff --git a/addons/mrp/static/tests/tours/mrp_manual_consumption.js b/addons/mrp/static/tests/tours/mrp_manual_consumption.js index 635cd1065ea..6021040c20e 100644 --- a/addons/mrp/static/tests/tours/mrp_manual_consumption.js +++ b/addons/mrp/static/tests/tours/mrp_manual_consumption.js @@ -7,15 +7,15 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', { test: true, steps: () => [ { - trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("5.00")', + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("5.00")', run: () => {}, }, { - trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last', + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last', run: 'click', }, { - trigger: 'div[name="quantity_done"] input', + trigger: 'div[name="quantity"] input', run: 'text 6.0' }, { @@ -31,7 +31,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', { trigger: ".o_pager_value:first()", }, { - trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("6.00")', + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("6.00")', run: () => {}, }, { @@ -56,7 +56,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', { trigger: ".o_pager_value:first()", }, { - trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("2.00")', + trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("2.00")', run: () => {}, }, { diff --git a/addons/mrp/tests/test_backorder.py b/addons/mrp/tests/test_backorder.py index 80813c7a5ed..c5e06be0813 100644 --- a/addons/mrp/tests/test_backorder.py +++ b/addons/mrp/tests/test_backorder.py @@ -74,7 +74,7 @@ class TestMrpProductionBackorder(TestMrpCommon): self.assertEqual(mo_backorder.product_id.id, production.product_id.id) self.assertEqual(mo_backorder.product_qty, 3) self.assertEqual(sum(mo_backorder.move_raw_ids.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_uom_qty")), 9) - self.assertEqual(mo_backorder.reserve_visible, False) # the reservation of the first MO should've been moved here + self.assertEqual(mo_backorder.reserve_visible, False) # the reservation is retrigger depending on the picking type def test_no_tracking_pbm_1(self): """Create a MO for 4 product. Produce 1. The backorder button should @@ -195,18 +195,18 @@ class TestMrpProductionBackorder(TestMrpCommon): self.env['stock.quant']._update_available_quantity(p1, self.stock_location, nb_product_todo*4, lot_id=lot_1) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, nb_product_todo, lot_id=lot_2) - production.action_assign() active_production = production for i in range(nb_product_todo): + active_production.action_assign() details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 4 + ml.quantity = 4 ml.lot_id = lot_1 details_operation_form.save() details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 ml.lot_id = lot_2 details_operation_form.save() @@ -215,6 +215,7 @@ class TestMrpProductionBackorder(TestMrpCommon): production_form.lot_producing_id = lot_final active_production = production_form.save() + active_production.move_raw_ids.picked = True active_production.button_mark_done() if i + 1 != nb_product_todo: # If last MO, don't make a backorder action = active_production.button_mark_done() @@ -249,7 +250,7 @@ class TestMrpProductionBackorder(TestMrpCommon): details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 4 * 3 + ml.quantity = 4 * 3 details_operation_form.save() # Consume 1 Product from lot1 and 2 from lot 2 @@ -257,10 +258,10 @@ class TestMrpProductionBackorder(TestMrpCommon): self.assertEqual(len(p2_smls), 2, 'One for each lot') details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 ml.lot_id = lot1 with details_operation_form.move_line_ids.edit(1) as ml: - ml.qty_done = 2 + ml.quantity = 2 ml.lot_id = lot2 details_operation_form.save() @@ -272,7 +273,7 @@ class TestMrpProductionBackorder(TestMrpCommon): p2_bo_mls = production.procurement_group_id.mrp_production_ids[-1].move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids self.assertEqual(len(p2_bo_mls), 1) self.assertEqual(p2_bo_mls.lot_id, lot1) - self.assertEqual(p2_bo_mls.reserved_qty, 2) + self.assertEqual(p2_bo_mls.quantity, 2) def test_uom_backorder(self): """ @@ -352,22 +353,20 @@ class TestMrpProductionBackorder(TestMrpCommon): production.action_assign() active_production = production for i in range(nb_product_todo): - - details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 - ml.lot_id = serials_p1[i] - details_operation_form.save() - details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 - ml.lot_id = serials_p2[i] - details_operation_form.save() - production_form = Form(active_production) production_form.qty_producing = 1 production_form.lot_producing_id = serials_final[i] active_production = production_form.save() + details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations')) + with details_operation_form.move_line_ids.edit(0) as ml: + ml.quantity = 1 + ml.lot_id = serials_p1[i] + details_operation_form.save() + details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) + with details_operation_form.move_line_ids.edit(0) as ml: + ml.quantity = 1 + ml.lot_id = serials_p2[i] + details_operation_form.save() active_production.button_mark_done() if i + 1 != nb_product_todo: # If last MO, don't make a backorder action = active_production.button_mark_done() @@ -556,6 +555,7 @@ class TestMrpProductionBackorder(TestMrpCommon): self.assertEqual(production.reserve_visible, False) backorder = produce_one(production) self.assertEqual(backorder.state, 'confirmed') + # The backorder is re reserved depending on the picking type self.assertEqual(backorder.reserve_visible, False) diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index e452279bc7b..db7032f4741 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -1264,7 +1264,7 @@ class TestBoM(TestMrpCommon): operation.duration_expected = 480 with mo_form.move_byproduct_ids.new() as by_product_line: by_product_line.product_id = by_product - by_product_line.quantity_done = 6 + by_product_line.quantity = 6 mo_3 = mo_form.save() bom_from_mo_3 = generate_bom_from_mo(mo_3) # Checks the MO's data. diff --git a/addons/mrp/tests/test_byproduct.py b/addons/mrp/tests/test_byproduct.py index 38856ca0ac1..69e23e2e433 100644 --- a/addons/mrp/tests/test_byproduct.py +++ b/addons/mrp/tests/test_byproduct.py @@ -105,7 +105,7 @@ class TestMrpByProduct(common.TransactionCase): mnf_product_a.action_confirm() self.assertEqual(mnf_product_a.state, "confirmed") mnf_product_a.move_raw_ids._action_assign() - mnf_product_a.move_raw_ids.quantity_done = mnf_product_a.move_raw_ids.product_uom_qty + mnf_product_a.move_raw_ids.picked = True mnf_product_a.move_raw_ids._action_done() self.assertEqual(mnf_product_a.state, "progress") mnf_product_a.qty_producing = 2 @@ -357,8 +357,6 @@ class TestMrpByProduct(common.TransactionCase): 'production_id': mo.id, 'location_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_output'), - 'product_uom_qty': 0, - 'quantity_done': 0 }) byproduct_2 = self.env['stock.move'].create({ 'name': 'By Product 2', @@ -367,8 +365,6 @@ class TestMrpByProduct(common.TransactionCase): 'production_id': mo.id, 'location_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_output'), - 'product_uom_qty': 0, - 'quantity_done': 0 }) # Update byproduct has cost share > 100% @@ -404,6 +400,5 @@ class TestMrpByProduct(common.TransactionCase): mo_form.qty_producing = 1 mo = mo_form.save() self.assertEqual(mo.state, 'to_close') - mo.move_byproduct_ids[0].quantity_done = 1 mo.button_mark_done() self.assertEqual(mo.state, 'done') diff --git a/addons/mrp/tests/test_consume_component.py b/addons/mrp/tests/test_consume_component.py index 31c901f7e11..74f968a92cd 100644 --- a/addons/mrp/tests/test_consume_component.py +++ b/addons/mrp/tests/test_consume_component.py @@ -1,7 +1,7 @@ import copy from odoo.exceptions import UserError -from odoo.tests import common, tagged +from odoo.tests import common, tagged, Form class TestConsumeComponentCommon(common.TransactionCase): @@ -261,7 +261,7 @@ class TestConsumeComponent(TestConsumeComponentCommon): # Quantities are fully reserved (stock.move state is available) mo_all.action_assign() for mov in mo_all.move_raw_ids: - self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.") + self.assertEqual(mov.product_qty, mov.quantity, "Reserved quantity shall be equal to To Consume quantity.") # Test for Serial Product self.executeConsumptionTriggers(mo_serial) @@ -269,9 +269,9 @@ class TestConsumeComponent(TestConsumeComponentCommon): self.executeConsumptionTriggers(mo_lot) for mov in mo_all.move_raw_ids: if mov.has_tracking == 'none' or mov.raw_material_production_id.state == 'done': - self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.") + self.assertTrue(mov.picked, "non tracked components should be picked") else: - self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to 0.") + self.assertFalse(mov.picked, "tracked components should be picked") def test_option_enabled_and_qty_available(self): """Option enabled, qty available @@ -296,13 +296,13 @@ class TestConsumeComponent(TestConsumeComponentCommon): # Quantities are fully reserved (stock.move state is available) mo_all.action_assign() for mov in mo_all.move_raw_ids: - self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.") + self.assertEqual(mov.product_qty, mov.quantity, "Reserved quantity shall be equal to To Consume quantity.") self.executeConsumptionTriggers(mo_serial) self.executeConsumptionTriggers(mo_none) self.executeConsumptionTriggers(mo_lot) for mov in mo_all.move_raw_ids: - self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.") + self.assertTrue(mov.picked, "All components should be picked") def test_option_enabled_and_qty_not_available(self): """Option enabled, qty not available @@ -320,7 +320,7 @@ class TestConsumeComponent(TestConsumeComponentCommon): # Quantities are not reserved at all (stock.move state is confirmed) mo_all.action_assign() for mov in mo_all.move_raw_ids: - self.assertEqual(0, mov.reserved_availability, "Reserved quantity shall be equal to 0.") + self.assertEqual(0, mov.quantity, "Reserved quantity shall be equal to 0.") self.executeConsumptionTriggers(mo_serial) self.executeConsumptionTriggers(mo_none) @@ -328,9 +328,9 @@ class TestConsumeComponent(TestConsumeComponentCommon): for mov in mo_all.move_raw_ids: if mov.has_tracking == 'none': - self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.") + self.assertTrue(mov.picked, "components should be picked even without no quantity reserved") else: - self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.") + self.assertEqual(mov.product_qty, mov.quantity, "Done quantity shall be equal to To Consume quantity.") def test_option_enabled_and_qty_partially_available(self): """Option enabled, qty partially available @@ -360,23 +360,24 @@ class TestConsumeComponent(TestConsumeComponentCommon): mo.action_assign() for mov in mo.move_raw_ids: if mov.has_tracking == "none": - self.assertEqual(raw_none_qty, mov.reserved_availability, "Reserved quantity shall be equal to " + str(raw_none_qty)+ ".") + self.assertEqual(raw_none_qty, mov.quantity, "Reserved quantity shall be equal to " + str(raw_none_qty) + ".") else: - self.assertEqual(raw_tracked_qty, mov.reserved_availability, "Reserved quantity shall be equal to " + str(raw_tracked_qty)+ ".") + self.assertEqual(raw_tracked_qty, mov.quantity, "Reserved quantity shall be equal to " + str(raw_tracked_qty) + ".") if serialTrigger is None: self.executeConsumptionTriggers(mo) elif serialTrigger == 1: - mo.qty_producing = mo.product_qty - mo._onchange_producing() + mo_form = Form(mo) + mo_form.qty_producing = mo_form.product_qty + mo = mo_form.save() elif serialTrigger == 2: mo.action_generate_serial() for mov in mo.move_raw_ids: if mov.has_tracking == "none": - self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.") + self.assertTrue(mov.picked, "non tracked components should be picked") else: - self.assertEqual(raw_tracked_qty, mov.quantity_done, "Done quantity shall be equal to " + str(raw_tracked_qty)+ ".") + self.assertEqual(mov.product_qty, mov.quantity, "Done quantity shall be equal to To Consume quantity.") mo.action_cancel() testUnit(self.mo_none_tmpl) diff --git a/addons/mrp/tests/test_manual_consumption.py b/addons/mrp/tests/test_manual_consumption.py index 3a8496e6e13..5d5188b00d4 100644 --- a/addons/mrp/tests/test_manual_consumption.py +++ b/addons/mrp/tests/test_manual_consumption.py @@ -9,9 +9,9 @@ from odoo.tests import tagged, Form, HttpCase class TestTourManualConsumption(HttpCase): def test_mrp_manual_consumption(self): """Test manual consumption mechanism. Test when manual consumption is - True, quantity_done won't be updated automatically. Bom line with tracked + True, quantity won't be updated automatically. Bom line with tracked products or operations should be set to manual consumption automatically. - Also test that when manually change quantity_done, manual consumption + Also test that when manually change quantity, manual consumption will be set to True. Also test when create backorder, the manual consumption should be set according to the bom. """ @@ -58,22 +58,25 @@ class TestTourManualConsumption(HttpCase): mo = mo_form.save() move_nt, move_sn, move_lot = mo.move_raw_ids self.assertEqual(move_nt.manual_consumption, False) - self.assertEqual(move_nt.quantity_done, 5) + self.assertEqual(move_nt.quantity, 5) + self.assertTrue(move_nt.picked) self.assertEqual(move_sn.manual_consumption, True) - self.assertEqual(move_sn.quantity_done, 0) + self.assertEqual(move_sn.quantity, 5) + self.assertFalse(move_sn.picked) self.assertEqual(move_lot.manual_consumption, True) - self.assertEqual(move_lot.quantity_done, 0) + self.assertEqual(move_lot.quantity, 5) + self.assertFalse(move_lot.picked) action_id = self.env.ref('mrp.menu_mrp_production_action').action url = "/web#model=mrp.production&view_type=form&action=%s&id=%s" % (str(action_id.id), str(mo.id)) - self.start_tour(url, "test_mrp_manual_consumption", login="admin", timeout=200) + self.start_tour(url, "test_mrp_manual_consumption", login="admin", timeout=100) self.assertEqual(move_nt.manual_consumption, True) - self.assertEqual(move_nt.quantity_done, 6.0) + self.assertEqual(move_nt.quantity, 6.0) self.assertEqual(move_sn.manual_consumption, True) - self.assertEqual(move_sn.quantity_done, 0) + self.assertEqual(move_sn.quantity, 0) self.assertEqual(move_lot.manual_consumption, True) - self.assertEqual(move_lot.quantity_done, 0) + self.assertEqual(move_lot.quantity, 0) backorder = mo.procurement_group_id.mrp_production_ids - mo move_nt = backorder.move_raw_ids.filtered(lambda m: m.product_id == product_nt) @@ -111,12 +114,13 @@ class TestManualConsumption(TestMrpCommon): mo_form = Form(mo) mo_form.qty_producing = 1 mo_form.save() - self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity_done")), 0) - self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity_done")), 1) + self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity")), 4) + self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity")), 1) + self.assertEqual(sorted(mo.move_raw_ids.mapped('picked')), sorted([False, True])) details_operation_form = Form(mo.move_raw_ids.filtered(lambda m: m.product_id == c1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 4 + ml.quantity = 4 ml.lot_id = lot details_operation_form.save() @@ -152,8 +156,8 @@ class TestManualConsumption(TestMrpCommon): mo_form = Form(mo) mo_form.qty_producing = 1 mo_form.save() - self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity_done")), 4) - self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity_done")), 1) + self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity")), 4) + self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity")), 1) action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py index fda22d07180..0a6fdf165ec 100644 --- a/addons/mrp/tests/test_multicompany.py +++ b/addons/mrp/tests/test_multicompany.py @@ -175,8 +175,9 @@ class TestMrpMulticompany(common.TransactionCase): details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = lot_b - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() + mo.move_raw_ids.picked = True with self.assertRaises(UserError): mo.button_mark_done() diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 6e1a6f16d3c..681b297eabe 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -182,24 +182,24 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 2 + ml.quantity = 2 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 11 + ml.quantity = 11 details_operation_form.save() self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2) - self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('qty_done'), [2]) - self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [11]) - self.assertEqual(mo.move_raw_ids[0].quantity_done, 2) - self.assertEqual(mo.move_raw_ids[1].quantity_done, 11) + self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('quantity'), [2]) + self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [11]) + self.assertEqual(mo.move_raw_ids[0].quantity, 2) + self.assertEqual(mo.move_raw_ids[1].quantity, 11) mo.button_mark_done() self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [2, 11]) - self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.qty_done'), [2, 11]) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [2, 11]) + self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.quantity'), [2, 11]) def test_under_consumption(self): """ Consume less component quantity than the initial demand. @@ -218,26 +218,26 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 0 + ml.quantity = 0 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 5 + ml.quantity = 5 details_operation_form.save() self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2) - self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('qty_done'), [0]) - self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [5]) - self.assertEqual(mo.move_raw_ids[0].quantity_done, 0) - self.assertEqual(mo.move_raw_ids[1].quantity_done, 5) + self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('quantity'), [0]) + self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [5]) + self.assertEqual(mo.move_raw_ids[0].quantity, 0) + self.assertEqual(mo.move_raw_ids[1].quantity, 5) mo.button_mark_done() self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 1) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [0, 5]) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [0, 5]) self.assertEqual(mo.move_raw_ids.mapped('product_uom_qty'), [1, 10]) self.assertEqual(mo.move_raw_ids.mapped('state'), ['cancel', 'done']) - self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.qty_done'), [5]) + self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.quantity'), [5]) def test_update_quantity_1(self): """ Build 5 final products with different consumed lots, @@ -271,17 +271,18 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = lot_1 - ml.qty_done = 20 + ml.quantity = 20 details_operation_form.save() + mo.move_raw_ids[1].picked = True update_quantity_wizard = self.env['change.production.qty'].create({ 'mo_id': mo.id, 'product_qty': 4, }) update_quantity_wizard.change_prod_qty() - self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done, 20, 'Update the produce quantity should not impact already produced quantity.') + self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity, 20, 'Update the produce quantity should not impact already produced quantity.') self.assertEqual(mo.move_finished_ids.product_uom_qty, 4) mo.button_mark_done() @@ -303,8 +304,6 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 2 mo = mo_form.save() - mo._post_inventory() - update_quantity_wizard = self.env['change.production.qty'].create({ 'mo_id': mo.id, 'product_qty': 5, @@ -315,8 +314,8 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() mo.button_mark_done() - self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity_done')), 20) - self.assertEqual(sum(mo.move_finished_ids.mapped('quantity_done')), 5) + self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity')), 20) + self.assertEqual(sum(mo.move_finished_ids.mapped('quantity')), 5) def test_update_quantity_3(self): bom = self.env['mrp.bom'].create({ @@ -536,8 +535,8 @@ class TestMrpOrder(TestMrpCommon): mo_form = Form(production) mo_form.qty_producing = 8 production = mo_form.save() - self.assertEqual(production.move_raw_ids[0].quantity_done, 16, 'Should use half-up rounding when producing') - self.assertEqual(production.move_raw_ids[1].quantity_done, 34, 'Should use half-up rounding when producing') + self.assertEqual(production.move_raw_ids[0].quantity, 16, 'Should use half-up rounding when producing') + self.assertEqual(production.move_raw_ids[1].quantity, 34, 'Should use half-up rounding when producing') def test_product_produce_1(self): """ Checks the production wizard contains lines even for untracked products. """ @@ -553,7 +552,7 @@ class TestMrpOrder(TestMrpCommon): # change the quantity done in one line details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() # change the quantity producing @@ -562,9 +561,9 @@ class TestMrpOrder(TestMrpCommon): # check than all quantities are update correctly self.assertEqual(mo_form.move_raw_ids._records[0]['product_uom_qty'], 5, "Wrong quantity to consume") - self.assertEqual(mo_form.move_raw_ids._records[0]['quantity_done'], 3, "Wrong quantity done") + self.assertEqual(mo_form.move_raw_ids._records[0]['quantity'], 3, "Wrong quantity done") self.assertEqual(mo_form.move_raw_ids._records[1]['product_uom_qty'], 20, "Wrong quantity to consume") - self.assertEqual(mo_form.move_raw_ids._records[1]['quantity_done'], 12, "Wrong quantity done") + self.assertEqual(mo_form.move_raw_ids._records[1]['quantity'], 12, "Wrong quantity done") def test_product_produce_2(self): """ Checks that, for a BOM where one of the components is tracked by serial number and the @@ -599,10 +598,10 @@ class TestMrpOrder(TestMrpCommon): # get the proposed lot details_operation_form = Form(mo.move_raw_ids.filtered(lambda move: move.product_id == p1), view=self.env.ref('stock.view_stock_move_operations')) - self.assertEqual(len(details_operation_form.move_line_ids), 2) + self.assertEqual(len(details_operation_form.move_line_ids), 1) with details_operation_form.move_line_ids.edit(0) as ml: consumed_lots = ml.lot_id - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() remaining_lot = (lot_p1_1 | lot_p1_2) - consumed_lots @@ -668,28 +667,22 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() # p2 details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.edit(0) as line: - line.qty_done = line.reserved_uom_qty with details_operation_form.move_line_ids.new() as line: - line.qty_done = 1 + line.quantity = 1 details_operation_form.save() # p1 details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) - for i in range(len(details_operation_form.move_line_ids)): - # reservation in shelf1: 3 lot1, shelf2: 3 lot1, stock: 4 lot2 - with details_operation_form.move_line_ids.edit(i) as line: - line.qty_done = line.reserved_uom_qty with details_operation_form.move_line_ids.new() as line: - line.qty_done = 2 + line.quantity = 2 line.lot_id = first_lot_for_p1 with details_operation_form.move_line_ids.new() as line: - line.qty_done = 1 + line.quantity = 1 line.lot_id = second_lot_for_p1 details_operation_form.save() move_1 = mo.move_raw_ids.filtered(lambda m: m.product_id == p1) - # qty_done/reserved_uom_qty lot + # quantity/reserved_uom_qty lot # 3/3 lot 1 shelf 1 # 1/1 lot 1 shelf 2 # 2/2 lot 1 shelf 2 @@ -698,9 +691,9 @@ class TestMrpOrder(TestMrpCommon): ml_to_shelf_1 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_1) ml_to_shelf_2 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_2) - self.assertEqual(sum(ml_to_shelf_1.mapped('qty_done')), 3.0, '3 units should be took from shelf1 as reserved.') - self.assertEqual(sum(ml_to_shelf_2.mapped('qty_done')), 3.0, '3 units should be took from shelf2 as reserved.') - self.assertEqual(move_1.quantity_done, 13, 'You should have used the tem units.') + self.assertEqual(sum(ml_to_shelf_1.mapped('quantity')), 3.0, '3 units should be took from shelf1 as reserved.') + self.assertEqual(sum(ml_to_shelf_2.mapped('quantity')), 3.0, '3 units should be took from shelf2 as reserved.') + self.assertEqual(move_1.quantity, 13, 'You should have used the tem units.') mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") @@ -723,9 +716,6 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(len(ml_p1), 2) self.assertEqual(len(ml_p2), 1) - # Add some quantity already done to force an extra move line to be created - ml_p1[0].qty_done = 1.0 - # Produce baby! mo_form = Form(mo) mo_form.qty_producing = 1 @@ -734,9 +724,8 @@ class TestMrpOrder(TestMrpCommon): m_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1) ml_p1 = m_p1.mapped('move_line_ids') self.assertEqual(len(ml_p1), 2) - self.assertEqual(sorted(ml_p1.mapped('qty_done')), [2.0, 3.0], 'Quantity done should be 1.0, 2.0 or 3.0') - self.assertEqual(m_p1.quantity_done, 5.0, 'Total qty done should be 6.0') - self.assertEqual(sum(ml_p1.mapped('reserved_uom_qty')), 5.0, 'Total qty reserved should be 5.0') + self.assertEqual(sorted(ml_p1.mapped('quantity')), [2.0, 3.0], 'Quantity should be 2.0 and 3.0') + self.assertEqual(m_p1.quantity, 5.0, 'Total qty done should be 5.0') mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") @@ -763,21 +752,21 @@ class TestMrpOrder(TestMrpCommon): mo._post_inventory() self.assertEqual(len(mo.move_raw_ids), 4) - mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity_done = 3 + mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity = 3 update_quantity_wizard = self.env['change.production.qty'].create({ 'mo_id': mo.id, 'product_qty': 3, }) - mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity_done = 0 + mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity = 0 update_quantity_wizard.change_prod_qty() self.assertEqual(len(mo.move_raw_ids), 4) + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertTrue(all(s in ['done', 'cancel'] for s in mo.move_raw_ids.mapped('state'))) - self.assertEqual(sum(mo.move_raw_ids.mapped('move_line_ids.reserved_uom_qty')), 0) def test_consumption_strict_1(self): """ Checks the constraints of a strict BOM without tracking when playing around @@ -800,7 +789,7 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() # Won't accept to be done, instead return a wizard mo.button_mark_done() @@ -838,7 +827,7 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() # Won't accept to be done, instead return a wizard @@ -878,7 +867,7 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() # Won't accept to be done, instead return a wizard @@ -910,7 +899,7 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() # Won't accept to be done, instead return a wizard @@ -937,7 +926,7 @@ class TestMrpOrder(TestMrpCommon): with self.assertRaises(AssertionError): with mo_form.move_raw_ids.new() as move: move.product_id = serial - move.quantity_done = 2 + move.quantity = 2 mo_form.save() def test_product_produce_10(self): @@ -1011,18 +1000,18 @@ class TestMrpOrder(TestMrpCommon): move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1) self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(move_byproduct_1.product_uom_qty, 2.0) - self.assertEqual(move_byproduct_1.quantity_done, 0) + self.assertEqual(move_byproduct_1.quantity, 2) self.assertEqual(len(move_byproduct_1.move_line_ids), 2) move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2) self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(move_byproduct_2.product_uom_qty, 4.0) - self.assertEqual(move_byproduct_2.quantity_done, 0) + self.assertEqual(move_byproduct_2.quantity, 4) self.assertEqual(len(move_byproduct_2.move_line_ids), 1) move_byproduct_3 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3) self.assertEqual(move_byproduct_3.product_uom_qty, 4.0) - self.assertEqual(move_byproduct_3.quantity_done, 0) + self.assertEqual(move_byproduct_3.quantity, 4) self.assertEqual(move_byproduct_3.product_uom, dozen) self.assertEqual(len(move_byproduct_3.move_line_ids), 1) @@ -1032,27 +1021,28 @@ class TestMrpOrder(TestMrpCommon): move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1) self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(move_byproduct_1.product_uom_qty, 2.0) - self.assertEqual(move_byproduct_1.quantity_done, 0) + self.assertEqual(move_byproduct_1.quantity, 1) + self.assertFalse(move_byproduct_1.picked) move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2) self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(move_byproduct_2.product_uom_qty, 4.0) - self.assertEqual(move_byproduct_2.quantity_done, 0) + self.assertEqual(move_byproduct_2.quantity, 2) + self.assertFalse(move_byproduct_2.picked) move_byproduct_3 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3) self.assertEqual(move_byproduct_3.product_uom_qty, 4.0) - self.assertEqual(move_byproduct_3.quantity_done, 2.0) + self.assertEqual(move_byproduct_3.quantity, 2.0) + self.assertTrue(move_byproduct_3.picked) self.assertEqual(move_byproduct_3.product_uom, dozen) details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.serial_1 - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.lot_1 - ml.qty_done = 2 details_operation_form.save() action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) @@ -1066,42 +1056,44 @@ class TestMrpOrder(TestMrpCommon): move_byproduct_1 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1) self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(move_byproduct_1.product_uom_qty, 1.0) - self.assertEqual(move_byproduct_1.quantity_done, 0) + self.assertEqual(move_byproduct_1.quantity, 1) + self.assertFalse(move_byproduct_1.picked) move_byproduct_2 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2) self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(move_byproduct_2.product_uom_qty, 2.0) - self.assertEqual(move_byproduct_2.quantity_done, 0) + self.assertEqual(move_byproduct_2.quantity, 2) + self.assertFalse(move_byproduct_2.picked) move_byproduct_3 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3) self.assertEqual(move_byproduct_3.product_uom_qty, 2.0) - self.assertEqual(move_byproduct_3.quantity_done, 2.0) + self.assertEqual(move_byproduct_3.quantity, 2.0) + self.assertTrue(move_byproduct_3.picked) self.assertEqual(move_byproduct_3.product_uom, dozen) details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.serial_2 - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.lot_2 - ml.qty_done = 2 details_operation_form.save() details_operation_form = Form(move_byproduct_3, view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 3 + ml.quantity = 3 details_operation_form.save() mo2.button_mark_done() move_lines_byproduct_1 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1).mapped('move_line_ids') move_lines_byproduct_2 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2).mapped('move_line_ids') move_lines_byproduct_3 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3).mapped('move_line_ids') - self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_1).qty_done, 1.0) - self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_2).qty_done, 1.0) - self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_1).qty_done, 2.0) - self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_2).qty_done, 2.0) - self.assertEqual(sum(move_lines_byproduct_3.mapped('qty_done')), 5.0) + self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_1).quantity, 1.0) + self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_2).quantity, 1.0) + self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_1).quantity, 2.0) + self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_2).quantity, 2.0) + self.assertEqual(sum(move_lines_byproduct_3.mapped('quantity')), 5.0) self.assertEqual(move_lines_byproduct_3.mapped('product_uom_id'), dozen) def test_product_produce_11(self): @@ -1122,33 +1114,30 @@ class TestMrpOrder(TestMrpCommon): mo_form = Form(mo) mo_form.qty_producing = 3 - self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 15, 'Update the produce quantity should change the components quantity.') + self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 15, 'Update the produce quantity should change the components quantity.') mo = mo_form.save() - self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.') mo_form = Form(mo) mo_form.qty_producing = 4 - self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 20, 'Update the produce quantity should change the components quantity.') + self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 20, 'Update the produce quantity should change the components quantity.') mo = mo_form.save() - self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.') mo_form = Form(mo) mo_form.qty_producing = 1 - self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 5, 'Update the produce quantity should change the components quantity.') + self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 5, 'Update the produce quantity should change the components quantity.') mo = mo_form.save() - self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.') # try adding another product that doesn't belong to the BoM with mo_form.move_raw_ids.new() as move: move.product_id = self.product_4 mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 10 + ml.quantity = 10 details_operation_form.save() # Check that this new product is not updated by qty_producing mo_form = Form(mo) mo_form.qty_producing = 2 for move in mo_form.move_raw_ids._records: if move['product_id'] == self.product_4.id: - self.assertEqual(move['quantity_done'], 10) + self.assertEqual(move['quantity'], 10) break mo = mo_form.save() mo.button_mark_done() @@ -1192,9 +1181,10 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 1 mo1 = mo_form.save() details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = sn details_operation_form.save() + mo1.move_raw_ids.picked = True mo1.button_mark_done() mo_form = Form(self.env['mrp.production']) @@ -1208,9 +1198,10 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 1 mo2 = mo_form.save() details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = sn details_operation_form.save() + mo2.move_raw_ids.picked = True with self.assertRaises(UserError): mo2.button_mark_done() @@ -1250,7 +1241,7 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() move_byproduct = mo.move_finished_ids.filtered(lambda m: m.product_id != mo.product_id) details_operation_form = Form(move_byproduct, view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = sn details_operation_form.save() mo.button_mark_done() @@ -1286,9 +1277,10 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 1 mo1 = mo_form.save() details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = sn details_operation_form.save() + mo1.move_raw_ids.picked = True mo1.button_mark_done() unbuild_form = Form(self.env['mrp.unbuild']) @@ -1310,9 +1302,10 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 1 mo2 = mo_form.save() details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = sn details_operation_form.save() + mo2.move_raw_ids.picked = True mo2.button_mark_done() def test_product_produce_duplicate_5(self): @@ -1373,9 +1366,10 @@ class TestMrpOrder(TestMrpCommon): view=self.env.ref("stock.view_stock_move_operations"), ) with finished_good_detailed_operations_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 ml.lot_id = subassembly_sn finished_good_detailed_operations_form.save() + finished_good_mo.move_raw_ids.picked = True finished_good_mo.button_mark_done() finished_good_ub_form = Form(self.env["mrp.unbuild"]) @@ -1425,8 +1419,6 @@ class TestMrpOrder(TestMrpCommon): 'product_uom': self.ref('uom.product_uom_unit'), 'production_id': mo.id, 'location_dest_id': self.ref('stock.stock_location_output'), - 'product_uom_qty': 0, - 'quantity_done': 0, }) self.assertEqual(move.name, mo.name) @@ -1464,8 +1456,6 @@ class TestMrpOrder(TestMrpCommon): 'production_id': mo.id, 'location_id': self.ref('stock.stock_location_stock'), 'location_dest_id': self.ref('stock.stock_location_output'), - 'product_uom_qty': 0, - 'quantity_done': 0, }) mo.move_raw_ids |= move mo.action_confirm() @@ -1525,13 +1515,13 @@ class TestMrpOrder(TestMrpCommon): mo_form.lot_producing_id = final_product_lot mo = mo_form.save() - move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) - self.assertEqual(move_line_raw.qty_done, 1) + move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.quantity) + self.assertEqual(move_line_raw.quantity, 1) self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') mo._post_inventory() - move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) - self.assertEqual(move_line_finished.qty_done, 1) + move_line_finished = mo.move_finished_ids.move_line_ids.filtered(lambda m: m.state == 'done' and m.quantity) + self.assertEqual(move_line_finished.quantity, 1) self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') def test_product_type_service_1(self): @@ -1577,27 +1567,9 @@ class TestMrpOrder(TestMrpCommon): mo.action_assign() res_dict = mo.button_mark_done() self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', 'done']) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1]) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1]) self.assertEqual(mo.move_finished_ids.state, 'done') - self.assertEqual(mo.move_finished_ids.quantity_done, 1) - - def test_immediate_validate_2(self): - """ In a production non-tracked components, clicking on mark as done after filling quantity for a move be - processed as immediate production. The manual filling quantity will be kept.""" - mo, bom, p_final, p1, p2 = self.generate_mo(qty_final=1, qty_base_1=1, qty_base_2=1) - self.env['stock.quant']._update_available_quantity(p1, self.stock_location_components, 5.0) - self.env['stock.quant']._update_available_quantity(p2, self.stock_location_components, 5.0) - mo.action_assign() - details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 2 - details_operation_form.save() - mo.move_raw_ids.filtered('quantity_done').manual_consumption = True # this should be set automatically in front end - mo.button_mark_done() - self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', 'done']) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [2, 1]) - self.assertEqual(mo.move_finished_ids.state, 'done') - self.assertEqual(mo.move_finished_ids.quantity_done, 1) + self.assertEqual(mo.move_finished_ids.quantity, 1) def test_immediate_validate_3(self): """ In a production with a serial number tracked product. Check that the immediate production only creates @@ -1611,7 +1583,7 @@ class TestMrpOrder(TestMrpCommon): wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() action = wizard.action_backorder() self.assertEqual(mo.qty_producing, 1) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1]) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1]) self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2) mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] self.assertEqual(mo_backorder.product_qty, 1) @@ -1628,7 +1600,7 @@ class TestMrpOrder(TestMrpCommon): wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() action = wizard.action_backorder() self.assertEqual(mo.qty_producing, 1) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1]) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1]) self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2) mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] self.assertEqual(mo_backorder.product_qty, 1) @@ -1657,9 +1629,9 @@ class TestMrpOrder(TestMrpCommon): mos = mo1 | mo2 | mo3 mos.button_mark_done() self.assertEqual(mos.move_raw_ids.mapped('state'), ['done'] * 6) - self.assertEqual(mos.move_raw_ids.mapped('quantity_done'), [1] * 6) + self.assertEqual(mos.move_raw_ids.mapped('quantity'), [1] * 6) self.assertEqual(mos.move_finished_ids.mapped('state'), ['done'] * 3) - self.assertEqual(mos.move_finished_ids.mapped('quantity_done'), [1] * 3) + self.assertEqual(mos.move_finished_ids.mapped('quantity'), [1] * 3) def test_components_availability(self): self.bom_2.unlink() # remove the kit bom of product_5 @@ -1698,7 +1670,7 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(mo.components_availability, f'Exp {format_date(self.env, tommorrow)}') self.assertEqual(mo.components_availability_state, 'expected') - (move1 | move2 | move3)._set_quantities_to_reservation() + (move1 | move2 | move3).picked = True (move1 | move2 | move3)._action_done() mo.invalidate_recordset(['components_availability', 'components_availability_state']) @@ -1721,9 +1693,9 @@ class TestMrpOrder(TestMrpCommon): mo.action_assign() mo.button_mark_done() self.assertEqual(mo.move_raw_ids.mapped('state'), ['done'] * 2) - self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1] * 2) + self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1] * 2) self.assertEqual(mo.move_finished_ids.state, 'done') - self.assertEqual(mo.move_finished_ids.quantity_done, 1) + self.assertEqual(mo.move_finished_ids.quantity, 1) self.assertTrue(mo.move_finished_ids.move_line_ids.lot_id != False) def test_immediate_validate_uom(self): @@ -1756,9 +1728,9 @@ class TestMrpOrder(TestMrpCommon): mo.action_assign() mo.button_mark_done() self.assertEqual(mo.move_raw_ids.state, 'done') - self.assertEqual(mo.move_raw_ids.quantity_done, 12) + self.assertEqual(mo.move_raw_ids.quantity, 12) self.assertEqual(mo.move_finished_ids.state, 'done') - self.assertEqual(mo.move_finished_ids.quantity_done, 1) + self.assertEqual(mo.move_finished_ids.quantity, 1) self.assertEqual(component.qty_available, 13) def test_immediate_validate_uom_2(self): @@ -1815,7 +1787,7 @@ class TestMrpOrder(TestMrpCommon): mo.button_mark_done() self.assertEqual(mo.move_raw_ids.product_uom_qty, 0.2) - self.assertEqual(mo.move_raw_ids.quantity_done, 0.2) + self.assertEqual(mo.move_raw_ids.quantity, 0.2) def test_copy(self): """ Check that copying a done production, create all the stock moves""" @@ -2072,9 +2044,11 @@ class TestMrpOrder(TestMrpCommon): """ mo, _, _, _, _ = self.generate_mo(qty_final=30, qty_base_1=2, qty_base_2=3) mo.action_confirm() - mo.qty_producing = 10 - mo.move_raw_ids[0].quantity_done = 90 - mo.move_raw_ids[1].quantity_done = 70 + mo_form = Form(mo) + mo_form.qty_producing = 10 + mo = mo_form.save() + mo.move_raw_ids[0].quantity = 90 + mo.move_raw_ids[1].quantity = 70 action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder.save().action_backorder() @@ -2089,9 +2063,9 @@ class TestMrpOrder(TestMrpCommon): move_prod_2 = self.env['stock.move'].search([ ('product_id', '=', mo.bom_id.bom_line_ids[1].product_id.id), ('raw_material_production_id', '=', mo.id)]) - self.assertEqual(sum(move_prod_1.mapped('quantity_done')), 90.0) + self.assertEqual(sum(move_prod_1.mapped('quantity')), 90.0) self.assertEqual(sum(move_prod_1.mapped('product_uom_qty')), 30.0) - self.assertEqual(sum(move_prod_2.mapped('quantity_done')), 70.0) + self.assertEqual(sum(move_prod_2.mapped('quantity')), 70.0) self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 20.0) # Check quantities of the backorder MO @@ -2111,9 +2085,11 @@ class TestMrpOrder(TestMrpCommon): """ mo, _, _, p1, p2 = self.generate_mo(qty_final=20, qty_base_1=1, qty_base_2=1) mo.action_confirm() - mo.qty_producing = 10 - mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done = 5 - mo.move_raw_ids.filtered(lambda m: m.product_id == p2).quantity_done = 10 + mo_form = Form(mo) + mo_form.qty_producing = 10 + mo = mo_form.save() + mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity = 5 + mo.move_raw_ids.filtered(lambda m: m.product_id == p2).quantity = 10 action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder.save().action_backorder() @@ -2123,10 +2099,10 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(mo.product_uom_qty, 10.0) self.assertEqual(mo.qty_produced, 10.0) move_prod_1_done = mo.move_raw_ids.filtered(lambda m: m.product_id == p1) - self.assertEqual(sum(move_prod_1_done.mapped('quantity_done')), 5) + self.assertEqual(sum(move_prod_1_done.mapped('quantity')), 5) self.assertEqual(sum(move_prod_1_done.mapped('product_uom_qty')), 10) move_prod_2 = mo.move_raw_ids.filtered(lambda m: m.product_id == p2) - self.assertEqual(sum(move_prod_2.mapped('quantity_done')), 10) + self.assertEqual(sum(move_prod_2.mapped('quantity')), 10) self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 10) # Check quantities of the backorder MO @@ -3193,7 +3169,7 @@ class TestMrpOrder(TestMrpCommon): mo.action_confirm() self.assertEqual(mo.move_raw_ids.product_uom_qty, 1) - self.assertEqual(mo.move_raw_ids.move_line_ids.reserved_uom_qty, mo.move_raw_ids.product_uom_qty) + self.assertEqual(mo.move_raw_ids.move_line_ids.quantity, mo.move_raw_ids.product_uom_qty) self.assertEqual(mo.move_raw_ids.availability, 250) update_quantity_wizard = self.env['change.production.qty'].create({ 'mo_id': mo.id, @@ -3202,7 +3178,7 @@ class TestMrpOrder(TestMrpCommon): update_quantity_wizard.change_prod_qty() self.assertEqual(mo.move_raw_ids.product_uom_qty, 2) - self.assertEqual(mo.move_raw_ids.move_line_ids.reserved_uom_qty, mo.move_raw_ids.product_uom_qty) + self.assertEqual(mo.move_raw_ids.move_line_ids.quantity, mo.move_raw_ids.product_uom_qty) self.assertEqual(mo.move_raw_ids.availability, 0) def test_update_qty_to_consume_of_component(self): @@ -3228,7 +3204,7 @@ class TestMrpOrder(TestMrpCommon): with mo_form.move_raw_ids.edit(0) as raw: raw.product_uom_qty = 1.25 - self.assertEqual(mo.move_raw_ids.quantity_done, 1.25) + self.assertEqual(mo.move_raw_ids.quantity, 1.25) def test_clear_finished_move(self): """ Test that the finished moves created by the compute are correctly @@ -3395,7 +3371,7 @@ class TestMrpOrder(TestMrpCommon): - a component's UoM is changed after MO is created => action_set_qty = match BoM qty, but leave UoM unchanged (i.e. correctly convert) scenario 3: - a component has 2 moves in a MO => action_set_qty = set the 1st move to the correct qty, set 2nd move to 0 - (i.e. no way to know how to distribute qty_done across these moves since warning aggregates qty by product) + (i.e. no way to know how to distribute quantity across these moves since warning aggregates qty by product) """ mo, bom, p_final, p1, p2 = self.generate_mo(consumption='warning', qty_final=10, qty_base_1=12, qty_base_2=20) @@ -3405,13 +3381,13 @@ class TestMrpOrder(TestMrpCommon): mo = mo_form.save() # mo.move_raw_ids[0] = p2 => 20 qty_base, mo.move_raw_ids[1] = p1 => 12 qty_base self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 200, "current MO To Consume qty should match expected qty to produce") - self.assertEqual(mo.move_raw_ids[0].quantity_done, 80, "current MO Consumed qty should match expected qty to produce") + self.assertEqual(mo.move_raw_ids[0].quantity, 80, "current MO Consumed qty should match expected qty to produce") self.assertEqual(mo.move_raw_ids[1].product_uom_qty, 120, "current MO To Consume qty should match expected qty produced") - self.assertEqual(mo.move_raw_ids[1].quantity_done, 48, "current MO Consumed qty should match expected qty produced") + self.assertEqual(mo.move_raw_ids[1].quantity, 48, "current MO Consumed qty should match expected qty produced") # bom changes won't auto-update MO, it will only show diff in consumption warning bom.bom_line_ids[0].product_qty = 10 self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 200) - self.assertEqual(mo.move_raw_ids[0].quantity_done, 80) + self.assertEqual(mo.move_raw_ids[0].quantity, 80) action = mo.button_mark_done() warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) consumption = warning.save() @@ -3421,9 +3397,9 @@ class TestMrpOrder(TestMrpCommon): backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder.save().action_backorder() self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 80, "current bom expected qty should remain unchanged") - self.assertEqual(mo.move_raw_ids[0].quantity_done, 40, "current bom expected qty was not applied as qty to be done") + self.assertEqual(mo.move_raw_ids[0].quantity, 40, "current bom expected qty was not applied as qty to be done") self.assertEqual(mo.move_raw_ids[1].product_uom_qty, 48, "line without consumption issue was incorrectly changed") - self.assertEqual(mo.move_raw_ids[1].quantity_done, 48, "line without consumption issue was incorrectly changed") + self.assertEqual(mo.move_raw_ids[1].quantity, 48, "line without consumption issue was incorrectly changed") self.assertEqual(mo.state, 'done') # double check that backorder qtys are also correct mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] @@ -3448,7 +3424,7 @@ class TestMrpOrder(TestMrpCommon): mo2 = mo2_form.save() self.assertEqual(len(mo2.move_raw_ids), 1, "current MO should still have 1 component from its BoM deleted") self.assertEqual(mo2.move_raw_ids[0].product_uom_qty, 60, "current MO To Consume qty should match manually set expected qty produced") - self.assertEqual(mo2.move_raw_ids[0].quantity_done, 48, "current MO Consumed qty should match expected qty to produce based on manually set value") + self.assertEqual(mo2.move_raw_ids[0].quantity, 48, "current MO Consumed qty should match expected qty to produce based on manually set value") action = mo2.button_mark_done() warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) @@ -3468,10 +3444,10 @@ class TestMrpOrder(TestMrpCommon): for move in mo2.move_raw_ids: if move.product_id == p2: self.assertEqual(move.product_uom_qty, 40, "missing line values were not correctly added") - self.assertEqual(move.quantity_done, 40, "missing line values were not correctly added") + self.assertEqual(move.quantity, 40, "missing line values were not correctly added") else: self.assertEqual(move.product_uom_qty, 48, "expected qty should be unchanged") - self.assertEqual(move.quantity_done, 4, "expected qty was not applied as qty to be done (UoM was possibly not correctly converted)") + self.assertEqual(move.quantity, 4, "expected qty was not applied as qty to be done (UoM was possibly not correctly converted)") self.assertEqual(mo2.state, 'done') # double check that backorder qtys are also correct mo2_backorder = mo2.procurement_group_id.mrp_production_ids[-1] @@ -3499,9 +3475,9 @@ class TestMrpOrder(TestMrpCommon): mo3_form.qty_producing = 1 mo3 = mo3_form.save() self.assertEqual(mo3.move_raw_ids[0].product_uom_qty, 12, "BoM created comp move does not match expected To Consume qty") - self.assertEqual(mo3.move_raw_ids[0].quantity_done, 12, "BoM created comp move does not match expected Consumed qty") + self.assertEqual(mo3.move_raw_ids[0].quantity, 12, "BoM created comp move does not match expected Consumed qty") self.assertEqual(mo3.move_raw_ids[1].product_uom_qty, 5, "Manually added comp move does not match original To Consume qty") - self.assertEqual(mo3.move_raw_ids[1].quantity_done, 5, "Manually added comp move was not Consumed") + self.assertEqual(mo3.move_raw_ids[1].quantity, 5, "Manually added comp move was not Consumed") action = mo3.button_mark_done() warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) consumption = warning.save() @@ -3510,9 +3486,9 @@ class TestMrpOrder(TestMrpCommon): self.assertEqual(consumption.mrp_consumption_warning_line_ids[0].product_consumed_qty_uom, 17, "total Consumed qty not correctly passed to wizard") action = consumption.action_set_qty() self.assertEqual(mo3.move_raw_ids[0].product_uom_qty, 12, "BoM created comp move does not match expected To Consume qty") - self.assertEqual(mo3.move_raw_ids[0].quantity_done, 12, "BoM created comp move does not match expected Consumed qty") + self.assertEqual(mo3.move_raw_ids[0].quantity, 12, "BoM created comp move does not match expected Consumed qty") self.assertEqual(mo3.move_raw_ids[1].product_uom_qty, 5, "Manually added comp move To Consume qty should be unchanged") - self.assertEqual(mo3.move_raw_ids[1].quantity_done, 0, "Extra line Consumed qty not correctly zero-ed") + self.assertEqual(mo3.move_raw_ids[1].quantity, 0, "Extra line Consumed qty not correctly zero-ed") self.assertEqual(mo3.state, 'done') def test_exceeded_consumed_qty_and_duplicated_lines(self): @@ -3549,15 +3525,15 @@ class TestMrpOrder(TestMrpCommon): mo_form.qty_producing = 1.0 mo = mo_form.save() - mo.move_raw_ids[0].move_line_ids.qty_done = 1.5 + mo.move_raw_ids[0].move_line_ids.quantity = 1.5 mo.button_mark_done() self.assertEqual(mo.state, 'done') p02_raws = mo.move_raw_ids.filtered(lambda m: m.product_id == product02) p03_raws = mo.move_raw_ids.filtered(lambda m: m.product_id == product03) - self.assertEqual(sum(p02_raws.mapped('quantity_done')), 1.5) - self.assertEqual(sum(p03_raws.mapped('quantity_done')), 2) + self.assertEqual(sum(p02_raws.mapped('quantity')), 1.5) + self.assertEqual(sum(p03_raws.mapped('quantity')), 2) def test_validation_mo_with_tracked_component(self): """ @@ -3584,18 +3560,15 @@ class TestMrpOrder(TestMrpCommon): mo.action_confirm() self.assertEqual(mo.state, 'confirmed') # set the qty to consume of the tracked product to 0 - mo.move_raw_ids[0].write({ - 'product_uom_qty': 0, - 'quantity_done': 0, - }) - mo.action_assign() + mo.move_raw_ids[0].product_uom_qty = 0 + mo.move_raw_ids[0].quantity = 0 # Set MO Done and create backorder action = mo.button_mark_done() consumption_warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])).save() - self.assertEqual(len(consumption_warning.mrp_consumption_warning_line_ids), 1) + self.assertEqual(len(consumption_warning.mrp_consumption_warning_line_ids), 2) self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_consumed_qty_uom, 0) - self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_expected_qty_uom, 1) + self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_expected_qty_uom, 0) # Force the warning consumption_warning.action_confirm() self.assertEqual(mo.state, 'done') diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py index 9a511ff4bed..b2aecbad51f 100644 --- a/addons/mrp/tests/test_procurement.py +++ b/addons/mrp/tests/test_procurement.py @@ -174,22 +174,21 @@ class TestProcurement(TestMrpCommon): self.assertTrue(picking_qc_to_stock) picking_input_to_qc.action_assign() self.assertEqual(picking_input_to_qc.state, 'assigned') - picking_input_to_qc.move_line_ids.write({'qty_done': 5.0}) + picking_input_to_qc.move_ids.write({'quantity': 5.0, 'picked': True}) picking_input_to_qc._action_done() picking_qc_to_stock.action_assign() self.assertEqual(picking_qc_to_stock.state, 'assigned') - picking_qc_to_stock.move_line_ids.write({'qty_done': 3.0}) + picking_qc_to_stock.move_ids.write({'quantity': 3.0, 'picked': True}) picking_qc_to_stock.with_context(skip_backorder=True, picking_ids_not_to_backorder=picking_qc_to_stock.ids).button_validate() self.assertEqual(picking_qc_to_stock.state, 'done') mo.action_assign() - self.assertEqual(mo.move_raw_ids.reserved_availability, 3.0) + self.assertEqual(mo.move_raw_ids.quantity, 3.0) produce_form = Form(mo) produce_form.qty_producing = 3.0 mo = produce_form.save() - self.assertEqual(mo.move_raw_ids.quantity_done, 3.0) - picking_qc_to_stock.move_line_ids.qty_done = 5.0 - self.assertEqual(mo.move_raw_ids.reserved_availability, 5.0) - self.assertEqual(mo.move_raw_ids.quantity_done, 3.0) + self.assertEqual(mo.move_raw_ids.quantity, 3.0) + picking_qc_to_stock.move_line_ids.quantity = 5.0 + self.assertEqual(mo.move_raw_ids.quantity, 3.0) def test_link_date_mo_moves(self): """ Check link of shedule date for manufaturing with date stock move.""" @@ -343,7 +342,7 @@ class TestProcurement(TestMrpCommon): production.button_mark_done() move_dest._action_assign() - self.assertEqual(move_dest.reserved_availability, 10.0) + self.assertEqual(move_dest.quantity, 10.0) def test_auto_assign(self): """ When auto reordering rule exists, check for when: @@ -455,7 +454,6 @@ class TestProcurement(TestMrpCommon): 'location_dest_id': self.ref('stock.stock_location_customers'), })], }) - pick_output.action_reset_draft() pick_output.action_confirm() # should trigger orderpoint to create and confirm 1st MO pick_output.action_assign() @@ -467,7 +465,7 @@ class TestProcurement(TestMrpCommon): self.assertEqual(len(mo), 1, "Manufacture order was not automatically created") mo.action_assign() mo.is_locked = False - self.assertEqual(mo.move_raw_ids.reserved_availability, 0, "No components should be reserved yet") + self.assertEqual(mo.move_raw_ids.quantity, 0, "No components should be reserved yet") self.assertEqual(mo.product_qty, 15, "Quantity to produce should be picking demand + reordering rule max qty") # 2nd MO for product_2 should have been created and confirmed when 1st MO for product_1 was confirmed @@ -483,7 +481,7 @@ class TestProcurement(TestMrpCommon): mo2 = mo2_form.save() mo2.button_mark_done() - self.assertEqual(mo.move_raw_ids.reserved_availability, 15, "Components should have been auto-reserved") + self.assertEqual(mo.move_raw_ids.quantity, 15, "Components should have been auto-reserved") # add new component to 1st MO mo_form = Form(mo) @@ -501,12 +499,12 @@ class TestProcurement(TestMrpCommon): self.assertEqual(mo3.product_qty, 6, "Quantity to produce should be 1 + reordering rule max qty") mo_form = Form(mo) - mo.move_raw_ids.quantity_done = 15 + mo.move_raw_ids.quantity = 15 mo_form.qty_producing = 15 mo = mo_form.save() mo.button_mark_done() - self.assertEqual(pick_output.move_ids_without_package.reserved_availability, 10, "Completed products should have been auto-reserved in picking") + self.assertEqual(pick_output.move_ids_without_package.quantity, 10, "Completed products should have been auto-reserved in picking") # make sure next MO auto-reserves components now that they are in stock since # default reservation_method = 'at_confirm' @@ -518,7 +516,7 @@ class TestProcurement(TestMrpCommon): mo_assign_at_confirm = mo_form.save() mo_assign_at_confirm.action_confirm() - self.assertEqual(mo_assign_at_confirm.move_raw_ids.reserved_availability, 5, "Components should have been auto-reserved") + self.assertEqual(mo_assign_at_confirm.move_raw_ids.quantity, 5, "Components should have been auto-reserved") def test_check_update_qty_mto_chain(self): """ Simulate a mto chain with a manufacturing order. Updating the diff --git a/addons/mrp/tests/test_smp.py b/addons/mrp/tests/test_smp.py index c5e98838b4d..3daeb966938 100644 --- a/addons/mrp/tests/test_smp.py +++ b/addons/mrp/tests/test_smp.py @@ -117,11 +117,11 @@ class TestMrpSerialMassProduce(TestMrpCommon): # Reload the wizard to apply generated serial numbers wizard = Form(self.env['stock.assign.serial'].browse(action['res_id'])) wizard.save().apply() - # 1st & 3rd MO in sequence should have only 1 move lines (1 lot) for product_to_use_1 (2nd in bom) + # 1st & 2nd MO in sequence should have only 1 move lines (1 lot) for product_to_use_1 (2nd in bom) self.assertEqual(mo.procurement_group_id.mrp_production_ids[0].move_raw_ids[1].move_lines_count, 1) - self.assertEqual(mo.procurement_group_id.mrp_production_ids[2].move_raw_ids[1].move_lines_count, 1) - # 2nd MO should have 2 move lines (2 different lots) for product_to_use_1 - self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 2) + self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 1) + # 3rd MO should have 2 move lines (2 different lots) for product_to_use_1 + self.assertEqual(mo.procurement_group_id.mrp_production_ids[2].move_raw_ids[1].move_lines_count, 2) # Verify if lot IDs were correctly assigned to move lines mo.procurement_group_id.mrp_production_ids.button_mark_done() diff --git a/addons/mrp/tests/test_stock.py b/addons/mrp/tests/test_stock.py index c4d6ae97ed0..546fb223ec5 100644 --- a/addons/mrp/tests/test_stock.py +++ b/addons/mrp/tests/test_stock.py @@ -296,7 +296,7 @@ class TestKitPicking(common.TestMrpCommon): } def test_kit_immediate_transfer(self): - """ Make sure a kit is split in the corrects quantity_done by components in case of an + """ Make sure a kit is split in the corrects quantity by components in case of an immediate transfer. """ picking = self.env['stock.picking'].create({ @@ -308,7 +308,8 @@ class TestKitPicking(common.TestMrpCommon): self.env['stock.move'].create({ 'name': self.kit_parent.name, 'product_id': self.kit_parent.id, - 'product_uom_qty': 3, + 'quantity': 3, + 'picked': True, 'product_uom': self.kit_parent.uom_id.id, 'picking_id': picking.id, 'picking_type_id': self.env.ref('stock.picking_type_in').id, @@ -316,14 +317,12 @@ class TestKitPicking(common.TestMrpCommon): 'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id, }) picking.button_validate() - backorder_wizard_dict = picking.button_validate() - backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])) - backorder_wizard_form.save().process() # We check that the picking has the correct quantities after its move were splitted. self.assertEqual(len(picking.move_ids), 7) - for move_line in picking.move_ids: - self.assertEqual(move_line.quantity_done, self.expected_quantities[move_line.product_id]) + for move in picking.move_ids: + self.assertEqual(move.quantity, self.expected_quantities[move.product_id]) + self.assertEqual(move.state, 'done') def test_kit_planned_transfer(self): """ Make sure a kit is split in the corrects product_qty by components in case of a @@ -334,7 +333,6 @@ class TestKitPicking(common.TestMrpCommon): 'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id, 'partner_id': self.test_partner.id, 'picking_type_id': self.env.ref('stock.picking_type_in').id, - 'immediate_transfer': False, }) move_receipt_1 = self.env['stock.move'].create({ 'name': self.kit_parent.name, @@ -368,8 +366,6 @@ class TestKitPicking(common.TestMrpCommon): 'picking_type_id': in_type.id, 'location_id': customer_location.id, 'location_dest_id': stock_location.id, - 'state': 'draft', - 'immediate_transfer': False, 'move_ids': [(0, 0, { 'name': product.name, 'product_id': product.id, @@ -381,19 +377,20 @@ class TestKitPicking(common.TestMrpCommon): }) receipt.action_confirm() - receipt.move_line_ids.qty_done = 1 + receipt.move_line_ids.quantity = 1 receipt.move_line_ids = [(0, 0, { 'product_id': kit.id, - 'qty_done': 1, + 'quantity': 1, 'product_uom_id': kit.uom_id.id, 'location_id': customer_location.id, 'location_dest_id': stock_location.id, })] + receipt.move_ids.picked = True receipt.button_validate() self.assertEqual(receipt.state, 'done') self.assertRecordValues(receipt.move_ids, [ - {'product_id': product.id, 'quantity_done': 1, 'state': 'done'}, - {'product_id': compo.id, 'quantity_done': 1, 'state': 'done'}, + {'product_id': product.id, 'quantity': 1, 'state': 'done'}, + {'product_id': compo.id, 'quantity': 1, 'state': 'done'}, ]) diff --git a/addons/mrp/tests/test_stock_report.py b/addons/mrp/tests/test_stock_report.py index ee691676a6b..0850d82eab2 100644 --- a/addons/mrp/tests/test_stock_report.py +++ b/addons/mrp/tests/test_stock_report.py @@ -123,7 +123,7 @@ class TestMrpStockReports(TestReportsCommon): pick = mo_1.move_raw_ids.move_orig_ids.picking_id pick_form = Form(pick) with pick_form.move_line_ids_without_package.edit(0) as move_line: - move_line.qty_done = 20 + move_line.quantity = 20 pick = pick_form.save() pick.button_validate() # Produces 3 products then creates a backorder for the remaining product. @@ -236,7 +236,7 @@ class TestMrpStockReports(TestReportsCommon): picking = picking_form.save() picking.action_confirm() - picking.move_ids.quantity_done = 1 + picking.move_ids.write({'quantity': 1, 'picked': True}) move = picking.move_ids.filtered(lambda m: m.name == "Super Kit" and m.product_id == compo03) move.move_line_ids.result_package_id = self.env['stock.quant.package'].create({'name': 'Package0001'}) picking.button_validate() @@ -254,4 +254,6 @@ class TestMrpStockReports(TestReportsCommon): break if keys[0] in line: keys = keys[1:] + + self.assertFalse(keys, "All keys should be in the report with the defined order") diff --git a/addons/mrp/tests/test_traceability.py b/addons/mrp/tests/test_traceability.py index 8c4526df261..7c14f948fea 100644 --- a/addons/mrp/tests/test_traceability.py +++ b/addons/mrp/tests/test_traceability.py @@ -95,12 +95,13 @@ class TestTraceability(TestMrpCommon): details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[2], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") @@ -192,48 +193,45 @@ class TestTraceability(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_1', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_1', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_1', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_1', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() + mo.move_raw_ids.picked = True action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder.save().action_backorder() @@ -251,56 +249,50 @@ class TestTraceability(TestMrpCommon): mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_1), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_2', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_2), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_2', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_2', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2), view=self.env.ref('stock.view_stock_move_operations') ) - with details_operation_form.move_line_ids.new() as ml: + with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_2', 'company_id': self.env.company.id, }) - ml.qty_done = 1 details_operation_form.save() + mo_backorder.move_raw_ids.picked = True mo_backorder.button_mark_done() - # self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 4) - # self.assertEqual(len(mo.move_finished_ids.mapped('move_line_ids')), 6) - mo = mo | mo_backorder raw_move_lines = mo.move_raw_ids.mapped('move_line_ids') raw_line_raw_1_lot_1 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_1') @@ -310,19 +302,19 @@ class TestTraceability(TestMrpCommon): finished_move_lines = mo.move_finished_ids.mapped('move_line_ids') finished_move_line_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_1') - self.assertEqual(finished_move_line_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) + self.assertEqual(finished_move_line_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) finished_move_line_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_2') raw_line_raw_1_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_2') raw_line_raw_2_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_2_lot_2') self.assertEqual(finished_move_line_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_1_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_1') - self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) + self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_1_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_2') self.assertEqual(byproduct_move_line_1_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_2_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_1') - self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) + self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_2_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_2') self.assertEqual(byproduct_move_line_2_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) @@ -348,6 +340,7 @@ class TestTraceability(TestMrpCommon): mo_form.qty_producing = 1.0 mo_form.lot_producing_id = lot mo = mo_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() unbuild_form = Form(self.env['mrp.unbuild']) @@ -367,6 +360,7 @@ class TestTraceability(TestMrpCommon): _logger.warning('Dummy') self.assertEqual(len(log_catcher.output), 1, "Useless warnings: \n%s" % "\n".join(log_catcher.output[:-1])) + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertEqual(mo.state, 'done') @@ -483,7 +477,7 @@ class TestTraceability(TestMrpCommon): mo_form.qty_producing = 1 mo_form.lot_producing_id = lot_componentA mo = mo_form.save() - mo.move_raw_ids[0].quantity_done = 1.0 + mo.move_raw_ids.picked = True mo.button_mark_done() # Produce 1 endProduct A @@ -496,7 +490,7 @@ class TestTraceability(TestMrpCommon): mo_form.qty_producing = 1 mo_form.lot_producing_id = lot_endProductA mo = mo_form.save() - mo.move_raw_ids[0].quantity_done = 1.0 + mo.move_raw_ids[0].write({'quantity': 1.0, 'picked': True}) mo.button_mark_done() # Create out picking for EndProduct A @@ -508,7 +502,7 @@ class TestTraceability(TestMrpCommon): moveA = self.env['stock.move'].create({ 'name': 'Picking A move', 'product_id': endproductA.id, - 'quantity_done': 1, + 'quantity': 1, 'product_uom': endproductA.uom_id.id, 'picking_id': pickingA_out.id, 'location_id': stock_location.id, @@ -516,10 +510,11 @@ class TestTraceability(TestMrpCommon): # Set move_line lot_id to the mrp.production lot_producing_id moveA.move_line_ids[0].write({ - 'qty_done': 1.0, + 'quantity': 1.0, 'lot_id': lot_endProductA.id, }) # Transfer picking + moveA.picked = True pickingA_out._action_done() # Use concat so that delivery_ids is computed in batch. @@ -557,7 +552,7 @@ class TestTraceability(TestMrpCommon): with Form(mo) as mo_form: mo_form.qty_producing = 1 - mo.move_raw_ids.move_line_ids.qty_done = 1 + mo.move_raw_ids.move_line_ids.quantity = 1 mo.button_mark_done() # unbuild @@ -583,12 +578,13 @@ class TestTraceability(TestMrpCommon): 'product_id': component.id, 'product_uom': component.uom_id.id, 'product_uom_qty': 1.0, + 'picked': True, 'move_line_ids': [(0, 0, { 'product_id': component.id, 'location_id': scrap_location.id, 'location_dest_id': stock_location.id, 'product_uom_id': component.uom_id.id, - 'qty_done': 1.0, + 'quantity': 1.0, 'lot_id': serial_number.id, })], }) @@ -605,12 +601,13 @@ class TestTraceability(TestMrpCommon): with Form(mo) as mo_form: mo_form.qty_producing = 1 - mo.move_raw_ids.move_line_ids.qty_done = 1 + mo.move_raw_ids.move_line_ids.quantity = 1 + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertRecordValues((mo.move_finished_ids + mo.move_raw_ids).move_line_ids, [ - {'product_id': self.bom_4.product_id.id, 'lot_id': False, 'qty_done': 1}, - {'product_id': component.id, 'lot_id': serial_number.id, 'qty_done': 1}, + {'product_id': self.bom_4.product_id.id, 'lot_id': False, 'quantity': 1}, + {'product_id': component.id, 'lot_id': serial_number.id, 'quantity': 1}, ]) def test_generate_serial_button(self): diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py index 1bb80accba4..de35232a608 100644 --- a/addons/mrp/tests/test_unbuild.py +++ b/addons/mrp/tests/test_unbuild.py @@ -181,9 +181,10 @@ class TestUnbuild(TestMrpCommon): details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = lot - ml.qty_done = 20 + ml.quantity = 20 details_operation_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. @@ -272,13 +273,14 @@ class TestUnbuild(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 5 + ml.quantity = 5 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 20 + ml.quantity = 20 details_operation_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. @@ -371,15 +373,8 @@ class TestUnbuild(TestMrpCommon): mo_form = Form(mo) mo_form.qty_producing = 5.0 mo = mo_form.save() - details_operation_form = Form(mo.move_raw_ids.filtered(lambda ml: ml.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = ml.reserved_uom_qty - with details_operation_form.move_line_ids.edit(1) as ml: - ml.qty_done = ml.reserved_uom_qty - with details_operation_form.move_line_ids.edit(2) as ml: - ml.qty_done = ml.reserved_uom_qty - details_operation_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. @@ -427,12 +422,13 @@ class TestUnbuild(TestMrpCommon): mo_form.qty_producing = 3.0 mo_form.lot_producing_id = lot_finished_1 mo = mo_form.save() - self.assertEqual(mo.move_raw_ids[1].quantity_done, 12) + self.assertEqual(mo.move_raw_ids[1].quantity, 12) details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) - with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 3 + with details_operation_form.move_line_ids.edit(0) as ml: + ml.quantity = 3 ml.lot_id = lot_1 details_operation_form.save() + mo.move_raw_ids.picked = True action = mo.button_mark_done() backorder = Form(self.env[action['res_model']].with_context(**action['context'])) backorder.save().action_backorder() @@ -460,16 +456,16 @@ class TestUnbuild(TestMrpCommon): mo = mo_form.save() details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: - ml.qty_done = 2 + ml.quantity = 2 ml.lot_id = lot_2 details_operation_form.save() action = mo.button_mark_done() mo1 = mo.procurement_group_id.mrp_production_ids[0] ml = mo1.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and lot_finished_1 in m.produce_line_ids.lot_id) - self.assertEqual(sum(ml.mapped('qty_done')), 12.0, 'Should have consumed 12 for the first lot') + self.assertEqual(sum(ml.mapped('quantity')), 12.0, 'Should have consumed 12 for the first lot') ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and lot_finished_2 in m.produce_line_ids.lot_id) - self.assertEqual(sum(ml.mapped('qty_done')), 8.0, 'Should have consumed 8 for the second lot') + self.assertEqual(sum(ml.mapped('quantity')), 8.0, 'Should have consumed 8 for the second lot') def test_unbuild_with_routes(self): """ This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock @@ -581,7 +577,7 @@ class TestUnbuild(TestMrpCommon): # Transfer it for ml in picking.move_ids_without_package: - ml.quantity_done = 1 + ml.write({'quantity': 1, 'picked': True}) picking._action_done() # Check the available quantity of components and final product in stock @@ -662,12 +658,13 @@ class TestUnbuild(TestMrpCommon): details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() + mo.move_raw_ids.picked = True mo.button_mark_done() uo_form = Form(self.env['mrp.unbuild']) @@ -708,7 +705,8 @@ class TestUnbuild(TestMrpCommon): internal_form.location_dest_id = subloc01 with internal_form.move_ids_without_package.new() as move: move.product_id = p_final - move.quantity_done = 1.0 + move.quantity = 1.0 + move.picked = True internal_transfer = internal_form.save() internal_transfer.button_validate() @@ -811,11 +809,12 @@ class TestUnbuild(TestMrpCommon): details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = product_1_sn - ml.qty_done = 1 + ml.quantity = 1 details_operation_form.save() mo_form = Form(mo2) mo_form.qty_producing = 1 mo2 = mo_form.save() + mo2.move_raw_ids.picked = True mo2.button_mark_done() self.assertEqual(mo2.state, 'done', "Production order should be in done state.") @@ -857,7 +856,7 @@ class TestUnbuild(TestMrpCommon): mo.action_confirm() mo.qty_producing = 1.0 mo.lot_producing_id = finished_product_sn - mo.move_raw_ids.quantity_done = 1 + mo.move_raw_ids.write({'quantity': 1, 'picked': True}) mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # unbuild order mo_1 @@ -886,7 +885,7 @@ class TestUnbuild(TestMrpCommon): mo_2.action_confirm() mo_2.qty_producing = 1.0 mo_2.lot_producing_id = finished_product_sn - mo_2.move_raw_ids.quantity_done = 1 + mo_2.move_raw_ids.write({'quantity': 1, 'picked': True}) mo_2.button_mark_done() self.assertEqual(mo_2.state, 'done', "Production order should be in done state.") # unbuild mo_2 diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py index 300ec39ba16..78dc3381cfb 100644 --- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py +++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py @@ -147,7 +147,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): 'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'), 'picking_type_id': self.warehouse.out_type_id.id, 'state': 'draft', - 'immediate_transfer': False, }) self.env['stock.move'].create({ @@ -189,7 +188,7 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): self.assertEqual(picking_stock_postprod.origin, 'SOURCEDOCUMENT', 'The post-prod origin should be the SO name') picking_stock_preprod.action_assign() - picking_stock_preprod.move_line_ids.qty_done = 4 + picking_stock_preprod.move_ids.write({'quantity': 4, 'picked': True}) picking_stock_preprod._action_done() self.assertFalse(sum(self.env['stock.quant']._gather(self.raw_product, self.warehouse.lot_stock_id).mapped('quantity'))) @@ -230,7 +229,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon): 'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'), 'picking_type_id': self.warehouse.out_type_id.id, 'state': 'draft', - 'immediate_transfer': False, }) self.env['stock.move'].create({ 'name': self.finished_product.name, diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index 9367bef5bf9..8bb83f0c1c6 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -349,7 +349,8 @@ That being said, the best would be to have the byproducts feature working without relying on anything in the view, e.g. so the byproducts feature works with xmlrpc calls. --> - + + @@ -391,22 +392,23 @@ - -