diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py
index 6c56fa22275..30a6fb49c44 100644
--- a/addons/mrp/tests/test_order.py
+++ b/addons/mrp/tests/test_order.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.tests import Form
from datetime import datetime, timedelta
from odoo.fields import Datetime as Dt
@@ -140,12 +141,12 @@ class TestMrpOrder(TestMrpCommon):
# #self.assertEqual(self.mrp_production_test1.availability, 'assigned', 'Production order availability should be set as available')
# produce product
- produce_wizard = self.env['mrp.product.produce'].sudo(self.user_mrp_user).with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': man_order.id,
'active_ids': [man_order.id],
- }).create({
- 'product_qty': 1.0,
- })
+ }))
+ produce_form.product_qty = 1.0
+ produce_wizard = produce_form.save()
produce_wizard.do_produce()
# man_order.button_mark_done()
@@ -412,7 +413,9 @@ class TestMrpOrder(TestMrpCommon):
# produce one item, call `post_inventory`
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
- custom_laptop_produce = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 1.00})
+ produce_form = Form(self.env['mrp.product.produce'].with_context(context))
+ produce_form.product_qty = 1.00
+ custom_laptop_produce = produce_form.save()
custom_laptop_produce.do_produce()
mo_custom_laptop.post_inventory()
@@ -423,7 +426,9 @@ class TestMrpOrder(TestMrpCommon):
# produce the second item, call `post_inventory`
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
- custom_laptop_produce = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 1.00})
+ produce_form = Form(self.env['mrp.product.produce'].with_context(context))
+ produce_form.product_qty = 1.00
+ custom_laptop_produce = produce_form.save()
custom_laptop_produce.do_produce()
mo_custom_laptop.post_inventory()
@@ -449,12 +454,12 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(production.move_raw_ids[1].product_qty, 84, 'The quantity should be rounded up')
# produce product
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': production.id,
'active_ids': [production.id],
- }).create({
- 'product_qty': 8,
- })
+ }))
+ produce_form.product_qty = 8
+ produce_wizard = produce_form.save()
produce_wizard.do_produce()
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')
@@ -470,12 +475,14 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign()
- product_produce = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({})
+ }))
+ product_produce = produce_form.save()
+ product_produce.do_produce()
- self.assertEqual(len(product_produce.produce_line_ids), 0, 'You should not have any produce lines since the consumed products are not tracked.')
+ self.assertEqual(len(product_produce.produce_line_ids), 2, 'You should have produce lines even the consumed products are not tracked.')
def test_product_produce_2(self):
""" Check that line are created when the consumed products are
@@ -498,24 +505,27 @@ class TestMrpOrder(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()
- product_produce = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({})
+ }))
+ product_produce = produce_form.save()
- self.assertEqual(len(product_produce.produce_line_ids), 2, 'You should have 2 produce lines. One for each serial to consume')
+ self.assertEqual(len(product_produce.produce_line_ids), 3, 'You should have 3 produce lines. One for each serial to consume')
product_produce.product_qty = 1
produce_line_1 = product_produce.produce_line_ids[0]
produce_line_1.qty_done = 1
remaining_lot = (lot_p1_1 | lot_p1_2) - produce_line_1.lot_id
product_produce.do_produce()
- product_produce = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({})
- self.assertEqual(len(product_produce.produce_line_ids), 1, 'You should have 1 produce lines since one has already be consumed.')
- self.assertEqual(product_produce.produce_line_ids[0].lot_id, remaining_lot, 'Wrong lot proposed.')
+ }))
+ product_produce = produce_form.save()
+ self.assertEqual(len(product_produce.produce_line_ids), 2, 'You should have 2 produce lines since one has already be consumed.')
+ for line in product_produce.produce_line_ids.filtered(lambda x: x.lot_id):
+ self.assertEqual(line.lot_id, remaining_lot, 'Wrong lot proposed.')
def test_product_produce_3(self):
""" Check that line are created when the consumed products are
@@ -546,17 +556,17 @@ class TestMrpOrder(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()
- product_produce = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 1.0,
- 'lot_id': final_product_lot.id,
- })
+ }))
+ produce_form.product_qty = 1.0
+ product_produce = produce_form.save()
+ product_produce.lot_id = final_product_lot.id
# product 1 lot 1 shelf1
# product 1 lot 1 shelf2
# product 1 lot 2
- self.assertEqual(len(product_produce.produce_line_ids), 3, 'You should have 3 produce lines. lot 1 shelf_1, lot 1 shelf_2, lot2')
+ self.assertEqual(len(product_produce.produce_line_ids), 4, 'You should have 4 produce lines. lot 1 shelf_1, lot 1 shelf_2, lot2 and for product which have tracking None')
for produce_line in product_produce.produce_line_ids:
produce_line.qty_done = produce_line.qty_to_consume + 1
@@ -578,3 +588,47 @@ class TestMrpOrder(TestMrpCommon):
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
+
+ def test_product_produce_uom(self):
+ hdd_on_demand = self.env.ref('mrp.product_product_19')
+ bom = self.env.ref('mrp.mrp_bom_3')
+ dozen = self.env.ref('uom.product_uom_dozen')
+ unit = self.env.ref('uom.product_uom_unit')
+
+ hdd_on_demand.tracking = 'serial'
+
+ mo = self.env['mrp.production'].create({
+ 'name': 'A dozen pls',
+ 'product_id': hdd_on_demand.id,
+ 'product_uom_id': dozen.id,
+ 'product_qty': 1,
+ 'bom_id': bom.id,
+ })
+
+ final_product_lot = self.env['stock.production.lot'].create({
+ 'name': 'lot1',
+ 'product_id': hdd_on_demand.id,
+ })
+
+ mo.action_assign()
+ self.assertEqual(mo.move_raw_ids.product_qty, 12, '12 units should be reserved.')
+
+ # produce product
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
+ 'active_id': mo.id,
+ 'active_ids': [mo.id],
+ }))
+ produce_form.lot_id = final_product_lot
+ product_produce = produce_form.save()
+ self.assertEqual(product_produce.product_qty, 1)
+ self.assertEqual(product_produce.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
+ product_produce.lot_id = final_product_lot.id
+
+ product_produce.do_produce()
+ 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)
+ self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
+
+ 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)
+ self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py
index 16c6dbe4a32..859bdf16312 100644
--- a/addons/mrp/tests/test_procurement.py
+++ b/addons/mrp/tests/test_procurement.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.tests import Form
from odoo.addons.mrp.tests.common import TestMrpCommon
from odoo.exceptions import UserError
@@ -65,13 +66,13 @@ class TestProcurement(TestMrpCommon):
# produce product4
# ---------------
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': produce_product_4.id,
'active_ids': [produce_product_4.id],
- }).create({
- 'product_qty': produce_product_4.product_qty,
- })
- produce_wizard.do_produce()
+ }))
+ produce_form.product_qty = produce_product_4.product_qty
+ product_produce = produce_form.save()
+ product_produce.do_produce()
produce_product_4.post_inventory()
# Check procurement and Production state for product 4.
produce_product_4.button_mark_done()
@@ -91,14 +92,13 @@ class TestProcurement(TestMrpCommon):
# ------------------------------------
self.assertEqual(production_product_6.availability, 'assigned', "Consume material not available")
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': production_product_6.id,
'active_ids': [production_product_6.id],
- }).create({
- 'product_qty': production_product_6.product_qty,
- })
- produce_wizard.do_produce()
-
+ }))
+ produce_form.product_qty = production_product_6.product_qty
+ product_produce = produce_form.save()
+ product_produce.do_produce()
production_product_6.post_inventory()
# Check procurement and Production state for product 6.
production_product_6.button_mark_done()
diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py
index 0c98b64b386..505e47c4b13 100644
--- a/addons/mrp/tests/test_unbuild.py
+++ b/addons/mrp/tests/test_unbuild.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.tests import Form
from odoo.addons.mrp.tests.common import TestMrpCommon
from odoo.exceptions import UserError
@@ -23,12 +24,12 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 5.0,
- })
+ }))
+ produce_form.product_qty = 5.0
+ produce_wizard = produce_form.save()
produce_wizard.do_produce()
mo.button_mark_done()
@@ -95,13 +96,14 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 5.0,
- 'lot_id': lot.id,
- })
+ }))
+ produce_form.product_qty = 5.0
+ produce_form.lot_id = lot
+ produce_wizard = produce_form.save()
+
produce_wizard.do_produce()
mo.button_mark_done()
@@ -176,17 +178,16 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
mo.action_assign()
for ml in mo.move_raw_ids.mapped('move_line_ids'):
- if ml.product_id.tracking != 'none':
- ml.qty_done = ml.product_qty
if ml.product_id.tracking != 'none':
self.assertEqual(ml.lot_id, lot, 'Wrong reserved lot.')
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 5.0,
- })
+ }))
+ produce_form.product_qty = 5.0
+ produce_wizard = produce_form.save()
+
produce_wizard.do_produce()
mo.button_mark_done()
@@ -270,15 +271,14 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5, lot_id=lot_2)
mo.action_assign()
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 5.0,
- 'lot_id': lot_final.id,
- })
- for pl in produce_wizard.produce_line_ids:
- pl.qty_done = pl.qty_to_consume
+ }))
+ produce_form.product_qty = 5.0
+ produce_form.lot_id = lot_final
+ produce_wizard = produce_form.save()
+
produce_wizard.do_produce()
mo.button_mark_done()
@@ -367,15 +367,14 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot_2)
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 2, lot_id=lot_3)
mo.action_assign()
- for ml in mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.product_id.tracking != 'none'):
- ml.qty_done = ml.product_qty
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 5.0,
- })
+ }))
+ produce_form.product_qty = 5.0
+ produce_wizard = produce_form.save()
+
produce_wizard.do_produce()
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -416,15 +415,14 @@ class TestUnbuild(TestMrpCommon):
'product_id': p_final.id,
})
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 3.0,
- 'lot_id': lot_finished_1.id,
- })
-
- produce_wizard.produce_line_ids[0].lot_id = lot_1.id
+ }))
+ produce_form.product_qty = 3.0
+ produce_form.lot_id = lot_finished_1
+ produce_wizard = produce_form.save()
+ produce_wizard.produce_line_ids[0].lot_id = lot_1
produce_wizard.do_produce()
lot_2 = self.env['stock.production.lot'].create({
@@ -438,18 +436,18 @@ class TestUnbuild(TestMrpCommon):
'product_id': p_final.id,
})
- produce_wizard = self.env['mrp.product.produce'].with_context({
+ produce_form = Form(self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
'active_ids': [mo.id],
- }).create({
- 'product_qty': 2.0,
- 'lot_id': lot_finished_2.id,
- })
-
- produce_wizard.produce_line_ids[0].lot_id = lot_2.id
+ }))
+ produce_form.product_qty = 2.0
+ produce_form.lot_id = lot_finished_2
+
+ produce_wizard = produce_form.save()
+ produce_wizard.produce_line_ids[0].lot_id = lot_2
produce_wizard.do_produce()
mo.button_mark_done()
ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and m.lot_produced_id == lot_finished_1)
- self.assertEqual(ml.qty_done, 12.0, 'Should have consumed 12 for the first lot')
+ self.assertEqual(ml[0].qty_done, 12.0, 'Should have consumed 12 for the first lot')
ml = mo.finished_move_line_ids[1].consume_line_ids.filtered(lambda m: m.product_id == p1 and m.lot_produced_id == lot_finished_2)
- self.assertEqual(ml.qty_done, 8.0, 'Should have consumed 8 for the second lot')
\ No newline at end of file
+ self.assertEqual(ml[0].qty_done, 8.0, 'Should have consumed 8 for the second lot')
\ No newline at end of file
diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py
index 2e62315d83c..d137356b11a 100644
--- a/addons/mrp/tests/test_workorder_operation.py
+++ b/addons/mrp/tests/test_workorder_operation.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
+from odoo.tests import Form
from odoo.tests import common
@@ -329,10 +330,12 @@ class TestWorkOrderProcess(common.TransactionCase):
# Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger)
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
- product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 6.00})
+ product_form = Form(self.env['mrp.product.produce'].with_context(context))
+ product_form.product_qty = 6.00
laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- product_consume.lot_id = laptop_lot_001.id
- product_consume.produce_line_ids.write({'qty_done': 12})
+ product_form.lot_id = laptop_lot_001
+ product_consume = product_form.save()
+ product_consume.produce_line_ids[0].qty_done = 12
product_consume.do_produce()
# Check consumed move after produce 6 quantity of customized laptop.
@@ -354,11 +357,13 @@ class TestWorkOrderProcess(common.TransactionCase):
# Produce 4 Unit of custom laptop will consume ( 8 Unit of keybord and 8 Unit of charger).
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
- product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 4.00})
+ produce_form = Form(self.env['mrp.product.produce'].with_context(context))
+ produce_form.product_qty = 4.00
laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- product_consume.lot_id = laptop_lot_002.id
+ produce_form.lot_id = laptop_lot_002
+ product_consume = produce_form.save()
self.assertEquals(len(product_consume.produce_line_ids), 2)
- product_consume.produce_line_ids.write({'qty_done': 8})
+ product_consume.produce_line_ids[0].qty_done = 8
product_consume.do_produce()
charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done')
keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done')
@@ -489,9 +494,11 @@ class TestWorkOrderProcess(common.TransactionCase):
mo_custom_product.action_assign()
context = {"active_ids": [mo_custom_product.id], "active_id": mo_custom_product.id}
- product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 10})
+ produce_form = Form(self.env['mrp.product.produce'].with_context(context))
+ produce_form.product_qty = 10.00
+ produce_form.lot_id = lot_a
+ product_consume = produce_form.save()
# laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- product_consume.lot_id = lot_a.id
self.assertEquals(len(product_consume.produce_line_ids), 2)
product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000})
product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20})
diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml
index 2b88ce333e6..4292b50b88a 100644
--- a/addons/mrp/views/mrp_production_views.xml
+++ b/addons/mrp/views/mrp_production_views.xml
@@ -126,7 +126,7 @@
-
+
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index 24415fa90b9..afcfb7a78c1 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from collections import Counter
from datetime import datetime
from odoo import api, fields, models, _
from odoo.addons import decimal_precision as dp
-from odoo.exceptions import UserError, ValidationError
+from odoo.exceptions import UserError
from odoo.tools import float_compare, float_round
class MrpProductProduce(models.TransientModel):
@@ -19,8 +18,11 @@ class MrpProductProduce(models.TransientModel):
if self._context and self._context.get('active_id'):
production = self.env['mrp.production'].browse(self._context['active_id'])
serial_finished = (production.product_id.tracking == 'serial')
+ todo_uom = production.product_uom_id.id
if serial_finished:
todo_quantity = 1.0
+ if production.product_uom_id.uom_type != 'reference':
+ todo_uom = self.env['uom.uom'].search([('category_id', '=', production.product_uom_id.category_id.id), ('uom_type', '=', 'reference')]).id
else:
main_product_moves = production.move_finished_ids.filtered(lambda x: x.product_id.id == production.product_id.id)
todo_quantity = production.product_qty - sum(main_product_moves.mapped('quantity_done'))
@@ -30,52 +32,11 @@ class MrpProductProduce(models.TransientModel):
if 'product_id' in fields:
res['product_id'] = production.product_id.id
if 'product_uom_id' in fields:
- res['product_uom_id'] = production.product_uom_id.id
+ res['product_uom_id'] = todo_uom
if 'serial' in fields:
res['serial'] = bool(serial_finished)
if 'product_qty' in fields:
res['product_qty'] = todo_quantity
- if 'produce_line_ids' in fields:
- lines = []
- for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel') and x.bom_line_id):
- qty_to_consume = float_round(todo_quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty,
- precision_rounding=move.product_uom.rounding, rounding_method="UP")
- for move_line in move.move_line_ids:
- if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
- break
- if move_line.lot_produced_id or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
- continue
- to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': to_consume_in_line,
- 'qty_done': 0.0,
- 'lot_id': move_line.lot_id.id,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- })
- qty_to_consume -= to_consume_in_line
- if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
- if move.product_id.tracking == 'serial':
- while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': 1,
- 'qty_done': 0.0,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- })
- qty_to_consume -= 1
- else:
- lines.append({
- 'move_id': move.id,
- 'qty_to_consume': qty_to_consume,
- 'qty_done': 0.0,
- 'product_uom_id': move.product_uom.id,
- 'product_id': move.product_id.id,
- })
-
- res['produce_line_ids'] = [(0, 0, x) for x in lines]
return res
serial = fields.Boolean('Requires Serial')
@@ -93,15 +54,6 @@ class MrpProductProduce(models.TransientModel):
quantity = self.product_qty
if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
raise UserError(_("The production order for '%s' has no quantity specified.") % self.product_id.display_name)
- for move in self.production_id.move_raw_ids:
- # TODO currently not possible to guess if the user updated quantity by hand or automatically by the produce wizard.
- if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel') and move.unit_factor:
- rounding = move.product_uom.rounding
- if self.product_id.tracking != 'none':
- qty_to_add = float_round(quantity * move.unit_factor, precision_rounding=rounding)
- move._generate_consumed_move_line(qty_to_add, self.lot_id)
- else:
- move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
for move in self.production_id.move_finished_ids:
if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel'):
rounding = move.product_uom.rounding
@@ -128,15 +80,16 @@ class MrpProductProduce(models.TransientModel):
if existing_move_line:
if self.product_id.tracking == 'serial':
raise UserError(_('You cannot produce the same serial number twice.'))
- existing_move_line.product_uom_qty += self.product_qty
- existing_move_line.qty_done += self.product_qty
+ produced_qty = self.product_uom_id._compute_quantity(self.product_qty, existing_move_line.product_uom_id)
+ existing_move_line.product_uom_qty += produced_qty
+ existing_move_line.qty_done += produced_qty
else:
vals = {
'move_id': produce_move.id,
'product_id': produce_move.product_id.id,
'production_id': self.production_id.id,
'product_uom_qty': self.product_qty,
- 'product_uom_id': produce_move.product_uom.id,
+ 'product_uom_id': self.product_uom_id.id,
'qty_done': self.product_qty,
'lot_id': self.lot_id.id,
'location_id': produce_move.location_id.id,
@@ -146,11 +99,11 @@ class MrpProductProduce(models.TransientModel):
for pl in self.produce_line_ids:
if pl.qty_done:
- if not pl.lot_id:
- raise UserError(_('Please enter a lot or serial number for %s !' % pl.product_id.name))
+ if pl.product_id.tracking != 'none' and not pl.lot_id:
+ raise UserError(_('Please enter a lot or serial number for %s !' % pl.product_id.display_name))
if not pl.move_id:
# Find move_id that would match
- move_id = self.production_id.move_raw_ids.filtered(lambda x: x.product_id == pl.product_id and x.state not in ('done', 'cancel'))
+ move_id = self.production_id.move_raw_ids.filtered(lambda m: m.product_id == pl.product_id and m.state not in ('done', 'cancel'))
if move_id:
pl.move_id = move_id
else:
@@ -169,6 +122,49 @@ class MrpProductProduce(models.TransientModel):
pl.move_id._generate_consumed_move_line(pl.qty_done, self.lot_id, lot=pl.lot_id)
return True
+ @api.onchange('product_qty')
+ def _onchange_product_qty(self):
+ lines = []
+ qty_todo = self.product_uom_id._compute_quantity(self.product_qty, self.production_id.product_uom_id, round=False)
+ for move in self.production_id.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.bom_line_id):
+ qty_to_consume = float_round(qty_todo * move.unit_factor, precision_rounding=move.product_uom.rounding)
+ for move_line in move.move_line_ids:
+ if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
+ break
+ if move_line.lot_produced_id or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
+ continue
+ to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
+ lines.append({
+ 'move_id': move.id,
+ 'qty_to_consume': to_consume_in_line,
+ 'qty_done': to_consume_in_line,
+ 'lot_id': move_line.lot_id.id,
+ 'product_uom_id': move.product_uom.id,
+ 'product_id': move.product_id.id,
+ 'qty_reserved': min(to_consume_in_line, move_line.product_uom_qty),
+ })
+ qty_to_consume -= to_consume_in_line
+ if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
+ if move.product_id.tracking == 'serial':
+ while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
+ lines.append({
+ 'move_id': move.id,
+ 'qty_to_consume': 1,
+ 'qty_done': 1,
+ 'product_uom_id': move.product_uom.id,
+ 'product_id': move.product_id.id,
+ })
+ qty_to_consume -= 1
+ else:
+ lines.append({
+ 'move_id': move.id,
+ 'qty_to_consume': qty_to_consume,
+ 'qty_done': qty_to_consume,
+ 'product_uom_id': move.product_uom.id,
+ 'product_id': move.product_id.id,
+ })
+
+ self.produce_line_ids = [(0, 0, x) for x in lines]
class MrpProductProduceLine(models.TransientModel):
_name = "mrp.product.produce.line"
@@ -176,11 +172,13 @@ class MrpProductProduceLine(models.TransientModel):
product_produce_id = fields.Many2one('mrp.product.produce')
product_id = fields.Many2one('product.product', 'Product')
+ product_tracking = fields.Selection(related="product_id.tracking")
lot_id = fields.Many2one('stock.production.lot', 'Lot')
qty_to_consume = fields.Float('To Consume', digits=dp.get_precision('Product Unit of Measure'))
product_uom_id = fields.Many2one('uom.uom', 'Unit of Measure')
- qty_done = fields.Float('Done', digits=dp.get_precision('Product Unit of Measure'))
+ qty_done = fields.Float('Consumed')
move_id = fields.Many2one('stock.move')
+ qty_reserved = fields.Float('Reserved')
@api.onchange('lot_id')
def _onchange_lot_id(self):
@@ -198,7 +196,7 @@ class MrpProductProduceLine(models.TransientModel):
help him. This onchange will warn him if he set `qty_done` to a non-supported value.
"""
res = {}
- if self.product_id.tracking == 'serial':
+ if self.product_id.tracking == 'serial' and self.qty_done:
if float_compare(self.qty_done, 1.0, precision_rounding=self.move_id.product_id.uom_id.rounding) != 0:
message = _('You can only process 1.0 %s of products with unique serial number.') % self.product_id.uom_id.name
res['warning'] = {'title': _('Warning'), 'message': message}
diff --git a/addons/mrp/wizard/mrp_product_produce_views.xml b/addons/mrp/wizard/mrp_product_produce_views.xml
index fb9518918e8..f17e5dc9c0e 100644
--- a/addons/mrp/wizard/mrp_product_produce_views.xml
+++ b/addons/mrp/wizard/mrp_product_produce_views.xml
@@ -22,10 +22,12 @@
-
-
-
+
+
+
+
+
diff --git a/addons/mrp_byproduct/tests/test_mrp_byproduct.py b/addons/mrp_byproduct/tests/test_mrp_byproduct.py
index 7f930a79346..eedcbb18c7a 100644
--- a/addons/mrp_byproduct/tests/test_mrp_byproduct.py
+++ b/addons/mrp_byproduct/tests/test_mrp_byproduct.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.tests import Form
from odoo.tests import common
@@ -70,7 +71,9 @@ class TestMrpByProduct(common.TransactionCase):
# I consume and produce the production of products.
# I create record for selecting mode and quantity of products to produce.
- product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 2.00})
+ produce_form = Form(self.env['mrp.product.produce'].with_context(context))
+ produce_form.product_qty = 2.00
+ product_consume = produce_form.save()
# I finish the production order.
self.assertEqual(len(mnf_product_a.move_raw_ids), 1, "Wrong consume move on production order.")
product_consume.do_produce()
diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py
index 46e9bbe9d01..14a94657f83 100644
--- a/addons/sale_mrp/tests/test_sale_mrp_flow.py
+++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py
@@ -267,8 +267,12 @@ class TestSaleMrpFlow(common.TransactionCase):
# produce product D.
# ------------------
- produce_d = self.ProductProduce.with_context({'active_ids': [mnf_product_d.id], 'active_id': mnf_product_d.id}).create({
- 'product_qty': 20})
+ produce_form = Form(self.ProductProduce.with_context({
+ 'active_id': mnf_product_d.id,
+ 'active_ids': [mnf_product_d.id],
+ }))
+ produce_form.product_qty = 20
+ produce_d = produce_form.save()
# produce_d.on_change_qty()
produce_d.do_produce()
mnf_product_d.post_inventory()