[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) <arm@odoo.com>
This commit is contained in:
Arnold Moyaux
2023-10-25 18:10:54 +00:00
parent c43297435c
commit 7dda6bb927
201 changed files with 3300 additions and 4607 deletions
@@ -110,21 +110,21 @@
<tbody> <tbody>
<t t-set="total_value" t-value="0"/> <t t-set="total_value" t-value="0"/>
<t t-foreach="o.move_ids" t-as="move"> <t t-foreach="o.move_ids" t-as="move">
<t t-if="move.quantity_done"> <t t-if="move.quantity">
<tr> <tr>
<td> <td>
<span t-field="move.product_id"/> <span t-field="move.product_id"/>
</td> </td>
<td> <td>
<span t-field="move.quantity_done"/> <span t-field="move.quantity"/>
<span t-field="move.product_uom" groups="uom.group_uom"/> <span t-field="move.product_uom" groups="uom.group_uom"/>
</td> </td>
<td> <td>
<t t-if="move.sale_line_id"> <t t-if="move.sale_line_id">
<t t-set="lst_price" t-value="move.sale_line_id.price_unit * move.quantity_done"/> <t t-set="lst_price" t-value="move.sale_line_id.price_unit * move.quantity"/>
</t> </t>
<t t-else=""> <t t-else="">
<t t-set="lst_price" t-value="move.product_id.lst_price * move.product_uom._compute_quantity(move.quantity_done, move.product_id.uom_id)"/> <t t-set="lst_price" t-value="move.product_id.lst_price * move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id)"/>
</t> </t>
<span t-esc="lst_price" t-options='{"widget": "monetary", "display_currency": o.company_id.currency_id}'/> <span t-esc="lst_price" t-options='{"widget": "monetary", "display_currency": o.company_id.currency_id}'/>
<t t-set="total_value" t-value="total_value + lst_price"/> <t t-set="total_value" t-value="total_value + lst_price"/>
+5 -5
View File
@@ -72,7 +72,7 @@ class TestDDT(TestSaleCommon):
# deliver partially # deliver partially
pick = self.so.picking_ids 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_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process() wiz.process()
@@ -84,14 +84,14 @@ class TestDDT(TestSaleCommon):
# deliver partially # deliver partially
pickx1 = self.so.picking_ids.filtered(lambda p: p.state != 'done') 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_act = pickx1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process() wiz.process()
# and again # and again
pickx2 = self.so.picking_ids.filtered(lambda p: p.state != 'done') 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_act = pickx2.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process() wiz.process()
@@ -129,7 +129,7 @@ class TestDDT(TestSaleCommon):
# deliver partially # deliver partially
picking_1 = so.picking_ids 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_act = picking_1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process() wiz.process()
@@ -139,7 +139,7 @@ class TestDDT(TestSaleCommon):
invoice_1.action_post() invoice_1.action_post()
picking_2 = so.picking_ids.filtered(lambda p: p.state != 'done') 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() picking_2.button_validate()
invoice_2 = so._create_invoices() invoice_2 = so._create_invoices()
+1 -1
View File
@@ -143,7 +143,7 @@ class TestItEdiDDT(TestItEdi):
def _create_delivery(self, sale_order, qty=1): def _create_delivery(self, sale_order, qty=1):
""" Create a picking of a limited quantity and create a backorder """ """ Create a picking of a limited quantity and create a backorder """
pickings = sale_order.picking_ids.filtered(lambda picking: picking.state != 'done') 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() wizard_action = pickings.button_validate()
context = wizard_action['context'] context = wizard_action['context']
wizard = Form(self.env['stock.backorder.confirmation'].with_context(context)) wizard = Form(self.env['stock.backorder.confirmation'].with_context(context))
+1 -1
View File
@@ -718,7 +718,7 @@
<function model="stock.move" name="write"> <function model="stock.move" name="write">
<value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp.mrp_production_drawer').id)]).ids"/> <value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp.mrp_production_drawer').id)]).ids"/>
<value eval="{'quantity_done': 5}"/> <value eval="{'quantity': 5, 'picked': True}"/>
</function> </function>
<function model="mrp.production" name="button_mark_done"> <function model="mrp.production" name="button_mark_done">
+64 -58
View File
@@ -533,8 +533,8 @@ class MrpProduction(models.Model):
production.finished_move_line_ids = production.move_finished_ids.mapped('move_line_ids') production.finished_move_line_ids = production.move_finished_ids.mapped('move_line_ids')
@api.depends( @api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'move_raw_ids.state', 'move_raw_ids.quantity', 'move_finished_ids.state',
'workorder_ids.state', 'product_qty', 'qty_producing') 'workorder_ids.state', 'product_qty', 'qty_producing', 'move_raw_ids.picked')
def _compute_state(self): def _compute_state(self):
""" Compute the production state. This uses a similar process to stock """ Compute the production state. This uses a similar process to stock
picking, but has been adapted to support having no moves. This adaption picking, but has been adapted to support having no moves. This adaption
@@ -564,7 +564,7 @@ class MrpProduction(models.Model):
production.state = 'progress' production.state = 'progress'
elif production.product_uom_id and not float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding): elif production.product_uom_id and not float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding):
production.state = 'progress' 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' production.state = 'progress'
@api.depends('bom_id', 'product_id', 'product_qty', 'product_uom_id') @api.depends('bom_id', 'product_id', 'product_qty', 'product_uom_id')
@@ -633,16 +633,16 @@ class MrpProduction(models.Model):
def _compute_unreserve_visible(self): def _compute_unreserve_visible(self):
for order in self: for order in self:
already_reserved = order.state not in ('done', 'cancel') and order.mapped('move_raw_ids.move_line_ids') 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.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) 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): def _get_produced_qty(self):
for production in 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) 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 production.qty_produced = qty_produced
return True return True
@@ -890,7 +890,7 @@ class MrpProduction(models.Model):
if 'lot_producing_id' in vals: if 'lot_producing_id' in vals:
finished_move_lines.write({'lot_id': vals.get('lot_producing_id')}) finished_move_lines.write({'lot_id': vals.get('lot_producing_id')})
if 'qty_producing' in vals: 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'): 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')) new_date_start = fields.Datetime.to_datetime(vals.get('date_start'))
if not production.date_finished or new_date_start >= production.date_finished: 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 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 # 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. # 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) qty = move.product_uom._compute_quantity(qty * ratio, target_uom)
return (target_uom, qty) 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') 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: if qty_producing_uom != 1:
self.qty_producing = self.product_id.uom_id._compute_quantity(1, self.product_uom_id, rounding_method='HALF-UP') 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)): 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 continue
new_qty = float_round((self.qty_producing - self.qty_produced) * move.unit_factor, precision_rounding=move.product_uom.rounding) 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) 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): def _update_raw_moves(self, factor):
self.ensure_one() self.ensure_one()
@@ -1518,17 +1516,17 @@ class MrpProduction(models.Model):
done_qty_by_product = defaultdict(float) done_qty_by_product = defaultdict(float)
for move in order.move_raw_ids: 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 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)): 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, qty_done, 0.0)) issues.append((order, move.product_id, quantity, 0.0))
continue 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(): for product, qty_to_consume in expected_qty_by_product.items():
qty_done = done_qty_by_product.get(product, 0.0) quantity = done_qty_by_product.get(product, 0.0)
if float_compare(qty_to_consume, qty_done, precision_rounding=product.uom_id.rounding) != 0: if float_compare(qty_to_consume, quantity, precision_rounding=product.uom_id.rounding) != 0:
issues.append((order, product, qty_done, qty_to_consume)) issues.append((order, product, quantity, qty_to_consume))
return issues return issues
@@ -1632,15 +1630,17 @@ class MrpProduction(models.Model):
return True return True
def _post_inventory(self, cancel_backorder=False): 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: for move in self.move_raw_ids:
if move.state == 'done': if move.state == 'done':
moves_not_to_do.add(move.id) moves_not_to_do.add(move.id)
elif not move.picked:
moves_to_cancel.add(move.id)
elif move.state != 'cancel': elif move.state != 'cancel':
moves_to_do.add(move.id) 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_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) 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. # Create a dict to avoid calling filtered inside for loops.
moves_to_do_by_order = defaultdict(lambda: self.env['stock.move'], [ 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')) 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. # the finish move can already be completed by the workorder.
for move in finish_moves: for move in finish_moves:
if not move.quantity_done: move.quantity = float_round(order.qty_producing - order.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP')
move._set_quantity_done(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: if move.has_tracking != 'none' and order.lot_producing_id:
move.move_line_ids.lot_id = 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 # 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'): if workorder.state not in ('done', 'cancel'):
workorder.duration_expected = workorder._get_duration_expected() workorder.duration_expected = workorder._get_duration_expected()
if workorder.duration == 0.0: 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) workorder.duration_unit = round(workorder.duration / max(workorder.qty_produced, 1), 2)
order._cal_price(moves_to_do_by_order[order.id]) 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 = 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) moves_to_finish = moves_to_finish._action_done(cancel_backorder=cancel_backorder)
self.action_assign()
for order in self: for order in self:
consume_move_lines = moves_to_do_by_order[order.id].mapped('move_line_ids') 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)] 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. and a new backorder with product_qty=2.
:param bool cancel_remaining_qty: whether to cancel remaining quantities or generate :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. 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. 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, :return: mrp.production records in order of [orig_prod_1, backorder_prod_1,
backorder_prod_2, orig_prod_2, backorder_prod_2, etc.] backorder_prod_2, orig_prod_2, backorder_prod_2, etc.]
@@ -1796,6 +1795,7 @@ class MrpProduction(models.Model):
moves.append(move) moves.append(move)
backorder_moves = self.env['stock.move'].create(new_moves_vals) backorder_moves = self.env['stock.move'].create(new_moves_vals)
move_to_assign = backorder_moves
# Split `stock.move.line`s. 2 options for this: # Split `stock.move.line`s. 2 options for this:
# - do_unreserve -> action_assign # - do_unreserve -> action_assign
# - Split the reserved amounts manually # - Split the reserved amounts manually
@@ -1810,7 +1810,6 @@ class MrpProduction(models.Model):
assigned_moves = set() assigned_moves = set()
partially_assigned_moves = set() partially_assigned_moves = set()
move_lines_to_unlink = set() move_lines_to_unlink = set()
for initial_move, backorder_moves in move_to_backorder_moves.items(): for initial_move, backorder_moves in move_to_backorder_moves.items():
# Create `stock.move.line` for consumed but non-reserved components # 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: 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): for move in list(initial_move + backorder_moves - backorder_move_to_ignore):
new_ml_vals = dict( new_ml_vals = dict(
ml_vals, ml_vals,
qty_done=move.product_uom_qty, quantity=move.product_uom_qty,
move_id=move.id move_id=move.id
) )
move_lines_vals.append(new_ml_vals) move_lines_vals.append(new_ml_vals)
@@ -1828,23 +1827,27 @@ class MrpProduction(models.Model):
ml_by_move = [] ml_by_move = []
product_uom = initial_move.product_id.uom_id product_uom = initial_move.product_id.uom_id
for move_line in initial_move.move_line_ids: 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: if float_compare(available_qty, 0, precision_rounding=move_line.product_uom_id.rounding) <= 0:
continue continue
ml_by_move.append((available_qty, move_line, move_line.copy_data()[0])) 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) moves = list(initial_move | backorder_moves)
move = moves and moves.pop(0) move = moves and moves.pop(0)
move_qty_to_reserve = move.product_qty move_qty_to_reserve = move.product_qty
for index, (quantity, move_line, ml_vals) in enumerate(ml_by_move): 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) 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): if float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
continue 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 move_qty_to_reserve -= taken_qty
ml_by_move[index] = (quantity - taken_qty, move_line, ml_vals) 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 = min(move_qty_to_reserve, quantity)
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id) taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id)
if move == initial_move: if move == initial_move:
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty += taken_qty_uom move_line.quantity += taken_qty_uom
if set_consumed_qty:
move_line.qty_done += taken_qty_uom
elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding): elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
new_ml_vals = dict( new_ml_vals = dict(
ml_vals, ml_vals,
reserved_uom_qty=taken_qty_uom, quantity=taken_qty_uom,
move_id=move.id move_id=move.id
) )
if set_consumed_qty:
new_ml_vals['qty_done'] = taken_qty_uom
move_lines_vals.append(new_ml_vals) move_lines_vals.append(new_ml_vals)
quantity -= taken_qty quantity -= taken_qty
move_qty_to_reserve -= 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: if move and move_qty_to_reserve != move.product_qty:
partially_assigned_moves.add(move.id) partially_assigned_moves.add(move.id)
move_lines_to_unlink.update(initial_move.move_line_ids.filtered( move_lines_to_unlink.update(initial_move.move_line_ids.filtered(lambda ml: not ml.quantity).ids)
lambda ml: not ml.reserved_uom_qty and not ml.qty_done).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(assigned_moves).write({'state': 'assigned'})
self.env['stock.move'].browse(partially_assigned_moves).write({'state': 'partially_available'}) 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 # 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).write({'move_id': False})
self.env['stock.move.line'].browse(move_lines_to_unlink).unlink() 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'] workorders_to_cancel = self.env['mrp.workorder']
for production in self: for production in self:
@@ -1993,7 +1999,7 @@ class MrpProduction(models.Model):
} }
elif self.user_has_groups('mrp.group_mrp_reception_report'): 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) 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 lines:
if any(mo.show_allocation for mo in mos_to_show): if any(mo.show_allocation for mo in mos_to_show):
another_action = mos_to_show.action_view_reception_report() another_action = mos_to_show.action_view_reception_report()
@@ -2187,7 +2193,7 @@ class MrpProduction(models.Model):
if self.product_id.tracking != 'serial': if self.product_id.tracking != 'serial':
return return
if self.state == 'confirmed' and self.reservation_state != 'assigned': 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 = _("Make sure enough quantities of these components are reserved to do the production:\n")
message += "\n".join(component.name for component in missing_components) message += "\n".join(component.name for component in missing_components)
raise UserError(message) raise UserError(message)
@@ -2474,17 +2480,17 @@ class MrpProduction(models.Model):
if move.has_tracking != 'serial' or move.product_id == self.product_id: if move.has_tracking != 'serial' or move.product_id == self.product_id:
continue continue
for move_line in move.move_line_ids: 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 continue
if self._is_finished_sn_already_produced(move_line.lot_id, excluded_sml=move_line): 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', 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)) number=move_line.lot_id.name, product_name=move_line.product_id.name))
for move in self.move_raw_ids: for move in self.move_raw_ids:
if move.has_tracking != 'serial': if move.has_tracking != 'serial' or not move.picked:
continue continue
for move_line in move.move_line_ids: 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 continue
message = _('The serial number %(number)s used for component %(component)s has already been consumed', message = _('The serial number %(number)s used for component %(component)s has already been consumed',
number=move_line.lot_id.name, number=move_line.lot_id.name,
@@ -2494,7 +2500,7 @@ class MrpProduction(models.Model):
# Check presence of same sn in previous productions # Check presence of same sn in previous productions
duplicates = self.env['stock.move.line'].search_count([ duplicates = self.env['stock.move.line'].search_count([
('lot_id', '=', move_line.lot_id.id), ('lot_id', '=', move_line.lot_id.id),
('qty_done', '=', 1), ('quantity', '=', 1),
('state', '=', 'done'), ('state', '=', 'done'),
('location_dest_id.usage', '=', 'production'), ('location_dest_id.usage', '=', 'production'),
('production_id', '!=', False), ('production_id', '!=', False),
@@ -2517,7 +2523,7 @@ class MrpProduction(models.Model):
if not ((duplicates_returned or removed) and duplicates - duplicates_returned - removed + unremoved == 0): if not ((duplicates_returned or removed) and duplicates - duplicates_returned - removed + unremoved == 0):
raise UserError(message) raise UserError(message)
# Check presence of same sn in current production # 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: if duplicates:
raise UserError(message) raise UserError(message)
@@ -2527,7 +2533,7 @@ class MrpProduction(models.Model):
excluded_sml = excluded_sml or self.env['stock.move.line'] excluded_sml = excluded_sml or self.env['stock.move.line']
domain = [ domain = [
('lot_id', '=', lot.id), ('lot_id', '=', lot.id),
('qty_done', '=', 1), ('quantity', '=', 1),
('state', '=', 'done') ('state', '=', 'done')
] ]
co_prod_move_lines = self.move_finished_ids.move_line_ids - excluded_sml 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): if not ((duplicates_unbuild or removed) and duplicates - duplicates_unbuild - removed == 0):
return True return True
# Check presence of same sn in current production # 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) return bool(duplicates)
def _pre_action_split_merge_hook(self, merge=False, split=False): def _pre_action_split_merge_hook(self, merge=False, split=False):
@@ -2612,10 +2618,10 @@ class MrpProduction(models.Model):
continue continue
rounding = move.product_uom.rounding rounding = move.product_uom.rounding
if move.manual_consumption: 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 missing_lot_id_products += "\n - %s" % move.product_id.display_name
if missing_lot_id_products: 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) raise UserError(error_msg)
def _get_autoprint_done_report_actions(self): def _get_autoprint_done_report_actions(self):
+26 -26
View File
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # 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.exceptions import UserError
from odoo.tools import float_compare, float_round 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): class MrpUnbuild(models.Model):
_name = "mrp.unbuild" _name = "mrp.unbuild"
@@ -131,17 +131,19 @@ class MrpUnbuild(models.Model):
def action_unbuild(self): def action_unbuild(self):
self.ensure_one() self.ensure_one()
self._check_company() 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: if self.product_id.tracking != 'none' and not self.lot_id.id:
raise UserError(_('You should provide a lot number for the final product.')) raise UserError(_('You should provide a lot number for the final product.'))
if self.mo_id: if self.mo_id and self.mo_id.state != 'done':
if self.mo_id.state != 'done': raise UserError(_('You cannot unbuild a undone manufacturing order.'))
raise UserError(_('You cannot unbuild a undone manufacturing order.'))
consume_moves = self._generate_consume_moves() consume_moves = self._generate_consume_moves()
consume_moves._action_confirm() consume_moves._action_confirm()
produce_moves = self._generate_produce_moves() 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) finished_moves = consume_moves.filtered(lambda m: m.product_id == self.product_id)
consume_moves -= finished_moves 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.')) 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: for finished_move in finished_moves:
if finished_move.has_tracking != 'none': self.env['stock.move.line'].create({
self.env['stock.move.line'].create({ 'move_id': finished_move.id,
'move_id': finished_move.id, 'lot_id': self.lot_id.id,
'lot_id': self.lot_id.id, 'quantity': finished_move.product_uom_qty,
'qty_done': finished_move.product_uom_qty, 'product_id': finished_move.product_id.id,
'product_id': finished_move.product_id.id, 'product_uom_id': finished_move.product_uom.id,
'product_uom_id': finished_move.product_uom.id, 'location_id': finished_move.location_id.id,
'location_id': finished_move.location_id.id, 'location_dest_id': finished_move.location_dest_id.id,
'location_dest_id': finished_move.location_dest_id.id, })
})
else:
finished_move.quantity_done = finished_move.product_uom_qty
# 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 # 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) 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 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: for move_line in moves_lines:
# Iterate over all move_lines until we unbuilded the correct quantity. # 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: if taken_quantity:
self.env['stock.move.line'].create({ self.env['stock.move.line'].create({
'move_id': move.id, 'move_id': move.id,
'lot_id': move_line.lot_id.id, 'lot_id': move_line.lot_id.id,
'qty_done': taken_quantity, 'quantity': taken_quantity,
'product_id': move.product_id.id, 'product_id': move.product_id.id,
'product_uom_id': move_line.product_uom_id.id, 'product_uom_id': move_line.product_uom_id.id,
'location_id': move.location_id.id, 'location_id': move.location_id.id,
@@ -192,12 +191,13 @@ class MrpUnbuild(models.Model):
needed_quantity -= taken_quantity needed_quantity -= taken_quantity
qty_already_used[move_line] += taken_quantity qty_already_used[move_line] += taken_quantity
else: 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() finished_moves._action_done()
consume_moves._action_done() consume_moves._action_done()
produce_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)]}) consume_moves.mapped('move_line_ids').write({'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]})
if self.mo_id: if self.mo_id:
unbuild_msg = _("%(qty)s %(measure)s unbuilt in %(order)s", 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_location_id': self.location_id.id,
'default_unbuild_id': self.id, 'default_unbuild_id': self.id,
'default_quantity': unbuild_qty, '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',
} }
+4 -7
View File
@@ -830,8 +830,8 @@ class MrpWorkorder(models.Model):
if move_line: if move_line:
if self.product_id.tracking == 'serial': if self.product_id.tracking == 'serial':
raise UserError(_('You cannot produce the same serial number twice.')) raise UserError(_('You cannot produce the same serial number twice.'))
move_line.reserved_uom_qty += self.qty_producing move_line.picked = True
move_line.qty_done += self.qty_producing move_line.quantity += self.qty_producing
else: else:
quantity = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP') 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) 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, 'move_id': production_move.id,
'product_id': production_move.product_id.id, 'product_id': production_move.product_id.id,
'lot_id': self.finished_lot_id.id, 'lot_id': self.finished_lot_id.id,
'reserved_uom_qty': self.qty_producing,
'product_uom_id': self.product_uom_id.id, '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_id': production_move.location_id.id,
'location_dest_id': putaway_location.id, 'location_dest_id': putaway_location.id,
}) })
else: else:
rounding = production_move.product_uom.rounding rounding = production_move.product_uom.rounding
production_move._set_quantity_done( production_move.quantity = float_round(self.qty_producing, precision_rounding=rounding)
float_round(self.qty_producing, precision_rounding=rounding)
)
def _should_start_timer(self): def _should_start_timer(self):
return True return True
+1 -1
View File
@@ -314,7 +314,7 @@ class ProductProduct(models.Model):
def _count_returned_sn_products(self, sn_lot): def _count_returned_sn_products(self, sn_lot):
res = self.env['stock.move.line'].search_count([ res = self.env['stock.move.line'].search_count([
('lot_id', '=', sn_lot.id), ('lot_id', '=', sn_lot.id),
('qty_done', '=', 1), ('quantity', '=', 1),
('state', '=', 'done'), ('state', '=', 'done'),
('production_id', '=', False), ('production_id', '=', False),
('location_id.usage', '=', 'production'), ('location_id.usage', '=', 'production'),
+16 -27
View File
@@ -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)) lines |= raw_moves_lines.filtered(lambda ml: ml.product_id == self.product_id and (ml.lot_id or ml.lot_name))
return lines 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): def write(self, vals):
for move_line in self: for move_line in self:
production = move_line.move_id.production_id or move_line.move_id.raw_material_production_id 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) move_line._log_message(production, move_line, 'mrp.track_production_move_template', vals)
return super(StockMoveLine, self).write(vals) return super(StockMoveLine, self).write(vals)
@@ -229,7 +220,8 @@ class StockMove(models.Model):
def _onchange_product_uom_qty(self): def _onchange_product_uom_qty(self):
if self.raw_material_production_id and self.has_tracking == 'none': if self.raw_material_production_id and self.has_tracking == 'none':
mo = self.raw_material_production_id 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 @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
@@ -362,12 +354,12 @@ class StockMove(models.Model):
moves_ids_to_return.add(move.id) moves_ids_to_return.add(move.id)
continue continue
if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding): 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: else:
factor = move.product_uom._compute_quantity(move.product_uom_qty, bom.product_uom_id) / bom.product_qty 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) boms, lines = bom.sudo().explode(move.product_id, factor, picking_type=bom.picking_type_id)
for bom_line, line_data in lines: 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']) phantom_moves_vals_list += move._generate_move_phantom(bom_line, 0, line_data['qty'])
else: else:
phantom_moves_vals_list += move._generate_move_phantom(bom_line, line_data['qty'], 0) 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) moves_ids_to_unlink.add(move.id)
move_to_unlink = self.env['stock.move'].browse(moves_ids_to_unlink).sudo() 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._action_cancel()
move_to_unlink.unlink() move_to_unlink.unlink()
if phantom_moves_vals_list: if phantom_moves_vals_list:
@@ -423,9 +415,9 @@ class StockMove(models.Model):
'product_id': bom_line.product_id.id, 'product_id': bom_line.product_id.id,
'product_uom': bom_line.product_uom_id.id, 'product_uom': bom_line.product_uom_id.id,
'product_uom_qty': product_qty, 'product_uom_qty': product_qty,
'quantity_done': quantity_done, 'quantity': quantity_done,
'state': 'draft', # will be confirmed below
'name': self.name, 'name': self.name,
'picked': self.picked,
'bom_line_id': bom_line.id, 'bom_line_id': bom_line.id,
} }
@@ -478,8 +470,6 @@ class StockMove(models.Model):
# Do not update extra product quantities # Do not update extra product quantities
if float_is_zero(self.product_uom_qty, precision_rounding=self.product_uom.rounding): if float_is_zero(self.product_uom_qty, precision_rounding=self.product_uom.rounding):
return True 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 return False
def _key_assign_picking(self): def _key_assign_picking(self):
@@ -542,15 +532,6 @@ class StockMove(models.Model):
else: else:
return 0.0 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): def _update_candidate_moves_list(self, candidate_moves_set):
super()._update_candidate_moves_list(candidate_moves_set) super()._update_candidate_moves_list(candidate_moves_set)
for production in self.mapped('raw_material_production_id'): 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): 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 \ 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) (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
+2 -1
View File
@@ -3,6 +3,7 @@
from odoo import _, api, fields, models from odoo import _, api, fields, models
class StockScrap(models.Model): class StockScrap(models.Model):
_inherit = 'stock.scrap' _inherit = 'stock.scrap'
@@ -55,7 +56,7 @@ class StockScrap(models.Model):
else: else:
return super()._onchange_serial_number() 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): def _compute_scrap_qty(self):
self.scrap_qty = 1 self.scrap_qty = 1
for scrap in self: for scrap in self:
@@ -115,7 +115,7 @@
</td> </td>
<td t-attf-class="{{ 'text-end' if not has_product_barcode else '' }}" t-if="o.state in ('progress', 'to_close','done')"> <td t-attf-class="{{ 'text-end' if not has_product_barcode else '' }}" t-if="o.state in ('progress', 'to_close','done')">
<div> <div>
<span t-field="raw_line.quantity_done"/> <span t-field="raw_line.quantity"/>
</div> </div>
</td> </td>
<td t-attf-class="{{ 'text-end' if not has_product_barcode else '' }}"> <td t-attf-class="{{ 'text-end' if not has_product_barcode else '' }}">
@@ -137,12 +137,12 @@
<t t-set='nRows' t-value='12'/> <t t-set='nRows' t-value='12'/>
<t t-set='nCols' t-value='4'/> <t t-set='nCols' t-value='4'/>
<t t-set="uom_categ_unit" t-value="env.ref('uom.product_uom_categ_unit')"/> <t t-set="uom_categ_unit" t-value="env.ref('uom.product_uom_categ_unit')"/>
<t t-set="move_lines" t-value="docs.move_finished_ids.move_line_ids.filtered(lambda ml: ml.move_id.production_id.state == 'done' and ml.state == 'done' and ml.qty_done)"/> <t t-set="move_lines" t-value="docs.move_finished_ids.move_line_ids.filtered(lambda ml: ml.move_id.production_id.state == 'done' and ml.state == 'done' and ml.quantity)"/>
<t t-set="move_line_qtys" t-value="[]"/> <t t-set="move_line_qtys" t-value="[]"/>
<t t-foreach="move_lines" t-as="move_line"> <t t-foreach="move_lines" t-as="move_line">
<t t-if="move_line.product_uom_id.category_id == uom_categ_unit"> <t t-if="move_line.product_uom_id.category_id == uom_categ_unit">
<!-- print 1 label per 1 uom when uom category = units --> <!-- print 1 label per 1 uom when uom category = units -->
<t t-set="move_line_qtys" t-value="move_line_qtys + [int(move_line.qty_done)]"/> <t t-set="move_line_qtys" t-value="move_line_qtys + [int(move_line.quantity)]"/>
</t> </t>
<t t-else=""> <t t-else="">
<t t-set="move_line_qtys" t-value="move_line_qtys + [1]"/> <t t-set="move_line_qtys" t-value="move_line_qtys + [1]"/>
@@ -173,7 +173,7 @@
<div t-out="move_line.product_id.display_name"/> <div t-out="move_line.product_id.display_name"/>
<div><span>Quantity: </span> <div><span>Quantity: </span>
<span t-if="move_line.product_uom_id.category_id == uom_categ_unit">1.0</span> <span t-if="move_line.product_uom_id.category_id == uom_categ_unit">1.0</span>
<span t-else="" t-out="move_line.qty_done"/> <span t-else="" t-out="move_line.quantity"/>
<span t-field="move_line.product_uom_id" groups="uom.group_uom"/> <span t-field="move_line.product_uom_id" groups="uom.group_uom"/>
</div> </div>
</div> </div>
+5 -5
View File
@@ -199,8 +199,8 @@ class ReportMoOverview(models.AbstractModel):
for move in record.move_raw_ids: for move in record.move_raw_ids:
if move.product_id.detailed_type != 'product': if move.product_id.detailed_type != 'product':
continue continue
components_qty_to_produce[move.product_id] += move.product_uom._compute_quantity(move.product_uom_qty, 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.reserved_availability, move.product_id.uom_id) components_qty_reserved[move.product_id] += move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id)
producible_qty = record.product_qty producible_qty = record.product_qty
for product_id, comp_qty_to_produce in components_qty_to_produce.items(): 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): 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): def _format_component_move(self, production, move_raw, replenishments, replenish_data, level, index):
currency = (production.company_id or self.env.company).currency_id currency = (production.company_id or self.env.company).currency_id
product = move_raw.product_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_mo_cost, dummy_real_cost = self._compute_cost_sums(replenishments)
replenish_quantity = sum(rep.get('summary', {}).get('quantity', 0.0) for rep in replenishments) replenish_quantity = sum(rep.get('summary', {}).get('quantity', 0.0) for rep in replenishments)
missing_quantity = quantity - replenish_quantity 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): def _get_replenishment_lines(self, production, move_raw, replenish_data, level, current_index):
product = move_raw.product_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
currency = (production.company_id or self.env.company).currency_id currency = (production.company_id or self.env.company).currency_id
forecast = replenish_data['products'][product.id].get('forecast', []) forecast = replenish_data['products'][product.id].get('forecast', [])
current_lines = filter(lambda line: line.get('document_in', False) and line.get('document_out', False) 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'): if move.state not in ('partially_available', 'assigned'):
continue continue
# count reserved stock in move_raw's uom # 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) # 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) 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 total_reserved += reserved
@@ -7,7 +7,7 @@
<t t-foreach="production.move_finished_ids" t-as="move"> <t t-foreach="production.move_finished_ids" t-as="move">
<t t-foreach="move.move_line_ids" t-as="move_line"> <t t-foreach="move.move_line_ids" t-as="move_line">
<t t-if="move_line.product_uom_id.category_id == uom_categ_unit"> <t t-if="move_line.product_uom_id.category_id == uom_categ_unit">
<t t-set="qty" t-value="int(move_line.qty_done)"/> <t t-set="qty" t-value="int(move_line.quantity)"/>
</t> </t>
<t t-else=""> <t t-else="">
<t t-set="qty" t-value="1"/> <t t-set="qty" t-value="1"/>
@@ -7,7 +7,7 @@ import { StockMoveX2ManyField, MovesListRenderer } from "@stock/views/picking_fo
export class MrpProductionComponentsListRenderer extends MovesListRenderer { export class MrpProductionComponentsListRenderer extends MovesListRenderer {
getCellClass(column, record) { getCellClass(column, record) {
let classNames = super.getCellClass(...arguments); 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'; classNames += ' o_non_manual_consumption';
} }
return classNames; return classNames;
@@ -7,15 +7,15 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
test: true, test: true,
steps: () => [ 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: () => {}, run: () => {},
}, },
{ {
trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last', trigger: 'div[name=move_raw_ids] td[name="quantity"]:last',
run: 'click', run: 'click',
}, },
{ {
trigger: 'div[name="quantity_done"] input', trigger: 'div[name="quantity"] input',
run: 'text 6.0' run: 'text 6.0'
}, },
{ {
@@ -31,7 +31,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
trigger: ".o_pager_value:first()", 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: () => {}, run: () => {},
}, },
{ {
@@ -56,7 +56,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
trigger: ".o_pager_value:first()", 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: () => {}, run: () => {},
}, },
{ {
+20 -20
View File
@@ -74,7 +74,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
self.assertEqual(mo_backorder.product_id.id, production.product_id.id) self.assertEqual(mo_backorder.product_id.id, production.product_id.id)
self.assertEqual(mo_backorder.product_qty, 3) 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(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): def test_no_tracking_pbm_1(self):
"""Create a MO for 4 product. Produce 1. The backorder button should """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(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) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, nb_product_todo, lot_id=lot_2)
production.action_assign()
active_production = production active_production = production
for i in range(nb_product_todo): 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4 ml.quantity = 4
ml.lot_id = lot_1 ml.lot_id = lot_1
details_operation_form.save() 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1 ml.quantity = 1
ml.lot_id = lot_2 ml.lot_id = lot_2
details_operation_form.save() details_operation_form.save()
@@ -215,6 +215,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
production_form.lot_producing_id = lot_final production_form.lot_producing_id = lot_final
active_production = production_form.save() active_production = production_form.save()
active_production.move_raw_ids.picked = True
active_production.button_mark_done() active_production.button_mark_done()
if i + 1 != nb_product_todo: # If last MO, don't make a backorder if i + 1 != nb_product_todo: # If last MO, don't make a backorder
action = active_production.button_mark_done() 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4 * 3 ml.quantity = 4 * 3
details_operation_form.save() details_operation_form.save()
# Consume 1 Product from lot1 and 2 from lot 2 # 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') 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1 ml.quantity = 1
ml.lot_id = lot1 ml.lot_id = lot1
with details_operation_form.move_line_ids.edit(1) as ml: with details_operation_form.move_line_ids.edit(1) as ml:
ml.qty_done = 2 ml.quantity = 2
ml.lot_id = lot2 ml.lot_id = lot2
details_operation_form.save() 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 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(len(p2_bo_mls), 1)
self.assertEqual(p2_bo_mls.lot_id, lot1) 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): def test_uom_backorder(self):
""" """
@@ -352,22 +353,20 @@ class TestMrpProductionBackorder(TestMrpCommon):
production.action_assign() production.action_assign()
active_production = production active_production = production
for i in range(nb_product_todo): 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 = Form(active_production)
production_form.qty_producing = 1 production_form.qty_producing = 1
production_form.lot_producing_id = serials_final[i] production_form.lot_producing_id = serials_final[i]
active_production = production_form.save() 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() active_production.button_mark_done()
if i + 1 != nb_product_todo: # If last MO, don't make a backorder if i + 1 != nb_product_todo: # If last MO, don't make a backorder
action = active_production.button_mark_done() action = active_production.button_mark_done()
@@ -556,6 +555,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
self.assertEqual(production.reserve_visible, False) self.assertEqual(production.reserve_visible, False)
backorder = produce_one(production) backorder = produce_one(production)
self.assertEqual(backorder.state, 'confirmed') self.assertEqual(backorder.state, 'confirmed')
# The backorder is re reserved depending on the picking type
self.assertEqual(backorder.reserve_visible, False) self.assertEqual(backorder.reserve_visible, False)
+1 -1
View File
@@ -1264,7 +1264,7 @@ class TestBoM(TestMrpCommon):
operation.duration_expected = 480 operation.duration_expected = 480
with mo_form.move_byproduct_ids.new() as by_product_line: with mo_form.move_byproduct_ids.new() as by_product_line:
by_product_line.product_id = by_product by_product_line.product_id = by_product
by_product_line.quantity_done = 6 by_product_line.quantity = 6
mo_3 = mo_form.save() mo_3 = mo_form.save()
bom_from_mo_3 = generate_bom_from_mo(mo_3) bom_from_mo_3 = generate_bom_from_mo(mo_3)
# Checks the MO's data. # Checks the MO's data.
+1 -6
View File
@@ -105,7 +105,7 @@ class TestMrpByProduct(common.TransactionCase):
mnf_product_a.action_confirm() mnf_product_a.action_confirm()
self.assertEqual(mnf_product_a.state, "confirmed") self.assertEqual(mnf_product_a.state, "confirmed")
mnf_product_a.move_raw_ids._action_assign() 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() mnf_product_a.move_raw_ids._action_done()
self.assertEqual(mnf_product_a.state, "progress") self.assertEqual(mnf_product_a.state, "progress")
mnf_product_a.qty_producing = 2 mnf_product_a.qty_producing = 2
@@ -357,8 +357,6 @@ class TestMrpByProduct(common.TransactionCase):
'production_id': mo.id, 'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'), 'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0
}) })
byproduct_2 = self.env['stock.move'].create({ byproduct_2 = self.env['stock.move'].create({
'name': 'By Product 2', 'name': 'By Product 2',
@@ -367,8 +365,6 @@ class TestMrpByProduct(common.TransactionCase):
'production_id': mo.id, 'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'), 'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0
}) })
# Update byproduct has cost share > 100% # Update byproduct has cost share > 100%
@@ -404,6 +400,5 @@ class TestMrpByProduct(common.TransactionCase):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo = mo_form.save() mo = mo_form.save()
self.assertEqual(mo.state, 'to_close') self.assertEqual(mo.state, 'to_close')
mo.move_byproduct_ids[0].quantity_done = 1
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
+16 -15
View File
@@ -1,7 +1,7 @@
import copy import copy
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests import common, tagged from odoo.tests import common, tagged, Form
class TestConsumeComponentCommon(common.TransactionCase): class TestConsumeComponentCommon(common.TransactionCase):
@@ -261,7 +261,7 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are fully reserved (stock.move state is available) # Quantities are fully reserved (stock.move state is available)
mo_all.action_assign() mo_all.action_assign()
for mov in mo_all.move_raw_ids: 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 # Test for Serial Product
self.executeConsumptionTriggers(mo_serial) self.executeConsumptionTriggers(mo_serial)
@@ -269,9 +269,9 @@ class TestConsumeComponent(TestConsumeComponentCommon):
self.executeConsumptionTriggers(mo_lot) self.executeConsumptionTriggers(mo_lot)
for mov in mo_all.move_raw_ids: for mov in mo_all.move_raw_ids:
if mov.has_tracking == 'none' or mov.raw_material_production_id.state == 'done': 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: 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): def test_option_enabled_and_qty_available(self):
"""Option enabled, qty available """Option enabled, qty available
@@ -296,13 +296,13 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are fully reserved (stock.move state is available) # Quantities are fully reserved (stock.move state is available)
mo_all.action_assign() mo_all.action_assign()
for mov in mo_all.move_raw_ids: 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_serial)
self.executeConsumptionTriggers(mo_none) self.executeConsumptionTriggers(mo_none)
self.executeConsumptionTriggers(mo_lot) self.executeConsumptionTriggers(mo_lot)
for mov in mo_all.move_raw_ids: 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): def test_option_enabled_and_qty_not_available(self):
"""Option enabled, qty not available """Option enabled, qty not available
@@ -320,7 +320,7 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are not reserved at all (stock.move state is confirmed) # Quantities are not reserved at all (stock.move state is confirmed)
mo_all.action_assign() mo_all.action_assign()
for mov in mo_all.move_raw_ids: 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_serial)
self.executeConsumptionTriggers(mo_none) self.executeConsumptionTriggers(mo_none)
@@ -328,9 +328,9 @@ class TestConsumeComponent(TestConsumeComponentCommon):
for mov in mo_all.move_raw_ids: for mov in mo_all.move_raw_ids:
if mov.has_tracking == 'none': 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: 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): def test_option_enabled_and_qty_partially_available(self):
"""Option enabled, qty partially available """Option enabled, qty partially available
@@ -360,23 +360,24 @@ class TestConsumeComponent(TestConsumeComponentCommon):
mo.action_assign() mo.action_assign()
for mov in mo.move_raw_ids: for mov in mo.move_raw_ids:
if mov.has_tracking == "none": 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: 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: if serialTrigger is None:
self.executeConsumptionTriggers(mo) self.executeConsumptionTriggers(mo)
elif serialTrigger == 1: elif serialTrigger == 1:
mo.qty_producing = mo.product_qty mo_form = Form(mo)
mo._onchange_producing() mo_form.qty_producing = mo_form.product_qty
mo = mo_form.save()
elif serialTrigger == 2: elif serialTrigger == 2:
mo.action_generate_serial() mo.action_generate_serial()
for mov in mo.move_raw_ids: for mov in mo.move_raw_ids:
if mov.has_tracking == "none": 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: 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() mo.action_cancel()
testUnit(self.mo_none_tmpl) testUnit(self.mo_none_tmpl)
+18 -14
View File
@@ -9,9 +9,9 @@ from odoo.tests import tagged, Form, HttpCase
class TestTourManualConsumption(HttpCase): class TestTourManualConsumption(HttpCase):
def test_mrp_manual_consumption(self): def test_mrp_manual_consumption(self):
"""Test manual consumption mechanism. Test when manual consumption is """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. 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 will be set to True. Also test when create backorder, the manual consumption
should be set according to the bom. should be set according to the bom.
""" """
@@ -58,22 +58,25 @@ class TestTourManualConsumption(HttpCase):
mo = mo_form.save() mo = mo_form.save()
move_nt, move_sn, move_lot = mo.move_raw_ids move_nt, move_sn, move_lot = mo.move_raw_ids
self.assertEqual(move_nt.manual_consumption, False) 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.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.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 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)) 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.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.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.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 backorder = mo.procurement_group_id.mrp_production_ids - mo
move_nt = backorder.move_raw_ids.filtered(lambda m: m.product_id == product_nt) 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 = Form(mo)
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo_form.save() 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 == c1.id).mapped("quantity")), 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 == 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4 ml.quantity = 4
ml.lot_id = lot ml.lot_id = lot
details_operation_form.save() details_operation_form.save()
@@ -152,8 +156,8 @@ class TestManualConsumption(TestMrpCommon):
mo_form = Form(mo) mo_form = Form(mo)
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo_form.save() 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 == c1.id).mapped("quantity")), 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 == c2.id).mapped("quantity")), 1)
action = mo.button_mark_done() action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+2 -1
View File
@@ -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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = lot_b ml.lot_id = lot_b
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids.picked = True
with self.assertRaises(UserError): with self.assertRaises(UserError):
mo.button_mark_done() mo.button_mark_done()
+140 -167
View File
@@ -182,24 +182,24 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 11 ml.quantity = 11
details_operation_form.save() details_operation_form.save()
self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_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[0].move_line_ids.mapped('quantity'), [2])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [11]) self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [11])
self.assertEqual(mo.move_raw_ids[0].quantity_done, 2) self.assertEqual(mo.move_raw_ids[0].quantity, 2)
self.assertEqual(mo.move_raw_ids[1].quantity_done, 11) self.assertEqual(mo.move_raw_ids[1].quantity, 11)
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_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('quantity'), [2, 11])
self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.qty_done'), [2, 11]) self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.quantity'), [2, 11])
def test_under_consumption(self): def test_under_consumption(self):
""" Consume less component quantity than the initial demand. """ Consume less component quantity than the initial demand.
@@ -218,26 +218,26 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_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[0].move_line_ids.mapped('quantity'), [0])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [5]) self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [5])
self.assertEqual(mo.move_raw_ids[0].quantity_done, 0) self.assertEqual(mo.move_raw_ids[0].quantity, 0)
self.assertEqual(mo.move_raw_ids[1].quantity_done, 5) self.assertEqual(mo.move_raw_ids[1].quantity, 5)
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(len(mo.move_raw_ids), 2) self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 1) 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('product_uom_qty'), [1, 10])
self.assertEqual(mo.move_raw_ids.mapped('state'), ['cancel', 'done']) 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): def test_update_quantity_1(self):
""" Build 5 final products with different consumed lots, """ Build 5 final products with different consumed lots,
@@ -271,17 +271,18 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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.lot_id = lot_1
ml.qty_done = 20 ml.quantity = 20
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids[1].picked = True
update_quantity_wizard = self.env['change.production.qty'].create({ update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id, 'mo_id': mo.id,
'product_qty': 4, 'product_qty': 4,
}) })
update_quantity_wizard.change_prod_qty() 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) self.assertEqual(mo.move_finished_ids.product_uom_qty, 4)
mo.button_mark_done() mo.button_mark_done()
@@ -303,8 +304,6 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 2 mo_form.qty_producing = 2
mo = mo_form.save() mo = mo_form.save()
mo._post_inventory()
update_quantity_wizard = self.env['change.production.qty'].create({ update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id, 'mo_id': mo.id,
'product_qty': 5, 'product_qty': 5,
@@ -315,8 +314,8 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
mo.button_mark_done() 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_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity')), 20)
self.assertEqual(sum(mo.move_finished_ids.mapped('quantity_done')), 5) self.assertEqual(sum(mo.move_finished_ids.mapped('quantity')), 5)
def test_update_quantity_3(self): def test_update_quantity_3(self):
bom = self.env['mrp.bom'].create({ bom = self.env['mrp.bom'].create({
@@ -536,8 +535,8 @@ class TestMrpOrder(TestMrpCommon):
mo_form = Form(production) mo_form = Form(production)
mo_form.qty_producing = 8 mo_form.qty_producing = 8
production = mo_form.save() 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[0].quantity, 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[1].quantity, 34, 'Should use half-up rounding when producing')
def test_product_produce_1(self): def test_product_produce_1(self):
""" Checks the production wizard contains lines even for untracked products. """ """ Checks the production wizard contains lines even for untracked products. """
@@ -553,7 +552,7 @@ class TestMrpOrder(TestMrpCommon):
# change the quantity done in one line # 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')) 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: 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.save()
# change the quantity producing # change the quantity producing
@@ -562,9 +561,9 @@ class TestMrpOrder(TestMrpCommon):
# check than all quantities are update correctly # 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]['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]['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): def test_product_produce_2(self):
""" Checks that, for a BOM where one of the components is tracked by serial number and the """ 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 # 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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
consumed_lots = ml.lot_id consumed_lots = ml.lot_id
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
remaining_lot = (lot_p1_1 | lot_p1_2) - consumed_lots remaining_lot = (lot_p1_1 | lot_p1_2) - consumed_lots
@@ -668,28 +667,22 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
# p2 # p2
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.new() as line:
line.qty_done = 1 line.quantity = 1
details_operation_form.save() details_operation_form.save()
# p1 # p1
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.new() as line:
line.qty_done = 2 line.quantity = 2
line.lot_id = first_lot_for_p1 line.lot_id = first_lot_for_p1
with details_operation_form.move_line_ids.new() as line: with details_operation_form.move_line_ids.new() as line:
line.qty_done = 1 line.quantity = 1
line.lot_id = second_lot_for_p1 line.lot_id = second_lot_for_p1
details_operation_form.save() details_operation_form.save()
move_1 = mo.move_raw_ids.filtered(lambda m: m.product_id == p1) 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 # 3/3 lot 1 shelf 1
# 1/1 lot 1 shelf 2 # 1/1 lot 1 shelf 2
# 2/2 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_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) 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_1.mapped('quantity')), 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(sum(ml_to_shelf_2.mapped('quantity')), 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(move_1.quantity, 13, 'You should have used the tem units.')
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") 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_p1), 2)
self.assertEqual(len(ml_p2), 1) 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! # Produce baby!
mo_form = Form(mo) mo_form = Form(mo)
mo_form.qty_producing = 1 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) m_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1)
ml_p1 = m_p1.mapped('move_line_ids') ml_p1 = m_p1.mapped('move_line_ids')
self.assertEqual(len(ml_p1), 2) 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(sorted(ml_p1.mapped('quantity')), [2.0, 3.0], 'Quantity should be 2.0 and 3.0')
self.assertEqual(m_p1.quantity_done, 5.0, 'Total qty done should be 6.0') self.assertEqual(m_p1.quantity, 5.0, 'Total qty done should be 5.0')
self.assertEqual(sum(ml_p1.mapped('reserved_uom_qty')), 5.0, 'Total qty reserved should be 5.0')
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -763,21 +752,21 @@ class TestMrpOrder(TestMrpCommon):
mo._post_inventory() mo._post_inventory()
self.assertEqual(len(mo.move_raw_ids), 4) 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({ update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id, 'mo_id': mo.id,
'product_qty': 3, '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() update_quantity_wizard.change_prod_qty()
self.assertEqual(len(mo.move_raw_ids), 4) self.assertEqual(len(mo.move_raw_ids), 4)
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
self.assertTrue(all(s in ['done', 'cancel'] for s in mo.move_raw_ids.mapped('state'))) 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): def test_consumption_strict_1(self):
""" Checks the constraints of a strict BOM without tracking when playing around """ Checks the constraints of a strict BOM without tracking when playing around
@@ -800,7 +789,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
# Won't accept to be done, instead return a wizard # Won't accept to be done, instead return a wizard
mo.button_mark_done() mo.button_mark_done()
@@ -838,7 +827,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
# Won't accept to be done, instead return a wizard # Won't accept to be done, instead return a wizard
@@ -878,7 +867,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
# Won't accept to be done, instead return a wizard # Won't accept to be done, instead return a wizard
@@ -910,7 +899,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
# Won't accept to be done, instead return a wizard # Won't accept to be done, instead return a wizard
@@ -937,7 +926,7 @@ class TestMrpOrder(TestMrpCommon):
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
with mo_form.move_raw_ids.new() as move: with mo_form.move_raw_ids.new() as move:
move.product_id = serial move.product_id = serial
move.quantity_done = 2 move.quantity = 2
mo_form.save() mo_form.save()
def test_product_produce_10(self): 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) move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 2.0) 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) 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) move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 4.0) 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) 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) 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.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(move_byproduct_3.product_uom, dozen)
self.assertEqual(len(move_byproduct_3.move_line_ids), 1) 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) move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 2.0) 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) move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 4.0) 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) 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.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) self.assertEqual(move_byproduct_3.product_uom, dozen)
details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations')) 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.lot_id = self.serial_1
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.lot_1 ml.lot_id = self.lot_1
ml.qty_done = 2
details_operation_form.save() details_operation_form.save()
action = mo.button_mark_done() action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) 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) move_byproduct_1 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1) self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 1.0) 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) move_byproduct_2 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1) self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 2.0) 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) 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.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) self.assertEqual(move_byproduct_3.product_uom, dozen)
details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations')) 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.lot_id = self.serial_2
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations')) 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.lot_id = self.lot_2
ml.qty_done = 2
details_operation_form.save() details_operation_form.save()
details_operation_form = Form(move_byproduct_3, view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 3 ml.quantity = 3
details_operation_form.save() details_operation_form.save()
mo2.button_mark_done() 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_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_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') 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_1).quantity, 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_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).qty_done, 2.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).qty_done, 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('qty_done')), 5.0) self.assertEqual(sum(move_lines_byproduct_3.mapped('quantity')), 5.0)
self.assertEqual(move_lines_byproduct_3.mapped('product_uom_id'), dozen) self.assertEqual(move_lines_byproduct_3.mapped('product_uom_id'), dozen)
def test_product_produce_11(self): def test_product_produce_11(self):
@@ -1122,33 +1114,30 @@ class TestMrpOrder(TestMrpCommon):
mo_form = Form(mo) mo_form = Form(mo)
mo_form.qty_producing = 3 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() 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 = Form(mo)
mo_form.qty_producing = 4 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() 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 = Form(mo)
mo_form.qty_producing = 1 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() 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 # try adding another product that doesn't belong to the BoM
with mo_form.move_raw_ids.new() as move: with mo_form.move_raw_ids.new() as move:
move.product_id = self.product_4 move.product_id = self.product_4
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 10 ml.quantity = 10
details_operation_form.save() details_operation_form.save()
# Check that this new product is not updated by qty_producing # Check that this new product is not updated by qty_producing
mo_form = Form(mo) mo_form = Form(mo)
mo_form.qty_producing = 2 mo_form.qty_producing = 2
for move in mo_form.move_raw_ids._records: for move in mo_form.move_raw_ids._records:
if move['product_id'] == self.product_4.id: if move['product_id'] == self.product_4.id:
self.assertEqual(move['quantity_done'], 10) self.assertEqual(move['quantity'], 10)
break break
mo = mo_form.save() mo = mo_form.save()
mo.button_mark_done() mo.button_mark_done()
@@ -1192,9 +1181,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo1 = mo_form.save() mo1 = mo_form.save()
details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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 ml.lot_id = sn
details_operation_form.save() details_operation_form.save()
mo1.move_raw_ids.picked = True
mo1.button_mark_done() mo1.button_mark_done()
mo_form = Form(self.env['mrp.production']) mo_form = Form(self.env['mrp.production'])
@@ -1208,9 +1198,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo2 = mo_form.save() mo2 = mo_form.save()
details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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 ml.lot_id = sn
details_operation_form.save() details_operation_form.save()
mo2.move_raw_ids.picked = True
with self.assertRaises(UserError): with self.assertRaises(UserError):
mo2.button_mark_done() mo2.button_mark_done()
@@ -1250,7 +1241,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
move_byproduct = mo.move_finished_ids.filtered(lambda m: m.product_id != mo.product_id) 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')) 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 ml.lot_id = sn
details_operation_form.save() details_operation_form.save()
mo.button_mark_done() mo.button_mark_done()
@@ -1286,9 +1277,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo1 = mo_form.save() mo1 = mo_form.save()
details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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 ml.lot_id = sn
details_operation_form.save() details_operation_form.save()
mo1.move_raw_ids.picked = True
mo1.button_mark_done() mo1.button_mark_done()
unbuild_form = Form(self.env['mrp.unbuild']) unbuild_form = Form(self.env['mrp.unbuild'])
@@ -1310,9 +1302,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo2 = mo_form.save() mo2 = mo_form.save()
details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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 ml.lot_id = sn
details_operation_form.save() details_operation_form.save()
mo2.move_raw_ids.picked = True
mo2.button_mark_done() mo2.button_mark_done()
def test_product_produce_duplicate_5(self): def test_product_produce_duplicate_5(self):
@@ -1373,9 +1366,10 @@ class TestMrpOrder(TestMrpCommon):
view=self.env.ref("stock.view_stock_move_operations"), view=self.env.ref("stock.view_stock_move_operations"),
) )
with finished_good_detailed_operations_form.move_line_ids.edit(0) as ml: 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 ml.lot_id = subassembly_sn
finished_good_detailed_operations_form.save() finished_good_detailed_operations_form.save()
finished_good_mo.move_raw_ids.picked = True
finished_good_mo.button_mark_done() finished_good_mo.button_mark_done()
finished_good_ub_form = Form(self.env["mrp.unbuild"]) finished_good_ub_form = Form(self.env["mrp.unbuild"])
@@ -1425,8 +1419,6 @@ class TestMrpOrder(TestMrpCommon):
'product_uom': self.ref('uom.product_uom_unit'), 'product_uom': self.ref('uom.product_uom_unit'),
'production_id': mo.id, 'production_id': mo.id,
'location_dest_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0,
}) })
self.assertEqual(move.name, mo.name) self.assertEqual(move.name, mo.name)
@@ -1464,8 +1456,6 @@ class TestMrpOrder(TestMrpCommon):
'production_id': mo.id, 'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'), 'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'), 'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0,
}) })
mo.move_raw_ids |= move mo.move_raw_ids |= move
mo.action_confirm() mo.action_confirm()
@@ -1525,13 +1515,13 @@ class TestMrpOrder(TestMrpCommon):
mo_form.lot_producing_id = final_product_lot mo_form.lot_producing_id = final_product_lot
mo = mo_form.save() mo = mo_form.save()
move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.quantity)
self.assertEqual(move_line_raw.qty_done, 1) 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.') self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
mo._post_inventory() mo._post_inventory()
move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) move_line_finished = mo.move_finished_ids.move_line_ids.filtered(lambda m: m.state == 'done' and m.quantity)
self.assertEqual(move_line_finished.qty_done, 1) 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.') 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): def test_product_type_service_1(self):
@@ -1577,27 +1567,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign() mo.action_assign()
res_dict = mo.button_mark_done() res_dict = mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', '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.state, 'done')
self.assertEqual(mo.move_finished_ids.quantity_done, 1) self.assertEqual(mo.move_finished_ids.quantity, 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)
def test_immediate_validate_3(self): def test_immediate_validate_3(self):
""" In a production with a serial number tracked product. Check that the immediate production only creates """ 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() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
action = wizard.action_backorder() action = wizard.action_backorder()
self.assertEqual(mo.qty_producing, 1) 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) self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2)
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
self.assertEqual(mo_backorder.product_qty, 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() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
action = wizard.action_backorder() action = wizard.action_backorder()
self.assertEqual(mo.qty_producing, 1) 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) self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2)
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
self.assertEqual(mo_backorder.product_qty, 1) self.assertEqual(mo_backorder.product_qty, 1)
@@ -1657,9 +1629,9 @@ class TestMrpOrder(TestMrpCommon):
mos = mo1 | mo2 | mo3 mos = mo1 | mo2 | mo3
mos.button_mark_done() mos.button_mark_done()
self.assertEqual(mos.move_raw_ids.mapped('state'), ['done'] * 6) 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('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): def test_components_availability(self):
self.bom_2.unlink() # remove the kit bom of product_5 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, f'Exp {format_date(self.env, tommorrow)}')
self.assertEqual(mo.components_availability_state, 'expected') self.assertEqual(mo.components_availability_state, 'expected')
(move1 | move2 | move3)._set_quantities_to_reservation() (move1 | move2 | move3).picked = True
(move1 | move2 | move3)._action_done() (move1 | move2 | move3)._action_done()
mo.invalidate_recordset(['components_availability', 'components_availability_state']) mo.invalidate_recordset(['components_availability', 'components_availability_state'])
@@ -1721,9 +1693,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign() mo.action_assign()
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.mapped('state'), ['done'] * 2) 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.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) self.assertTrue(mo.move_finished_ids.move_line_ids.lot_id != False)
def test_immediate_validate_uom(self): def test_immediate_validate_uom(self):
@@ -1756,9 +1728,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign() mo.action_assign()
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.state, '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.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) self.assertEqual(component.qty_available, 13)
def test_immediate_validate_uom_2(self): def test_immediate_validate_uom_2(self):
@@ -1815,7 +1787,7 @@ class TestMrpOrder(TestMrpCommon):
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 0.2) 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): def test_copy(self):
""" Check that copying a done production, create all the stock moves""" """ 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, _, _, _, _ = self.generate_mo(qty_final=30, qty_base_1=2, qty_base_2=3)
mo.action_confirm() mo.action_confirm()
mo.qty_producing = 10 mo_form = Form(mo)
mo.move_raw_ids[0].quantity_done = 90 mo_form.qty_producing = 10
mo.move_raw_ids[1].quantity_done = 70 mo = mo_form.save()
mo.move_raw_ids[0].quantity = 90
mo.move_raw_ids[1].quantity = 70
action = mo.button_mark_done() action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder() backorder.save().action_backorder()
@@ -2089,9 +2063,9 @@ class TestMrpOrder(TestMrpCommon):
move_prod_2 = self.env['stock.move'].search([ move_prod_2 = self.env['stock.move'].search([
('product_id', '=', mo.bom_id.bom_line_ids[1].product_id.id), ('product_id', '=', mo.bom_id.bom_line_ids[1].product_id.id),
('raw_material_production_id', '=', mo.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_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) self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 20.0)
# Check quantities of the backorder MO # 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, _, _, p1, p2 = self.generate_mo(qty_final=20, qty_base_1=1, qty_base_2=1)
mo.action_confirm() mo.action_confirm()
mo.qty_producing = 10 mo_form = Form(mo)
mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done = 5 mo_form.qty_producing = 10
mo.move_raw_ids.filtered(lambda m: m.product_id == p2).quantity_done = 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() action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder() backorder.save().action_backorder()
@@ -2123,10 +2099,10 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(mo.product_uom_qty, 10.0) self.assertEqual(mo.product_uom_qty, 10.0)
self.assertEqual(mo.qty_produced, 10.0) self.assertEqual(mo.qty_produced, 10.0)
move_prod_1_done = mo.move_raw_ids.filtered(lambda m: m.product_id == p1) 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) 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) 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) self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 10)
# Check quantities of the backorder MO # Check quantities of the backorder MO
@@ -3193,7 +3169,7 @@ class TestMrpOrder(TestMrpCommon):
mo.action_confirm() mo.action_confirm()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 1) 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) self.assertEqual(mo.move_raw_ids.availability, 250)
update_quantity_wizard = self.env['change.production.qty'].create({ update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id, 'mo_id': mo.id,
@@ -3202,7 +3178,7 @@ class TestMrpOrder(TestMrpCommon):
update_quantity_wizard.change_prod_qty() update_quantity_wizard.change_prod_qty()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 2) 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) self.assertEqual(mo.move_raw_ids.availability, 0)
def test_update_qty_to_consume_of_component(self): 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: with mo_form.move_raw_ids.edit(0) as raw:
raw.product_uom_qty = 1.25 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): def test_clear_finished_move(self):
""" Test that the finished moves created by the compute are correctly """ 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) - 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: 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 - 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) 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 = mo_form.save()
# mo.move_raw_ids[0] = p2 => 20 qty_base, mo.move_raw_ids[1] = p1 => 12 qty_base # 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].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].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 changes won't auto-update MO, it will only show diff in consumption warning
bom.bom_line_ids[0].product_qty = 10 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].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() action = mo.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context']))
consumption = warning.save() consumption = warning.save()
@@ -3421,9 +3397,9 @@ class TestMrpOrder(TestMrpCommon):
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder() 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].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].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') self.assertEqual(mo.state, 'done')
# double check that backorder qtys are also correct # double check that backorder qtys are also correct
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
@@ -3448,7 +3424,7 @@ class TestMrpOrder(TestMrpCommon):
mo2 = mo2_form.save() 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(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].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() action = mo2.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) 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: for move in mo2.move_raw_ids:
if move.product_id == p2: if move.product_id == p2:
self.assertEqual(move.product_uom_qty, 40, "missing line values were not correctly added") 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: else:
self.assertEqual(move.product_uom_qty, 48, "expected qty should be unchanged") 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') self.assertEqual(mo2.state, 'done')
# double check that backorder qtys are also correct # double check that backorder qtys are also correct
mo2_backorder = mo2.procurement_group_id.mrp_production_ids[-1] mo2_backorder = mo2.procurement_group_id.mrp_production_ids[-1]
@@ -3499,9 +3475,9 @@ class TestMrpOrder(TestMrpCommon):
mo3_form.qty_producing = 1 mo3_form.qty_producing = 1
mo3 = mo3_form.save() 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].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].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() action = mo3.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])) warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context']))
consumption = warning.save() 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") 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() 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].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].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') self.assertEqual(mo3.state, 'done')
def test_exceeded_consumed_qty_and_duplicated_lines(self): def test_exceeded_consumed_qty_and_duplicated_lines(self):
@@ -3549,15 +3525,15 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1.0 mo_form.qty_producing = 1.0
mo = mo_form.save() 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() mo.button_mark_done()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
p02_raws = mo.move_raw_ids.filtered(lambda m: m.product_id == product02) 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) 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(p02_raws.mapped('quantity')), 1.5)
self.assertEqual(sum(p03_raws.mapped('quantity_done')), 2) self.assertEqual(sum(p03_raws.mapped('quantity')), 2)
def test_validation_mo_with_tracked_component(self): def test_validation_mo_with_tracked_component(self):
""" """
@@ -3584,18 +3560,15 @@ class TestMrpOrder(TestMrpCommon):
mo.action_confirm() mo.action_confirm()
self.assertEqual(mo.state, 'confirmed') self.assertEqual(mo.state, 'confirmed')
# set the qty to consume of the tracked product to 0 # set the qty to consume of the tracked product to 0
mo.move_raw_ids[0].write({ mo.move_raw_ids[0].product_uom_qty = 0
'product_uom_qty': 0, mo.move_raw_ids[0].quantity = 0
'quantity_done': 0,
})
mo.action_assign()
# Set MO Done and create backorder # Set MO Done and create backorder
action = mo.button_mark_done() action = mo.button_mark_done()
consumption_warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])).save() 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_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 # Force the warning
consumption_warning.action_confirm() consumption_warning.action_confirm()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
+12 -14
View File
@@ -174,22 +174,21 @@ class TestProcurement(TestMrpCommon):
self.assertTrue(picking_qc_to_stock) self.assertTrue(picking_qc_to_stock)
picking_input_to_qc.action_assign() picking_input_to_qc.action_assign()
self.assertEqual(picking_input_to_qc.state, 'assigned') 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_input_to_qc._action_done()
picking_qc_to_stock.action_assign() picking_qc_to_stock.action_assign()
self.assertEqual(picking_qc_to_stock.state, 'assigned') 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() 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') self.assertEqual(picking_qc_to_stock.state, 'done')
mo.action_assign() 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 = Form(mo)
produce_form.qty_producing = 3.0 produce_form.qty_producing = 3.0
mo = produce_form.save() mo = produce_form.save()
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.qty_done = 5.0 picking_qc_to_stock.move_line_ids.quantity = 5.0
self.assertEqual(mo.move_raw_ids.reserved_availability, 5.0) self.assertEqual(mo.move_raw_ids.quantity, 3.0)
self.assertEqual(mo.move_raw_ids.quantity_done, 3.0)
def test_link_date_mo_moves(self): def test_link_date_mo_moves(self):
""" Check link of shedule date for manufaturing with date stock move.""" """ Check link of shedule date for manufaturing with date stock move."""
@@ -343,7 +342,7 @@ class TestProcurement(TestMrpCommon):
production.button_mark_done() production.button_mark_done()
move_dest._action_assign() move_dest._action_assign()
self.assertEqual(move_dest.reserved_availability, 10.0) self.assertEqual(move_dest.quantity, 10.0)
def test_auto_assign(self): def test_auto_assign(self):
""" When auto reordering rule exists, check for when: """ When auto reordering rule exists, check for when:
@@ -455,7 +454,6 @@ class TestProcurement(TestMrpCommon):
'location_dest_id': self.ref('stock.stock_location_customers'), '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_confirm() # should trigger orderpoint to create and confirm 1st MO
pick_output.action_assign() pick_output.action_assign()
@@ -467,7 +465,7 @@ class TestProcurement(TestMrpCommon):
self.assertEqual(len(mo), 1, "Manufacture order was not automatically created") self.assertEqual(len(mo), 1, "Manufacture order was not automatically created")
mo.action_assign() mo.action_assign()
mo.is_locked = False 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") 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 # 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 = mo2_form.save()
mo2.button_mark_done() 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 # add new component to 1st MO
mo_form = Form(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") self.assertEqual(mo3.product_qty, 6, "Quantity to produce should be 1 + reordering rule max qty")
mo_form = Form(mo) mo_form = Form(mo)
mo.move_raw_ids.quantity_done = 15 mo.move_raw_ids.quantity = 15
mo_form.qty_producing = 15 mo_form.qty_producing = 15
mo = mo_form.save() mo = mo_form.save()
mo.button_mark_done() 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 # make sure next MO auto-reserves components now that they are in stock since
# default reservation_method = 'at_confirm' # default reservation_method = 'at_confirm'
@@ -518,7 +516,7 @@ class TestProcurement(TestMrpCommon):
mo_assign_at_confirm = mo_form.save() mo_assign_at_confirm = mo_form.save()
mo_assign_at_confirm.action_confirm() 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): def test_check_update_qty_mto_chain(self):
""" Simulate a mto chain with a manufacturing order. Updating the """ Simulate a mto chain with a manufacturing order. Updating the
+4 -4
View File
@@ -117,11 +117,11 @@ class TestMrpSerialMassProduce(TestMrpCommon):
# Reload the wizard to apply generated serial numbers # Reload the wizard to apply generated serial numbers
wizard = Form(self.env['stock.assign.serial'].browse(action['res_id'])) wizard = Form(self.env['stock.assign.serial'].browse(action['res_id']))
wizard.save().apply() 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[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) self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 1)
# 2nd MO should have 2 move lines (2 different lots) for product_to_use_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[1].move_raw_ids[1].move_lines_count, 2) 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 # Verify if lot IDs were correctly assigned to move lines
mo.procurement_group_id.mrp_production_ids.button_mark_done() mo.procurement_group_id.mrp_production_ids.button_mark_done()
+11 -14
View File
@@ -296,7 +296,7 @@ class TestKitPicking(common.TestMrpCommon):
} }
def test_kit_immediate_transfer(self): 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. immediate transfer.
""" """
picking = self.env['stock.picking'].create({ picking = self.env['stock.picking'].create({
@@ -308,7 +308,8 @@ class TestKitPicking(common.TestMrpCommon):
self.env['stock.move'].create({ self.env['stock.move'].create({
'name': self.kit_parent.name, 'name': self.kit_parent.name,
'product_id': self.kit_parent.id, 'product_id': self.kit_parent.id,
'product_uom_qty': 3, 'quantity': 3,
'picked': True,
'product_uom': self.kit_parent.uom_id.id, 'product_uom': self.kit_parent.uom_id.id,
'picking_id': picking.id, 'picking_id': picking.id,
'picking_type_id': self.env.ref('stock.picking_type_in').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, 'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id,
}) })
picking.button_validate() 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. # We check that the picking has the correct quantities after its move were splitted.
self.assertEqual(len(picking.move_ids), 7) self.assertEqual(len(picking.move_ids), 7)
for move_line in picking.move_ids: for move in picking.move_ids:
self.assertEqual(move_line.quantity_done, self.expected_quantities[move_line.product_id]) self.assertEqual(move.quantity, self.expected_quantities[move.product_id])
self.assertEqual(move.state, 'done')
def test_kit_planned_transfer(self): def test_kit_planned_transfer(self):
""" Make sure a kit is split in the corrects product_qty by components in case of a """ 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, 'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id,
'partner_id': self.test_partner.id, 'partner_id': self.test_partner.id,
'picking_type_id': self.env.ref('stock.picking_type_in').id, 'picking_type_id': self.env.ref('stock.picking_type_in').id,
'immediate_transfer': False,
}) })
move_receipt_1 = self.env['stock.move'].create({ move_receipt_1 = self.env['stock.move'].create({
'name': self.kit_parent.name, 'name': self.kit_parent.name,
@@ -368,8 +366,6 @@ class TestKitPicking(common.TestMrpCommon):
'picking_type_id': in_type.id, 'picking_type_id': in_type.id,
'location_id': customer_location.id, 'location_id': customer_location.id,
'location_dest_id': stock_location.id, 'location_dest_id': stock_location.id,
'state': 'draft',
'immediate_transfer': False,
'move_ids': [(0, 0, { 'move_ids': [(0, 0, {
'name': product.name, 'name': product.name,
'product_id': product.id, 'product_id': product.id,
@@ -381,19 +377,20 @@ class TestKitPicking(common.TestMrpCommon):
}) })
receipt.action_confirm() receipt.action_confirm()
receipt.move_line_ids.qty_done = 1 receipt.move_line_ids.quantity = 1
receipt.move_line_ids = [(0, 0, { receipt.move_line_ids = [(0, 0, {
'product_id': kit.id, 'product_id': kit.id,
'qty_done': 1, 'quantity': 1,
'product_uom_id': kit.uom_id.id, 'product_uom_id': kit.uom_id.id,
'location_id': customer_location.id, 'location_id': customer_location.id,
'location_dest_id': stock_location.id, 'location_dest_id': stock_location.id,
})] })]
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
self.assertEqual(receipt.state, 'done') self.assertEqual(receipt.state, 'done')
self.assertRecordValues(receipt.move_ids, [ self.assertRecordValues(receipt.move_ids, [
{'product_id': product.id, 'quantity_done': 1, 'state': 'done'}, {'product_id': product.id, 'quantity': 1, 'state': 'done'},
{'product_id': compo.id, 'quantity_done': 1, 'state': 'done'}, {'product_id': compo.id, 'quantity': 1, 'state': 'done'},
]) ])
+4 -2
View File
@@ -123,7 +123,7 @@ class TestMrpStockReports(TestReportsCommon):
pick = mo_1.move_raw_ids.move_orig_ids.picking_id pick = mo_1.move_raw_ids.move_orig_ids.picking_id
pick_form = Form(pick) pick_form = Form(pick)
with pick_form.move_line_ids_without_package.edit(0) as move_line: 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 = pick_form.save()
pick.button_validate() pick.button_validate()
# Produces 3 products then creates a backorder for the remaining product. # Produces 3 products then creates a backorder for the remaining product.
@@ -236,7 +236,7 @@ class TestMrpStockReports(TestReportsCommon):
picking = picking_form.save() picking = picking_form.save()
picking.action_confirm() 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 = 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'}) move.move_line_ids.result_package_id = self.env['stock.quant.package'].create({'name': 'Package0001'})
picking.button_validate() picking.button_validate()
@@ -254,4 +254,6 @@ class TestMrpStockReports(TestReportsCommon):
break break
if keys[0] in line: if keys[0] in line:
keys = keys[1:] keys = keys[1:]
self.assertFalse(keys, "All keys should be in the report with the defined order") self.assertFalse(keys, "All keys should be in the report with the defined order")
+30 -33
View File
@@ -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')) 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: 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.save()
details_operation_form = Form(mo.move_raw_ids[2], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -192,48 +193,45 @@ class TestTraceability(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': product_1.id, 'product_id': product_1.id,
'name': 'Raw_1_lot_1', 'name': 'Raw_1_lot_1',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': product_2.id, 'product_id': product_2.id,
'name': 'Raw_2_lot_1', 'name': 'Raw_2_lot_1',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form( details_operation_form = Form(
mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1), mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_1.id, 'product_id': byproduct_1.id,
'name': 'Byproduct_1_lot_1', 'name': 'Byproduct_1_lot_1',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form( details_operation_form = Form(
mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2), mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_2.id, 'product_id': byproduct_2.id,
'name': 'Byproduct_2_lot_1', 'name': 'Byproduct_2_lot_1',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids.picked = True
action = mo.button_mark_done() action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context'])) backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder() backorder.save().action_backorder()
@@ -251,56 +249,50 @@ class TestTraceability(TestMrpCommon):
mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_1), mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_1),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': product_1.id, 'product_id': product_1.id,
'name': 'Raw_1_lot_2', 'name': 'Raw_1_lot_2',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form( details_operation_form = Form(
mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_2), mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_2),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': product_2.id, 'product_id': product_2.id,
'name': 'Raw_2_lot_2', 'name': 'Raw_2_lot_2',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form( details_operation_form = Form(
mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1), mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_1.id, 'product_id': byproduct_1.id,
'name': 'Byproduct_1_lot_2', 'name': 'Byproduct_1_lot_2',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
details_operation_form = Form( details_operation_form = Form(
mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2), mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2),
view=self.env.ref('stock.view_stock_move_operations') 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({ ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_2.id, 'product_id': byproduct_2.id,
'name': 'Byproduct_2_lot_2', 'name': 'Byproduct_2_lot_2',
'company_id': self.env.company.id, 'company_id': self.env.company.id,
}) })
ml.qty_done = 1
details_operation_form.save() details_operation_form.save()
mo_backorder.move_raw_ids.picked = True
mo_backorder.button_mark_done() 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 mo = mo | mo_backorder
raw_move_lines = mo.move_raw_ids.mapped('move_line_ids') 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') 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_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') 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') 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_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') 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) 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') 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') 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) 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') 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') 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) 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.qty_producing = 1.0
mo_form.lot_producing_id = lot mo_form.lot_producing_id = lot
mo = mo_form.save() mo = mo_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
unbuild_form = Form(self.env['mrp.unbuild']) unbuild_form = Form(self.env['mrp.unbuild'])
@@ -367,6 +360,7 @@ class TestTraceability(TestMrpCommon):
_logger.warning('Dummy') _logger.warning('Dummy')
self.assertEqual(len(log_catcher.output), 1, "Useless warnings: \n%s" % "\n".join(log_catcher.output[:-1])) 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() mo.button_mark_done()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
@@ -483,7 +477,7 @@ class TestTraceability(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo_form.lot_producing_id = lot_componentA mo_form.lot_producing_id = lot_componentA
mo = mo_form.save() mo = mo_form.save()
mo.move_raw_ids[0].quantity_done = 1.0 mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
# Produce 1 endProduct A # Produce 1 endProduct A
@@ -496,7 +490,7 @@ class TestTraceability(TestMrpCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo_form.lot_producing_id = lot_endProductA mo_form.lot_producing_id = lot_endProductA
mo = mo_form.save() 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() mo.button_mark_done()
# Create out picking for EndProduct A # Create out picking for EndProduct A
@@ -508,7 +502,7 @@ class TestTraceability(TestMrpCommon):
moveA = self.env['stock.move'].create({ moveA = self.env['stock.move'].create({
'name': 'Picking A move', 'name': 'Picking A move',
'product_id': endproductA.id, 'product_id': endproductA.id,
'quantity_done': 1, 'quantity': 1,
'product_uom': endproductA.uom_id.id, 'product_uom': endproductA.uom_id.id,
'picking_id': pickingA_out.id, 'picking_id': pickingA_out.id,
'location_id': stock_location.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 # Set move_line lot_id to the mrp.production lot_producing_id
moveA.move_line_ids[0].write({ moveA.move_line_ids[0].write({
'qty_done': 1.0, 'quantity': 1.0,
'lot_id': lot_endProductA.id, 'lot_id': lot_endProductA.id,
}) })
# Transfer picking # Transfer picking
moveA.picked = True
pickingA_out._action_done() pickingA_out._action_done()
# Use concat so that delivery_ids is computed in batch. # Use concat so that delivery_ids is computed in batch.
@@ -557,7 +552,7 @@ class TestTraceability(TestMrpCommon):
with Form(mo) as mo_form: with Form(mo) as mo_form:
mo_form.qty_producing = 1 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() mo.button_mark_done()
# unbuild # unbuild
@@ -583,12 +578,13 @@ class TestTraceability(TestMrpCommon):
'product_id': component.id, 'product_id': component.id,
'product_uom': component.uom_id.id, 'product_uom': component.uom_id.id,
'product_uom_qty': 1.0, 'product_uom_qty': 1.0,
'picked': True,
'move_line_ids': [(0, 0, { 'move_line_ids': [(0, 0, {
'product_id': component.id, 'product_id': component.id,
'location_id': scrap_location.id, 'location_id': scrap_location.id,
'location_dest_id': stock_location.id, 'location_dest_id': stock_location.id,
'product_uom_id': component.uom_id.id, 'product_uom_id': component.uom_id.id,
'qty_done': 1.0, 'quantity': 1.0,
'lot_id': serial_number.id, 'lot_id': serial_number.id,
})], })],
}) })
@@ -605,12 +601,13 @@ class TestTraceability(TestMrpCommon):
with Form(mo) as mo_form: with Form(mo) as mo_form:
mo_form.qty_producing = 1 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() mo.button_mark_done()
self.assertRecordValues((mo.move_finished_ids + mo.move_raw_ids).move_line_ids, [ 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': self.bom_4.product_id.id, 'lot_id': False, 'quantity': 1},
{'product_id': component.id, 'lot_id': serial_number.id, 'qty_done': 1}, {'product_id': component.id, 'lot_id': serial_number.id, 'quantity': 1},
]) ])
def test_generate_serial_button(self): def test_generate_serial_button(self):
+23 -24
View File
@@ -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')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = lot ml.lot_id = lot
ml.qty_done = 20 ml.quantity = 20
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild. # Check quantity in stock before unbuild.
@@ -272,13 +273,14 @@ class TestUnbuild(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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: with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 20 ml.quantity = 20
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild. # Check quantity in stock before unbuild.
@@ -371,15 +373,8 @@ class TestUnbuild(TestMrpCommon):
mo_form = Form(mo) mo_form = Form(mo)
mo_form.qty_producing = 5.0 mo_form.qty_producing = 5.0
mo = mo_form.save() 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() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild. # Check quantity in stock before unbuild.
@@ -427,12 +422,13 @@ class TestUnbuild(TestMrpCommon):
mo_form.qty_producing = 3.0 mo_form.qty_producing = 3.0
mo_form.lot_producing_id = lot_finished_1 mo_form.lot_producing_id = lot_finished_1
mo = mo_form.save() 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')) 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.qty_done = 3 ml.quantity = 3
ml.lot_id = lot_1 ml.lot_id = lot_1
details_operation_form.save() details_operation_form.save()
mo.move_raw_ids.picked = True
action = mo.button_mark_done() action = mo.button_mark_done()
backorder = Form(self.env[action['res_model']].with_context(**action['context'])) backorder = Form(self.env[action['res_model']].with_context(**action['context']))
backorder.save().action_backorder() backorder.save().action_backorder()
@@ -460,16 +456,16 @@ class TestUnbuild(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 2 ml.quantity = 2
ml.lot_id = lot_2 ml.lot_id = lot_2
details_operation_form.save() details_operation_form.save()
action = mo.button_mark_done() action = mo.button_mark_done()
mo1 = mo.procurement_group_id.mrp_production_ids[0] 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) 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) 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): def test_unbuild_with_routes(self):
""" This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock """ 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 # Transfer it
for ml in picking.move_ids_without_package: for ml in picking.move_ids_without_package:
ml.quantity_done = 1 ml.write({'quantity': 1, 'picked': True})
picking._action_done() picking._action_done()
# Check the available quantity of components and final product in stock # 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')) 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: 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.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) 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: 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.save()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
uo_form = Form(self.env['mrp.unbuild']) uo_form = Form(self.env['mrp.unbuild'])
@@ -708,7 +705,8 @@ class TestUnbuild(TestMrpCommon):
internal_form.location_dest_id = subloc01 internal_form.location_dest_id = subloc01
with internal_form.move_ids_without_package.new() as move: with internal_form.move_ids_without_package.new() as move:
move.product_id = p_final move.product_id = p_final
move.quantity_done = 1.0 move.quantity = 1.0
move.picked = True
internal_transfer = internal_form.save() internal_transfer = internal_form.save()
internal_transfer.button_validate() 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')) 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.new() as ml:
ml.lot_id = product_1_sn ml.lot_id = product_1_sn
ml.qty_done = 1 ml.quantity = 1
details_operation_form.save() details_operation_form.save()
mo_form = Form(mo2) mo_form = Form(mo2)
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo2 = mo_form.save() mo2 = mo_form.save()
mo2.move_raw_ids.picked = True
mo2.button_mark_done() mo2.button_mark_done()
self.assertEqual(mo2.state, 'done', "Production order should be in done state.") self.assertEqual(mo2.state, 'done', "Production order should be in done state.")
@@ -857,7 +856,7 @@ class TestUnbuild(TestMrpCommon):
mo.action_confirm() mo.action_confirm()
mo.qty_producing = 1.0 mo.qty_producing = 1.0
mo.lot_producing_id = finished_product_sn 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() mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.") self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# unbuild order mo_1 # unbuild order mo_1
@@ -886,7 +885,7 @@ class TestUnbuild(TestMrpCommon):
mo_2.action_confirm() mo_2.action_confirm()
mo_2.qty_producing = 1.0 mo_2.qty_producing = 1.0
mo_2.lot_producing_id = finished_product_sn 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() mo_2.button_mark_done()
self.assertEqual(mo_2.state, 'done', "Production order should be in done state.") self.assertEqual(mo_2.state, 'done', "Production order should be in done state.")
# unbuild mo_2 # unbuild mo_2
@@ -147,7 +147,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'), 'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'),
'picking_type_id': self.warehouse.out_type_id.id, 'picking_type_id': self.warehouse.out_type_id.id,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
self.env['stock.move'].create({ 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') self.assertEqual(picking_stock_postprod.origin, 'SOURCEDOCUMENT', 'The post-prod origin should be the SO name')
picking_stock_preprod.action_assign() 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() picking_stock_preprod._action_done()
self.assertFalse(sum(self.env['stock.quant']._gather(self.raw_product, self.warehouse.lot_stock_id).mapped('quantity'))) 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'), 'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'),
'picking_type_id': self.warehouse.out_type_id.id, 'picking_type_id': self.warehouse.out_type_id.id,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
self.env['stock.move'].create({ self.env['stock.move'].create({
'name': self.finished_product.name, 'name': self.finished_product.name,
+10 -8
View File
@@ -349,7 +349,8 @@
That being said, the best would be to have the byproducts feature working without relying on anything in the view, 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. e.g. so the byproducts feature works with xmlrpc calls.
--> -->
<field name="quantity_done"/> <field name="quantity"/>
<field name="picked"/>
<!-- Required for test_fifo_byproduct --> <!-- Required for test_fifo_byproduct -->
<field name="cost_share"/> <field name="cost_share"/>
</tree> </tree>
@@ -391,22 +392,23 @@
<field name="should_consume_qty" column_invisible="True"/> <field name="should_consume_qty" column_invisible="True"/>
<field name="product_uom_qty" force_save="1" string="To Consume" column_invisible="not parent.show_produce_all" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or (parent.is_locked and state != 'draft'))"/> <field name="product_uom_qty" force_save="1" string="To Consume" column_invisible="not parent.show_produce_all" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or (parent.is_locked and state != 'draft'))"/>
<field name="product_uom_qty" widget="mrp_should_consume" force_save="1" string="To Consume" column_invisible="parent.show_produce_all" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or (parent.is_locked and state != 'draft'))"/> <field name="product_uom_qty" widget="mrp_should_consume" force_save="1" string="To Consume" column_invisible="parent.show_produce_all" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or (parent.is_locked and state != 'draft'))"/>
<field name="product_uom" readonly="state != 'draft' and id" options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/>
<field name="product_type" column_invisible="True"/> <field name="product_type" column_invisible="True"/>
<field name="product_qty" readonly="1" column_invisible="True"/> <field name="product_qty" readonly="1" column_invisible="True"/>
<field name="reserved_availability" column_invisible="True"/>
<field name="show_reserved" column_invisible="True"/> <field name="show_reserved" column_invisible="True"/>
<field name="forecast_expected_date" column_invisible="True"/> <field name="forecast_expected_date" column_invisible="True"/>
<!-- Button are used in state draft to doesn't have the name of the column "Reserved"--> <!-- Button are used in state draft to doesn't have the name of the column "Reserved"-->
<button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart" column_invisible="parent.state != 'draft'" invisible="forecast_availability &lt; 0"/> <button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart" column_invisible="parent.state != 'draft'" invisible="forecast_availability &lt; 0"/>
<button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart text-danger" column_invisible="parent.state != 'draft'" invisible="forecast_availability &gt;= 0"/> <button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart text-danger" column_invisible="parent.state != 'draft'" invisible="forecast_availability &gt;= 0"/>
<field name="forecast_availability" string="Reserved" column_invisible="parent.state in ('draft', 'done')" widget="forecast_widget"/> <!-- TODO put forecast back -->
<field name="quantity_done" string="Consumed" <field name="forecast_availability" string="Forecast" widget="forecast_widget"/>
decoration-success="product_uom_qty - quantity_done &gt; 0.0001" <field name="quantity" string="Reserved"
decoration-warning="quantity_done - product_uom_qty &gt; 0.0001" decoration-success="product_uom_qty - quantity &gt; 0.0001"
decoration-warning="quantity - product_uom_qty &gt; 0.0001"
column_invisible="parent.state == 'draft'" column_invisible="parent.state == 'draft'"
readonly="has_tracking != 'none'" readonly="has_tracking != 'none'"
force_save="1" widget="mrp_consumed"/> force_save="1" widget="mrp_consumed"/>
<field name="product_uom" readonly="state != 'draft' and id" options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/>
<field name="picked" string="Consumed" column_invisible="parent.state == 'draft'" optional="show"/>
<field name="manual_consumption" column_invisible="True" force_save="1"/> <field name="manual_consumption" column_invisible="True" force_save="1"/>
<field name="show_details_visible" column_invisible="True"/> <field name="show_details_visible" column_invisible="True"/>
<field name="lot_ids" widget="many2many_tags" <field name="lot_ids" widget="many2many_tags"
@@ -453,7 +455,7 @@
<field name="move_lines_count" column_invisible="True"/> <field name="move_lines_count" column_invisible="True"/>
<field name="state" column_invisible="True" force_save="1"/> <field name="state" column_invisible="True" force_save="1"/>
<field name="product_uom_qty" string="To Produce" force_save="1" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or parent.is_locked)"/> <field name="product_uom_qty" string="To Produce" force_save="1" readonly="parent.state != 'draft' and ((parent.state not in ('confirmed', 'progress', 'to_close') and not parent.is_planned) or parent.is_locked)"/>
<field name="quantity_done" string="Produced" column_invisible="parent.state == 'draft'" readonly="has_tracking != 'none'"/> <field name="quantity" string="Produced" column_invisible="parent.state == 'draft'" readonly="has_tracking != 'none'"/>
<field name="product_uom" groups="uom.group_uom"/> <field name="product_uom" groups="uom.group_uom"/>
<field name="cost_share" optional="hide"/> <field name="cost_share" optional="hide"/>
<field name="show_details_visible" column_invisible="True"/> <field name="show_details_visible" column_invisible="True"/>
+2 -2
View File
@@ -226,8 +226,8 @@
<field name="product_type" column_invisible="True"/> <field name="product_type" column_invisible="True"/>
<field name="product_id" readonly="state == 'done'"/> <field name="product_id" readonly="state == 'done'"/>
<field name="product_qty" string="To Consume"/> <field name="product_qty" string="To Consume"/>
<field name="reserved_availability" string ="Reserved"/> <field name="quantity" string="Quantity"/>
<field name="quantity_done" string="Consumed"/> <field name="picked" string="Consumed"/>
<field name="product_qty_available" string="On Hand" invisible="product_type != 'product'"/> <field name="product_qty_available" string="On Hand" invisible="product_type != 'product'"/>
<field name="product_virtual_available" string="Forecasted" invisible="product_type != 'product'"/> <field name="product_virtual_available" string="Forecasted" invisible="product_type != 'product'"/>
</tree> </tree>
+2 -8
View File
@@ -15,10 +15,7 @@
<field name="inherit_id" ref="stock.view_stock_move_operations" /> <field name="inherit_id" ref="stock.view_stock_move_operations" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//label[@for='product_uom_qty']" position="attributes"> <xpath expr="//label[@for='product_uom_qty']" position="attributes">
<attribute name="string">Total To Consume</attribute> <attribute name="string">Total To Consume</attribute>
</xpath>
<xpath expr="//label[@for='quantity_done']" position="attributes">
<attribute name="string">Consumed</attribute>
</xpath> </xpath>
</field> </field>
</record> </record>
@@ -31,10 +28,7 @@
<field name="inherit_id" ref="stock.view_stock_move_operations" /> <field name="inherit_id" ref="stock.view_stock_move_operations" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//label[@for='product_uom_qty']" position="attributes"> <xpath expr="//label[@for='product_uom_qty']" position="attributes">
<attribute name="string">To Produce</attribute> <attribute name="string">To Produce</attribute>
</xpath>
<xpath expr="//label[@for='quantity_done']" position="attributes">
<attribute name="string">Produced</attribute>
</xpath> </xpath>
</field> </field>
</record> </record>
+5 -9
View File
@@ -58,20 +58,16 @@ class ChangeProductionQty(models.TransientModel):
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
for wizard in self: for wizard in self:
production = wizard.mo_id production = wizard.mo_id
produced = sum(production.move_finished_ids.filtered(lambda m: m.product_id == production.product_id).mapped('quantity_done')) if wizard.product_qty < production.qty_produced:
if wizard.product_qty < produced:
format_qty = '%.{precision}f'.format(precision=precision) format_qty = '%.{precision}f'.format(precision=precision)
raise UserError(_( raise UserError(_(
"You have already processed %(quantity)s. Please input a quantity higher than %(minimum)s ", "You have already processed %(quantity)s. Please input a quantity higher than %(quantity)s ",
quantity=format_qty % produced, quantity=format_qty % production.qty_produced,
minimum=format_qty % produced
)) ))
old_production_qty = production.product_qty old_production_qty = production.product_qty
new_production_qty = wizard.product_qty new_production_qty = wizard.product_qty
done_moves = production.move_finished_ids.filtered(lambda x: x.state == 'done' and x.product_id == production.product_id)
qty_produced = production.product_id.uom_id._compute_quantity(sum(done_moves.mapped('product_qty')), production.product_uom_id)
factor = (new_production_qty - qty_produced) / (old_production_qty - qty_produced) factor = (new_production_qty - production.qty_produced) / (old_production_qty - production.qty_produced)
update_info = production._update_raw_moves(factor) update_info = production._update_raw_moves(factor)
documents = {} documents = {}
for move, old_qty, new_qty in update_info: for move, old_qty, new_qty in update_info:
@@ -84,7 +80,7 @@ class ChangeProductionQty(models.TransientModel):
else: else:
documents[key] = [value] documents[key] = [value]
production._log_manufacture_exception(documents) production._log_manufacture_exception(documents)
self._update_finished_moves(production, new_production_qty - qty_produced, old_production_qty - qty_produced) self._update_finished_moves(production, new_production_qty - production.qty_produced, old_production_qty - production.qty_produced)
production.write({'product_qty': new_production_qty}) production.write({'product_qty': new_production_qty})
for wo in production.workorder_ids: for wo in production.workorder_ids:
+4 -3
View File
@@ -46,14 +46,14 @@ class MrpConsumptionWarning(models.TransientModel):
if line.product_id != move.product_id: if line.product_id != move.product_id:
continue continue
qty_expected = line.product_uom_id._compute_quantity(line.product_expected_qty_uom, move.product_uom) qty_expected = line.product_uom_id._compute_quantity(line.product_expected_qty_uom, move.product_uom)
qty_compare_result = float_compare(qty_expected, move.quantity_done, precision_rounding=move.product_uom.rounding) qty_compare_result = float_compare(qty_expected, move.quantity, precision_rounding=move.product_uom.rounding)
if qty_compare_result != 0: if qty_compare_result != 0:
if (move.has_tracking in ('lot', 'serial') if (move.has_tracking in ('lot', 'serial')
and not production.use_auto_consume_components_lots and not production.use_auto_consume_components_lots
and qty_compare_result > 0): and qty_compare_result > 0):
problem_tracked_products |= line.product_id problem_tracked_products |= line.product_id
break break
move.quantity_done = qty_expected move.quantity = qty_expected
# in case multiple lines with same product => set others to 0 since we have no way to know how to distribute the qty done # in case multiple lines with same product => set others to 0 since we have no way to know how to distribute the qty done
line.product_expected_qty_uom = 0 line.product_expected_qty_uom = 0
# move was deleted before confirming MO or force deleted somehow # move was deleted before confirming MO or force deleted somehow
@@ -65,9 +65,10 @@ class MrpConsumptionWarning(models.TransientModel):
'product_id': line.product_id.id, 'product_id': line.product_id.id,
'product_uom': line.product_uom_id.id, 'product_uom': line.product_uom_id.id,
'product_uom_qty': line.product_expected_qty_uom, 'product_uom_qty': line.product_expected_qty_uom,
'quantity_done': line.product_expected_qty_uom, 'quantity': line.product_expected_qty_uom,
'raw_material_production_id': line.mrp_production_id.id, 'raw_material_production_id': line.mrp_production_id.id,
'additional': True, 'additional': True,
'picked': True,
}) })
if problem_tracked_products: if problem_tracked_products:
raise UserError( raise UserError(
@@ -78,10 +78,6 @@ class StockAssignSerialNumbers(models.TransientModel):
for workorder in production.workorder_ids: for workorder in production.workorder_ids:
workorder.qty_produced = workorder.qty_producing workorder.qty_produced = workorder.qty_producing
if productions and len(production_lots) < len(productions):
productions[-1].move_raw_ids.move_line_ids.write({'qty_done': 0})
productions[-1].state = "confirmed"
if self.mark_as_done: if self.mark_as_done:
productions.button_mark_done() productions.button_mark_done()
+2 -2
View File
@@ -44,11 +44,11 @@
<function model="stock.move" name="write"> <function model="stock.move" name="write">
<value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp_account.mrp_production_drawer_2').id)]).ids"/> <value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp_account.mrp_production_drawer_2').id)]).ids"/>
<value eval="{'quantity_done': 5}"/> <value eval="{'quantity': 5}"/>
</function> </function>
<function model="stock.move" name="write"> <function model="stock.move" name="write">
<value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp_account.mrp_production_drawer_3').id)]).ids"/> <value model="stock.move" eval="obj().env['stock.move'].search([('raw_material_production_id', '=', obj().env.ref('mrp_account.mrp_production_drawer_3').id)]).ids"/>
<value eval="{'quantity_done': 4}"/> <value eval="{'quantity': 4}"/>
</function> </function>
<function model="mrp.production" name="_post_inventory"> <function model="mrp.production" name="_post_inventory">
+7 -7
View File
@@ -83,25 +83,25 @@ class MrpProduction(models.Model):
super(MrpProduction, self)._cal_price(consumed_moves) super(MrpProduction, self)._cal_price(consumed_moves)
work_center_cost = 0 work_center_cost = 0
finished_move = self.move_finished_ids.filtered( finished_move = self.move_finished_ids.filtered(
lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity_done > 0) lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity > 0)
if finished_move: if finished_move:
finished_move.ensure_one() finished_move.ensure_one()
for work_order in self.workorder_ids: for work_order in self.workorder_ids:
work_center_cost += work_order._cal_cost() work_center_cost += work_order._cal_cost()
qty_done = finished_move.product_uom._compute_quantity( quantity = finished_move.product_uom._compute_quantity(
finished_move.quantity_done, finished_move.product_id.uom_id) finished_move.quantity, finished_move.product_id.uom_id)
extra_cost = self.extra_cost * qty_done extra_cost = self.extra_cost * quantity
total_cost = - sum(consumed_moves.sudo().stock_valuation_layer_ids.mapped('value')) + work_center_cost + extra_cost total_cost = - sum(consumed_moves.sudo().stock_valuation_layer_ids.mapped('value')) + work_center_cost + extra_cost
byproduct_moves = self.move_byproduct_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.quantity_done > 0) byproduct_moves = self.move_byproduct_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.quantity > 0)
byproduct_cost_share = 0 byproduct_cost_share = 0
for byproduct in byproduct_moves: for byproduct in byproduct_moves:
if byproduct.cost_share == 0: if byproduct.cost_share == 0:
continue continue
byproduct_cost_share += byproduct.cost_share byproduct_cost_share += byproduct.cost_share
if byproduct.product_id.cost_method in ('fifo', 'average'): if byproduct.product_id.cost_method in ('fifo', 'average'):
byproduct.price_unit = total_cost * byproduct.cost_share / 100 / byproduct.product_uom._compute_quantity(byproduct.quantity_done, byproduct.product_id.uom_id) byproduct.price_unit = total_cost * byproduct.cost_share / 100 / byproduct.product_uom._compute_quantity(byproduct.quantity, byproduct.product_id.uom_id)
if finished_move.product_id.cost_method in ('fifo', 'average'): if finished_move.product_id.cost_method in ('fifo', 'average'):
finished_move.price_unit = total_cost * float_round(1 - byproduct_cost_share / 100, precision_rounding=0.0001) / qty_done finished_move.price_unit = total_cost * float_round(1 - byproduct_cost_share / 100, precision_rounding=0.0001) / quantity
return True return True
def _get_backorder_mo_vals(self): def _get_backorder_mo_vals(self):
+1 -9
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, models from odoo import models
class StockMove(models.Model): class StockMove(models.Model):
@@ -12,14 +12,6 @@ class StockMove(models.Model):
res += self.filtered(lambda m: m.bom_line_id.bom_id.product_tmpl_id.id == product.product_tmpl_id.id) res += self.filtered(lambda m: m.bom_line_id.bom_id.product_tmpl_id.id == product.product_tmpl_id.id)
return res return res
def _generate_analytic_lines_data(self, unit_amount, amount, distribution, account, distribution_on_each_plan):
vals = super()._generate_analytic_lines_data(unit_amount, amount, distribution, account, distribution_on_each_plan)
if self._get_analytic_distribution():
vals['name'] = _('[Raw] %s', self.product_id.display_name)
vals['ref'] = self.raw_material_production_id.display_name
vals['category'] = 'manufacturing_order'
return vals
def _get_analytic_distribution(self): def _get_analytic_distribution(self):
distribution = self.raw_material_production_id.analytic_distribution distribution = self.raw_material_production_id.analytic_distribution
if distribution: if distribution:
@@ -296,6 +296,6 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
mo.button_mark_done() mo.button_mark_done()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
self.assertEqual(analytic_account.debit, 100) self.assertEqual(analytic_account.debit, 100)
mo.move_raw_ids[0].quantity_done = 0 mo.move_raw_ids[0].quantity = 0
self.assertEqual(analytic_account.debit, 0) self.assertEqual(analytic_account.debit, 0)
self.assertFalse(analytic_account.line_ids) self.assertFalse(analytic_account.line_ids)
@@ -370,20 +370,20 @@ class TestMrpValuationStandard(TestMrpValuationCommon):
'location_dest_id': self.stock_location.id, 'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_in.id, 'picking_type_id': self.picking_type_in.id,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
'move_line_ids': [(0, 0, { 'move_line_ids': [(0, 0, {
'product_id': self.product1.id, 'product_id': self.product1.id,
'qty_done': 1, 'quantity': 1,
'product_uom_id': self.product1.uom_id.id, 'product_uom_id': self.product1.uom_id.id,
'location_id': self.customer_location.id, 'location_id': self.customer_location.id,
'location_dest_id': self.stock_location.id, 'location_dest_id': self.stock_location.id,
})] })]
}) })
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
self.assertEqual(receipt.state, 'done') self.assertEqual(receipt.state, 'done')
self.assertRecordValues(receipt.move_ids, [ self.assertRecordValues(receipt.move_ids, [
{'product_id': self.component.id, 'quantity_done': 1, 'state': 'done'}, {'product_id': self.component.id, 'quantity': 1, 'state': 'done'},
]) ])
self.assertEqual(self.component.qty_available, 1) self.assertEqual(self.component.qty_available, 1)
self.assertEqual(self.component.value_svl, 1424) self.assertEqual(self.component.value_svl, 1424)
@@ -434,7 +434,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0, 'product_uom_qty': 10.0,
}) })
production_in.action_confirm() production_in.action_confirm()
move.quantity_done = 10 move.quantity = 10
move.picked = True
production_in.button_validate() production_in.button_validate()
in_aml = self._get_production_cost_move_lines() in_aml = self._get_production_cost_move_lines()
@@ -457,7 +458,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0, 'product_uom_qty': 10.0,
}) })
production_out.action_confirm() production_out.action_confirm()
move.quantity_done = 10 move.quantity = 10
move.picked = True
production_out.button_validate() production_out.button_validate()
out_aml = self._get_production_cost_move_lines() - in_aml out_aml = self._get_production_cost_move_lines() - in_aml
@@ -509,7 +511,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0, 'product_uom_qty': 10.0,
}) })
production_in.action_confirm() production_in.action_confirm()
move.quantity_done = 10 move.quantity = 10
move.picked = True
production_in.button_validate() production_in.button_validate()
in_aml = self.env['account.move.line'].search([ in_aml = self.env['account.move.line'].search([
@@ -534,7 +537,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0, 'product_uom_qty': 10.0,
}) })
production_out.action_confirm() production_out.action_confirm()
move.quantity_done = 10 move.quantity = 10
move.picked = True
production_out.button_validate() production_out.button_validate()
out_aml = self.env['account.move.line'].search([ out_aml = self.env['account.move.line'].search([
@@ -82,7 +82,7 @@ class TestStockLot(TestStockCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 3 ml.quantity = 3
ml.lot_id = self.lot_good_apple ml.lot_id = self.lot_good_apple
details_operation_form.save() details_operation_form.save()
res = mo.button_mark_done() res = mo.button_mark_done()
@@ -105,7 +105,7 @@ class TestStockLot(TestStockCommon):
mo = mo_form.save() mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) 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.new() as ml:
ml.qty_done = 3 ml.quantity = 3
ml.lot_id = self.lot_expired_apple ml.lot_id = self.lot_expired_apple
details_operation_form.save() details_operation_form.save()
res = mo.button_mark_done() res = mo.button_mark_done()
+5 -1
View File
@@ -44,6 +44,7 @@ class TestRepairTraceability(TestMrpCommon):
mo.lot_producing_id = ptrepair_lot mo.lot_producing_id = ptrepair_lot
# Set component serial to B2 # Set component serial to B2
mo.move_raw_ids.move_line_ids.lot_id = ptremove_lot mo.move_raw_ids.move_line_ids.lot_id = ptremove_lot
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
with Form(self.env['repair.order']) as ro_form: with Form(self.env['repair.order']) as ro_form:
@@ -56,6 +57,7 @@ class TestRepairTraceability(TestMrpCommon):
ro.action_validate() ro.action_validate()
ro.move_ids[0].lot_ids = ptremove_lot # Remove product Serial B2 from the product. ro.move_ids[0].lot_ids = ptremove_lot # Remove product Serial B2 from the product.
ro.action_repair_start() ro.action_repair_start()
ro.move_ids.picked = True
ro.action_repair_end() ro.action_repair_end()
# Create a manufacturing order with product (with SN A2) # Create a manufacturing order with product (with SN A2)
@@ -74,6 +76,7 @@ class TestRepairTraceability(TestMrpCommon):
}) })
# Set component serial to B2 again, it is possible # Set component serial to B2 again, it is possible
mo2.move_raw_ids.move_line_ids.lot_id = ptremove_lot mo2.move_raw_ids.move_line_ids.lot_id = ptremove_lot
mo2.move_raw_ids.picked = True
# We are not forbidden to use that serial number, so nothing raised here # We are not forbidden to use that serial number, so nothing raised here
mo2.button_mark_done() mo2.button_mark_done()
@@ -92,6 +95,7 @@ class TestRepairTraceability(TestMrpCommon):
mo = mo_form.save() mo = mo_form.save()
mo.action_confirm() mo.action_confirm()
mo.action_assign() mo.action_assign()
mo.move_raw_ids.picked = True
mo.button_mark_done() mo.button_mark_done()
return mo return mo
@@ -128,8 +132,8 @@ class TestRepairTraceability(TestMrpCommon):
ro.action_validate() ro.action_validate()
ro.move_ids[0].lot_ids = sn_lot ro.move_ids[0].lot_ids = sn_lot
ro.action_repair_start() ro.action_repair_start()
ro.move_ids.picked = True
ro.action_repair_end() ro.action_repair_end()
mo = produce_one(finished, component) mo = produce_one(finished, component)
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
self.assertEqual(mo.move_raw_ids.lot_ids, sn_lot) self.assertEqual(mo.move_raw_ids.lot_ids, sn_lot)
@@ -38,7 +38,7 @@ class MrpProduction(models.Model):
for move in production.move_raw_ids: for move in production.move_raw_ids:
move.move_line_ids = line_by_product.pop(move.product_id, self.env['stock.move.line']) move.move_line_ids = line_by_product.pop(move.product_id, self.env['stock.move.line'])
for product_id, lines in line_by_product.items(): for product_id, lines in line_by_product.items():
qty = sum(line.product_uom_id._compute_quantity(line.qty_done, product_id.uom_id) for line in lines) qty = sum(line.product_uom_id._compute_quantity(line.quantity, product_id.uom_id) for line in lines)
move = production._get_move_raw_values(product_id, qty, product_id.uom_id) move = production._get_move_raw_values(product_id, qty, product_id.uom_id)
move['additional'] = True move['additional'] = True
production.move_raw_ids = [(0, 0, move)] production.move_raw_ids = [(0, 0, move)]
@@ -58,6 +58,7 @@ class MrpProduction(models.Model):
def subcontracting_record_component(self): def subcontracting_record_component(self):
self.ensure_one() self.ensure_one()
self.move_raw_ids.picked = True
if not self._get_subcontract_move(): if not self._get_subcontract_move():
raise UserError(_("This MO isn't related to a subcontracted move")) raise UserError(_("This MO isn't related to a subcontracted move"))
if float_is_zero(self.qty_producing, precision_rounding=self.product_uom_id.rounding): if float_is_zero(self.qty_producing, precision_rounding=self.product_uom_id.rounding):
@@ -67,13 +68,12 @@ class MrpProduction(models.Model):
for sml in self.move_raw_ids.move_line_ids: for sml in self.move_raw_ids.move_line_ids:
if sml.tracking != 'none' and not sml.lot_id: if sml.tracking != 'none' and not sml.lot_id:
raise UserError(_('You must enter a serial number for each line of %s', sml.product_id.display_name)) raise UserError(_('You must enter a serial number for each line of %s', sml.product_id.display_name))
if self.move_raw_ids and not any(self.move_raw_ids.mapped('quantity_done')): if self.move_raw_ids and not any(self.move_raw_ids.mapped('quantity')):
raise UserError(_("You must indicate a non-zero amount consumed for at least one of your components")) raise UserError(_("You must indicate a non-zero amount consumed for at least one of your components"))
consumption_issues = self._get_consumption_issues() consumption_issues = self._get_consumption_issues()
if consumption_issues: if consumption_issues:
return self._action_generate_consumption_wizard(consumption_issues) return self._action_generate_consumption_wizard(consumption_issues)
self.sudo()._update_finished_move() # Portal user may need sudo rights to update pickings
self.sudo()._update_finished_move() # Portal user may need sudo rights to update pickings
self.subcontracting_has_been_recorded = True self.subcontracting_has_been_recorded = True
quantity_issues = self._get_quantity_produced_issues() quantity_issues = self._get_quantity_produced_issues()
@@ -104,37 +104,30 @@ class MrpProduction(models.Model):
if subcontract_move_id: if subcontract_move_id:
quantity = self.qty_producing quantity = self.qty_producing
if self.lot_producing_id: if self.lot_producing_id:
move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: ml.lot_id == self.lot_producing_id or not ml.lot_id) move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: not ml.picked and ml.lot_id == self.lot_producing_id or not ml.lot_id)
else: else:
move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: not ml.lot_id) move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: not ml.picked and not ml.lot_id)
# Update reservation and quantity done # Update reservation and quantity done
for ml in move_lines: for ml in move_lines:
rounding = ml.product_uom_id.rounding rounding = ml.product_uom_id.rounding
if float_compare(quantity, 0, precision_rounding=rounding) <= 0: if float_compare(quantity, 0, precision_rounding=rounding) <= 0:
break break
quantity_to_process = min(quantity, ml.reserved_uom_qty - ml.qty_done) quantity_to_process = min(quantity, ml.quantity)
quantity -= quantity_to_process quantity -= quantity_to_process
new_quantity_done = (ml.qty_done + quantity_to_process)
# on which lot of finished product # on which lot of finished product
if float_compare(new_quantity_done, ml.reserved_uom_qty, precision_rounding=rounding) >= 0: if float_compare(quantity_to_process, ml.quantity, precision_rounding=rounding) >= 0:
ml.write({ ml.write({
'qty_done': new_quantity_done, 'quantity': quantity_to_process,
'picked': True,
'lot_id': self.lot_producing_id and self.lot_producing_id.id, 'lot_id': self.lot_producing_id and self.lot_producing_id.id,
}) })
else: else:
new_qty_reserved = ml.reserved_uom_qty - new_quantity_done
default = {
'reserved_uom_qty': new_quantity_done,
'qty_done': new_quantity_done,
'lot_id': self.lot_producing_id and self.lot_producing_id.id,
}
ml.write({ ml.write({
'reserved_uom_qty': new_qty_reserved, 'quantity': quantity_to_process,
'qty_done': 0 'picked': True,
'lot_id': self.lot_producing_id and self.lot_producing_id.id,
}) })
ml.copy(default=default)
if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) > 0: if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) > 0:
self.env['stock.move.line'].create({ self.env['stock.move.line'].create({
@@ -143,18 +136,13 @@ class MrpProduction(models.Model):
'product_id': self.product_id.id, 'product_id': self.product_id.id,
'location_id': subcontract_move_id.location_id.id, 'location_id': subcontract_move_id.location_id.id,
'location_dest_id': subcontract_move_id.location_dest_id.id, 'location_dest_id': subcontract_move_id.location_dest_id.id,
'reserved_uom_qty': 0,
'product_uom_id': self.product_uom_id.id, 'product_uom_id': self.product_uom_id.id,
'qty_done': quantity, 'quantity': quantity,
'picked': True,
'lot_id': self.lot_producing_id and self.lot_producing_id.id, 'lot_id': self.lot_producing_id and self.lot_producing_id.id,
}) })
if not self._get_quantity_to_backorder(): if not self._get_quantity_to_backorder():
ml_reserved = subcontract_move_id.move_line_ids.filtered(lambda ml: subcontract_move_id.move_line_ids.filtered(lambda ml: not ml.picked).unlink()
float_is_zero(ml.qty_done, precision_rounding=ml.product_uom_id.rounding) and
not float_is_zero(ml.reserved_uom_qty, precision_rounding=ml.product_uom_id.rounding))
ml_reserved.unlink()
for ml in subcontract_move_id.move_line_ids:
ml.reserved_uom_qty = ml.qty_done
subcontract_move_id._recompute_state() subcontract_move_id._recompute_state()
def _subcontracting_filter_to_done(self): def _subcontracting_filter_to_done(self):
+3 -15
View File
@@ -38,7 +38,7 @@ class StockMove(models.Model):
for move in self: for move in self:
if not move.is_subcontract: if not move.is_subcontract:
continue continue
if float_is_zero(move.quantity_done, precision_rounding=move.product_uom.rounding): if float_is_zero(move.quantity, precision_rounding=move.product_uom.rounding):
continue continue
productions = move._get_subcontract_production() productions = move._get_subcontract_production()
if not productions or (productions[:1].consumption == 'strict' and not productions[:1]._has_tracked_component()): if not productions or (productions[:1].consumption == 'strict' and not productions[:1]._has_tracked_component()):
@@ -110,10 +110,7 @@ class StockMove(models.Model):
'show_lots_text': False, 'show_lots_text': False,
}) })
elif self.env.user.has_group('base.group_portal'): elif self.env.user.has_group('base.group_portal'):
if self.show_reserved: action['views'] = [(self.env.ref('mrp_subcontracting.mrp_subcontracting_view_stock_move_operations').id, 'form')]
action['views'] = [(self.env.ref('mrp_subcontracting.mrp_subcontracting_view_stock_move_operations').id, 'form')]
else:
action['views'] = [(self.env.ref('mrp_subcontracting.mrp_subcontracting_view_stock_move_nosuggest_operations').id, 'form')]
return action return action
def action_show_subcontract_details(self): def action_show_subcontract_details(self):
@@ -135,10 +132,6 @@ class StockMove(models.Model):
'context': ctx 'context': ctx
} }
def _set_quantities_to_reservation(self):
move_untouchable = self.filtered(lambda m: m.is_subcontract and m._get_subcontract_production()._has_tracked_component())
return super(StockMove, self - move_untouchable)._set_quantities_to_reservation()
def _action_cancel(self): def _action_cancel(self):
for move in self: for move in self:
if move.is_subcontract: if move.is_subcontract:
@@ -225,11 +218,6 @@ class StockMove(models.Model):
vals['location_id'] = self.location_id.id vals['location_id'] = self.location_id.id
return vals return vals
def _should_bypass_set_qty_producing(self):
if (self.production_id | self.raw_material_production_id)._get_subcontract_move():
return False
return super()._should_bypass_set_qty_producing()
def _should_bypass_reservation(self, forced_location=False): def _should_bypass_reservation(self, forced_location=False):
""" If the move is subcontracted then ignore the reservation. """ """ If the move is subcontracted then ignore the reservation. """
should_bypass_reservation = super()._should_bypass_reservation(forced_location=forced_location) should_bypass_reservation = super()._should_bypass_reservation(forced_location=forced_location)
@@ -239,7 +227,7 @@ class StockMove(models.Model):
def _update_subcontract_order_qty(self, new_quantity): def _update_subcontract_order_qty(self, new_quantity):
for move in self: for move in self:
move_quantity = move.quantity_done if move.from_immediate_transfer else move.product_uom_qty move_quantity = move.product_uom_qty
quantity_to_remove = move_quantity - new_quantity quantity_to_remove = move_quantity - new_quantity
if float_is_zero(quantity_to_remove, precision_rounding=move.product_uom.rounding): if float_is_zero(quantity_to_remove, precision_rounding=move.product_uom.rounding):
continue continue
@@ -13,10 +13,6 @@ class StockPicking(models.Model):
_name = 'stock.picking' _name = 'stock.picking'
_inherit = 'stock.picking' _inherit = 'stock.picking'
# override existing field domains to prevent suboncontracting production lines from showing in Detailed Operations tab
move_line_nosuggest_ids = fields.One2many(
domain=['&', '|', ('location_dest_id.usage', '!=', 'production'), ('move_id.picking_code', '!=', 'outgoing'),
'|', ('reserved_qty', '=', 0.0), '&', ('reserved_qty', '!=', 0.0), ('qty_done', '!=', 0.0)])
move_line_ids_without_package = fields.One2many( move_line_ids_without_package = fields.One2many(
domain=['&', '|', ('location_dest_id.usage', '!=', 'production'), ('move_id.picking_code', '!=', 'outgoing'), domain=['&', '|', ('location_dest_id.usage', '!=', 'production'), ('move_id.picking_code', '!=', 'outgoing'),
'|', ('package_level_id', '=', False), ('picking_type_entire_packs', '=', False)]) '|', ('package_level_id', '=', False), ('picking_type_entire_packs', '=', False)])
@@ -49,7 +45,7 @@ class StockPicking(models.Model):
productions = move._get_subcontract_production() productions = move._get_subcontract_production()
recorded_productions = productions.filtered(lambda p: p._has_been_recorded()) recorded_productions = productions.filtered(lambda p: p._has_been_recorded())
recorded_qty = sum(recorded_productions.mapped('qty_producing')) recorded_qty = sum(recorded_productions.mapped('qty_producing'))
sm_done_qty = sum(productions._get_subcontract_move().mapped('quantity_done')) sm_done_qty = sum(productions._get_subcontract_move().filtered(lambda m: m.picked).mapped('quantity'))
rounding = self.env['decimal.precision'].precision_get('Product Unit of Measure') rounding = self.env['decimal.precision'].precision_get('Product Unit of Measure')
if float_compare(recorded_qty, sm_done_qty, precision_digits=rounding) >= 0: if float_compare(recorded_qty, sm_done_qty, precision_digits=rounding) >= 0:
continue continue
@@ -59,7 +55,7 @@ class StockPicking(models.Model):
if len(production) > 1: if len(production) > 1:
raise UserError(_("There shouldn't be multiple productions to record for the same subcontracted move.")) raise UserError(_("There shouldn't be multiple productions to record for the same subcontracted move."))
# Manage additional quantities # Manage additional quantities
quantity_done_move = move.product_uom._compute_quantity(move.quantity_done, production.product_uom_id) quantity_done_move = move.product_uom._compute_quantity(move.quantity, production.product_uom_id)
if float_compare(production.product_qty, quantity_done_move, precision_rounding=production.product_uom_id.rounding) == -1: if float_compare(production.product_qty, quantity_done_move, precision_rounding=production.product_uom_id.rounding) == -1:
change_qty = self.env['change.production.qty'].create({ change_qty = self.env['change.production.qty'].create({
'mo_id': production.id, 'mo_id': production.id,
@@ -67,14 +63,15 @@ class StockPicking(models.Model):
}) })
change_qty.with_context(skip_activity=True).change_prod_qty() change_qty.with_context(skip_activity=True).change_prod_qty()
# Create backorder MO for each move lines # Create backorder MO for each move lines
amounts = [move_line.qty_done for move_line in move.move_line_ids] amounts = [move_line.quantity for move_line in move.move_line_ids]
len_amounts = len(amounts) len_amounts = len(amounts)
productions = production._split_productions({production: amounts}, set_consumed_qty=True) productions = production._split_productions({production: amounts}, set_consumed_qty=True)
productions.move_finished_ids.move_line_ids.write({'qty_done': 0}) productions.move_finished_ids.move_line_ids.write({'quantity': 0})
for production, move_line in zip(productions, move.move_line_ids): for production, move_line in zip(productions, move.move_line_ids):
if move_line.lot_id: if move_line.lot_id:
production.lot_producing_id = move_line.lot_id production.lot_producing_id = move_line.lot_id
production.qty_producing = production.product_qty production.qty_producing = production.product_qty
production._set_qty_producing()
productions[:len_amounts].subcontracting_has_been_recorded = True productions[:len_amounts].subcontracting_has_been_recorded = True
for picking in self: for picking in self:
@@ -138,7 +135,7 @@ class StockPicking(models.Model):
'bom_id': bom.id, 'bom_id': bom.id,
'location_src_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id, 'location_src_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id, 'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'product_qty': subcontract_move.from_immediate_transfer and subcontract_move.quantity_done or subcontract_move.product_qty, 'product_qty': subcontract_move.product_qty or subcontract_move.quantity,
'picking_type_id': warehouse.subcontracting_type_id.id, 'picking_type_id': warehouse.subcontracting_type_id.id,
'date_start': subcontract_move.date - relativedelta(days=bom.produce_delay) 'date_start': subcontract_move.date - relativedelta(days=bom.produce_delay)
} }
@@ -150,13 +147,11 @@ class StockPicking(models.Model):
# do not create extra production for move that have their quantity updated # do not create extra production for move that have their quantity updated
if move.move_orig_ids.production_id: if move.move_orig_ids.production_id:
continue continue
quantity = move.from_immediate_transfer and move.quantity_done or move.product_qty quantity = move.product_qty or move.quantity
if float_compare(quantity, 0, precision_rounding=move.product_uom.rounding) <= 0: if float_compare(quantity, 0, precision_rounding=move.product_uom.rounding) <= 0:
# If a subcontracted amount is decreased, don't create a MO that would be for a negative value. # If a subcontracted amount is decreased, don't create a MO that would be for a negative value.
continue continue
mo = self.env['mrp.production'].with_company(move.company_id).create(self._prepare_subcontract_mo_vals(move, bom)) mo = self.env['mrp.production'].with_company(move.company_id).create(self._prepare_subcontract_mo_vals(move, bom))
self.env['stock.move'].create(mo._get_moves_raw_values())
self.env['stock.move'].create(mo._get_moves_finished_values())
mo.date_finished = move.date # Avoid to have the picking late depending of the MO mo.date_finished = move.date # Avoid to have the picking late depending of the MO
mo.action_confirm() mo.action_confirm()
@@ -40,11 +40,10 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 1 move.product_uom_qty = 1
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# Nothing should be tracked # Nothing should be tracked
self.assertTrue(all(m.product_uom_qty == m.reserved_availability for m in picking_receipt.move_ids)) self.assertTrue(all(m.product_uom_qty == m.quantity for m in picking_receipt.move_ids))
self.assertEqual(picking_receipt.state, 'assigned') self.assertEqual(picking_receipt.state, 'assigned')
self.assertEqual(picking_receipt.display_action_record_components, 'hide') self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -72,7 +71,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)]) picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)])
self.assertEqual(len(picking), 1) self.assertEqual(len(picking), 1)
self.assertEqual(picking.picking_type_id, wh.subcontracting_resupply_type_id) self.assertEqual(picking.picking_type_id, wh.subcontracting_resupply_type_id)
picking_receipt.move_ids.quantity_done = 1 picking_receipt.move_ids.quantity = 1
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
@@ -122,7 +122,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
# Nothing should be tracked # Nothing should be tracked
@@ -149,7 +150,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)]) comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)])
self.assertEqual(len(comp2mo), 0) self.assertEqual(len(comp2mo), 0)
picking_receipt.move_ids.quantity_done = 1 picking_receipt.move_ids.quantity = 1
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
@@ -189,7 +191,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
# Nothing should be tracked # Nothing should be tracked
@@ -215,7 +218,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
# As well as a manufacturing order for `self.comp2` # As well as a manufacturing order for `self.comp2`
comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)]) comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)])
self.assertEqual(len(comp2mo), 1) self.assertEqual(len(comp2mo), 1)
picking_receipt.move_ids.quantity_done = 1 picking_receipt.move_ids.quantity = 1
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
@@ -254,7 +258,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
warehouse = picking_receipt.picking_type_id.warehouse_id warehouse = picking_receipt.picking_type_id.warehouse_id
@@ -322,7 +327,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt1 = picking_form.save() picking_receipt1 = picking_form.save()
# Create a receipt picking from the subcontractor2 # Create a receipt picking from the subcontractor2
@@ -331,7 +337,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = subcontractor_partner2 picking_form.partner_id = subcontractor_partner2
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt2 = picking_form.save() picking_receipt2 = picking_form.save()
mo_pick1 = picking_receipt1.move_ids.mapped('move_orig_ids.production_id') mo_pick1 = picking_receipt1.move_ids.mapped('move_orig_ids.production_id')
@@ -383,21 +390,21 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 1 move.product_uom_qty = 1
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.move_ids.quantity_done = 3.0 picking_receipt.move_ids.quantity = 3.0
picking_receipt.move_ids.picked = True
picking_receipt._action_done() picking_receipt._action_done()
mo = picking_receipt._get_subcontract_production() mo = picking_receipt._get_subcontract_production()
move_comp1 = mo.move_raw_ids.filtered(lambda m: m.product_id == self.comp1) move_comp1 = mo.move_raw_ids.filtered(lambda m: m.product_id == self.comp1)
move_comp3 = mo.move_raw_ids.filtered(lambda m: m.product_id == comp3) move_comp3 = mo.move_raw_ids.filtered(lambda m: m.product_id == comp3)
self.assertEqual(sum(move_comp1.mapped('product_uom_qty')), 3.0) self.assertEqual(sum(move_comp1.mapped('product_uom_qty')), 3.0)
self.assertEqual(sum(move_comp3.mapped('product_uom_qty')), 6.0) self.assertEqual(sum(move_comp3.mapped('product_uom_qty')), 6.0)
self.assertEqual(sum(move_comp1.mapped('quantity_done')), 3.0) self.assertEqual(sum(move_comp1.mapped('quantity')), 3.0)
self.assertEqual(sum(move_comp3.mapped('quantity_done')), 6.0) self.assertEqual(sum(move_comp3.mapped('quantity')), 6.0)
move_finished = mo.move_finished_ids move_finished = mo.move_finished_ids
self.assertEqual(sum(move_finished.mapped('product_uom_qty')), 3.0) self.assertEqual(sum(move_finished.mapped('product_uom_qty')), 3.0)
self.assertEqual(sum(move_finished.mapped('quantity_done')), 3.0) self.assertEqual(sum(move_finished.mapped('quantity')), 3.0)
def test_flow_8(self): def test_flow_8(self):
resupply_sub_on_order_route = self.env['stock.route'].search([('name', '=', 'Resupply Subcontractor on Order')]) resupply_sub_on_order_route = self.env['stock.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
@@ -411,10 +418,10 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 5 move.product_uom_qty = 5
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.move_ids.quantity_done = 3 picking_receipt.move_ids.quantity = 3
picking_receipt.move_ids.picked = True
backorder_wiz = picking_receipt.button_validate() backorder_wiz = picking_receipt.button_validate()
backorder_wiz = Form(self.env[backorder_wiz['res_model']].with_context(backorder_wiz['context'])).save() backorder_wiz = Form(self.env[backorder_wiz['res_model']].with_context(backorder_wiz['context'])).save()
backorder_wiz.process() backorder_wiz.process()
@@ -430,7 +437,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertTrue(backorder_mo) self.assertTrue(backorder_mo)
self.assertEqual(backorder_mo.product_uom_qty, 2) self.assertEqual(backorder_mo.product_uom_qty, 2)
self.assertEqual(backorder_mo.qty_produced, 0) self.assertEqual(backorder_mo.qty_produced, 0)
backorder.move_ids.quantity_done = 2 backorder.move_ids.quantity = 2
backorder.move_ids.picked = True
backorder._action_done() backorder._action_done()
self.assertTrue(picking_receipt.move_ids.move_orig_ids[0].production_id.state == 'done') self.assertTrue(picking_receipt.move_ids.move_orig_ids[0].production_id.state == 'done')
@@ -450,7 +458,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = 5 move.quantity = 5
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_delivery = self.env['stock.move'].search([ picking_delivery = self.env['stock.move'].search([
@@ -484,7 +493,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 1 move.product_uom_qty = 1
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# Check that a manufacturing order is created # Check that a manufacturing order is created
@@ -502,7 +510,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 1 move.product_uom_qty = 1
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
self.assertEqual(picking_receipt.display_action_record_components, 'facultative') self.assertEqual(picking_receipt.display_action_record_components, 'facultative')
action = picking_receipt.action_record_components() action = picking_receipt.action_record_components()
@@ -511,11 +518,11 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
with mo_form.move_line_raw_ids.edit(0) as ml: with mo_form.move_line_raw_ids.edit(0) as ml:
self.assertEqual(ml.product_id, self.comp1) self.assertEqual(ml.product_id, self.comp1)
self.assertEqual(ml.qty_done, 1) self.assertEqual(ml.quantity, 1)
ml.qty_done = 2 ml.quantity = 2
mo = mo_form.save() mo = mo_form.save()
mo.subcontracting_record_component() mo.subcontracting_record_component()
self.assertEqual(mo.move_raw_ids[0].move_line_ids.qty_done, 2) self.assertEqual(mo.move_raw_ids[0].move_line_ids.quantity, 2)
# We should not be able to call the 'record_components' button # We should not be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, 'hide') self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -536,7 +543,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 1 move.product_uom_qty = 1
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
self.assertEqual(picking_receipt.display_action_record_components, 'facultative') self.assertEqual(picking_receipt.display_action_record_components, 'facultative')
@@ -546,8 +552,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
mo_form.qty_producing = 1 mo_form.qty_producing = 1
with mo_form.move_line_raw_ids.edit(0) as ml: with mo_form.move_line_raw_ids.edit(0) as ml:
self.assertEqual(ml.product_id, self.comp1) self.assertEqual(ml.product_id, self.comp1)
self.assertEqual(ml.qty_done, 1) self.assertEqual(ml.quantity, 1)
ml.qty_done = 2 ml.quantity = 2
mo = mo_form.save() mo = mo_form.save()
action_warning = mo.subcontracting_record_component() action_warning = mo.subcontracting_record_component()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action_warning['context'])) warning = Form(self.env['mrp.consumption.warning'].with_context(**action_warning['context']))
@@ -559,7 +565,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
warning = warning.save() warning = warning.save()
warning.action_confirm() warning.action_confirm()
self.assertEqual(mo.move_raw_ids[0].move_line_ids.qty_done, 2) self.assertEqual(mo.move_raw_ids[0].move_line_ids.quantity, 2)
# We should not be able to call the 'record_components' button # We should not be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, 'hide') self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -614,7 +620,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
def test_several_backorders(self): def test_several_backorders(self):
def process_picking(picking, qty): def process_picking(picking, qty):
picking.move_ids.quantity_done = qty picking.move_ids.quantity = qty
picking.move_ids.picked = True
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -645,7 +652,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = finished move.product_id = finished
move.product_uom_qty = 5 move.product_uom_qty = 5
picking = picking_form.save() picking = picking_form.save()
picking.action_reset_draft()
picking.action_confirm() picking.action_confirm()
supply_picking = self.env['mrp.production'].search([('bom_id', '=', bom.id)]).picking_ids supply_picking = self.env['mrp.production'].search([('bom_id', '=', bom.id)]).picking_ids
@@ -657,8 +663,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
process_picking(backorder01, 1) process_picking(backorder01, 1)
backorder02 = backorder01.backorder_ids backorder02 = backorder01.backorder_ids
process_picking(backorder02, 0) self.assertEqual(backorder02.move_ids.quantity, 2.75)
self.assertEqual(backorder02.move_ids.quantity_done, 2.75)
self.assertEqual(self.env['mrp.production'].search_count([('bom_id', '=', bom.id)]), 3) self.assertEqual(self.env['mrp.production'].search_count([('bom_id', '=', bom.id)]), 3)
@@ -698,7 +703,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
with Form(picking) as picking_form: with Form(picking) as picking_form:
with picking_form.move_ids_without_package.edit(0) as line: with picking_form.move_ids_without_package.edit(0) as line:
line.quantity_done = done_qty line.quantity = done_qty
line.picked = True
action = picking.action_record_components() action = picking.action_record_components()
mo = self.env['mrp.production'].browse(action['res_id']) mo = self.env['mrp.production'].browse(action['res_id'])
@@ -715,7 +721,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
productions = self.env['mrp.production'].search([('product_id', '=', finished.id)], order='id') productions = self.env['mrp.production'].search([('product_id', '=', finished.id)], order='id')
self.assertEqual(productions.mapped('state'), ['done', 'done']) self.assertEqual(productions.mapped('state'), ['done', 'done'])
self.assertEqual(productions.move_raw_ids.mapped('quantity_done'), [2.0, 3.0]) self.assertEqual(productions.move_raw_ids.mapped('quantity'), [2.0, 3.0])
def test_subcontracting_rules_replication(self): def test_subcontracting_rules_replication(self):
""" Test activate/archive subcontracting location rules.""" """ Test activate/archive subcontracting location rules."""
@@ -802,9 +808,9 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product move.product_id = self.finished_product
move.quantity_done = 1 move.quantity = 1
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# We should be able to call the 'record_components' button # We should be able to call the 'record_components' button
@@ -881,10 +887,10 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product move.product_id = self.finished_product
move.quantity_done = nb_finished_product move.quantity = nb_finished_product
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# We shouldn't be able to call the 'record_components' button # We shouldn't be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, 'hide') self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -892,13 +898,13 @@ class TestSubcontractingTracking(TransactionCase):
wh = picking_receipt.picking_type_id.warehouse_id wh = picking_receipt.picking_type_id.warehouse_id
lot_names_finished = [f"subtracked_{i}" for i in range(nb_finished_product)] lot_names_finished = [f"subtracked_{i}" for i in range(nb_finished_product)]
move_details = Form(picking_receipt.move_ids, view='stock.view_stock_move_nosuggest_operations') move_details = Form(picking_receipt.move_ids, view='stock.view_stock_move_operations')
for lot_name in lot_names_finished: for lot_name in lot_names_finished:
with move_details.move_line_nosuggest_ids.new() as ml: with move_details.move_line_ids.new() as ml:
ml.qty_done = 1 ml.quantity = 1
ml.lot_name = lot_name ml.lot_name = lot_name
move_details.save() move_details.save()
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
# Check the created manufacturing order # Check the created manufacturing order
# Should have one mo by serial number # Should have one mo by serial number
@@ -929,9 +935,8 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product move.product_id = self.finished_product
move.quantity_done = todo_nb move.quantity = todo_nb
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# We should be able to call the 'record_components' button # We should be able to call the 'record_components' button
@@ -981,6 +986,7 @@ class TestSubcontractingTracking(TransactionCase):
# We should not be able to call the 'record_components' button # We should not be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, 'hide') self.assertEqual(picking_receipt.display_action_record_components, 'hide')
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
self.assertEqual(mo.procurement_group_id.mrp_production_ids.mapped("state"), ['done'] * todo_nb) self.assertEqual(mo.procurement_group_id.mrp_production_ids.mapped("state"), ['done'] * todo_nb)
@@ -1035,7 +1041,6 @@ class TestSubcontractingTracking(TransactionCase):
move.product_id = finished_product move.product_id = finished_product
move.product_uom_qty = todo_nb move.product_uom_qty = todo_nb
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)]) mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
@@ -1043,9 +1048,7 @@ class TestSubcontractingTracking(TransactionCase):
# Process the delivery of the components # Process the delivery of the components
compo_picking = mo.picking_ids compo_picking = mo.picking_ids
compo_picking.action_assign() compo_picking.action_assign()
wizard_data = compo_picking.button_validate() compo_picking.button_validate()
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
wizard.process()
for qty in [3, 1]: for qty in [3, 1]:
# Record the receiption of <qty> finished products # Record the receiption of <qty> finished products
@@ -1108,9 +1111,9 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = finished_product move.product_id = finished_product
move.quantity_done = todo_nb move.quantity = todo_nb
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)]) mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
@@ -1118,16 +1121,13 @@ class TestSubcontractingTracking(TransactionCase):
# Process the delivery of the components # Process the delivery of the components
compo_picking = mo.picking_ids compo_picking = mo.picking_ids
compo_picking.action_assign() compo_picking.action_assign()
wizard_data = compo_picking.button_validate() compo_picking.button_validate()
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
wizard.process()
picking_receipt = self.env['stock.picking'].search([('partner_id', '=', self.subcontractor_partner1.id), ('state', '!=', 'done')]) picking_receipt = self.env['stock.picking'].search([('partner_id', '=', self.subcontractor_partner1.id), ('state', '!=', 'done')])
for sn in finished_serials: for sn in finished_serials:
# Record the production of each serial number separately # Record the production of each serial number separately
action = picking_receipt.action_record_components() action = picking_receipt.action_record_components()
mo = self.env['mrp.production'].browse(action['res_id']) mo = self.env['mrp.production'].browse(action['res_id'])
self.assertEqual(mo.move_raw_ids.state, 'assigned')
mo_form = Form(mo.with_context(**action['context']), view=action['view_id']) mo_form = Form(mo.with_context(**action['context']), view=action['view_id'])
mo_form.qty_producing = 1 mo_form.qty_producing = 1
mo_form.lot_producing_id = sn mo_form.lot_producing_id = sn
@@ -1210,7 +1210,6 @@ class TestSubcontractingPortal(TransactionCase):
move.product_id = self.finished_product move.product_id = self.finished_product
move.product_uom_qty = 2 move.product_uom_qty = 2
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# Using the subcontractor (portal user) # Using the subcontractor (portal user)
@@ -1259,7 +1258,7 @@ class TestSubcontractingPortal(TransactionCase):
ml.product_id = self.comp1_sn ml.product_id = self.comp1_sn
ml.lot_id = serial3 ml.lot_id = serial3
with mo_form.move_line_raw_ids.edit(1) as ml: with mo_form.move_line_raw_ids.edit(1) as ml:
ml.qty_done = 2 ml.quantity = 2
# The portal user should not be able to add a product not in the BoM # The portal user should not be able to add a product not in the BoM
with self.assertRaises(AccessError): with self.assertRaises(AccessError):
with mo_form.move_line_raw_ids.new() as ml: with mo_form.move_line_raw_ids.new() as ml:
@@ -1277,11 +1276,11 @@ class TestSubcontractingPortal(TransactionCase):
# Validation from the backend user # Validation from the backend user
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(mo.state, 'done') self.assertEqual(mo.state, 'done')
self.assertEqual(mo.move_line_raw_ids[0].qty_done, 1) self.assertEqual(mo.move_line_raw_ids[0].quantity, 1)
self.assertEqual(mo.move_line_raw_ids[0].lot_id, serial2) self.assertEqual(mo.move_line_raw_ids[0].lot_id, serial2)
self.assertEqual(mo.move_line_raw_ids[1].qty_done, 1) self.assertEqual(mo.move_line_raw_ids[1].quantity, 1)
self.assertEqual(mo.move_line_raw_ids[1].lot_id, serial3) self.assertEqual(mo.move_line_raw_ids[1].lot_id, serial3)
self.assertEqual(mo.move_line_raw_ids[2].qty_done, 2) self.assertEqual(mo.move_line_raw_ids[2].quantity, 2)
class TestSubcontractingSerialMassReceipt(TransactionCase): class TestSubcontractingSerialMassReceipt(TransactionCase):
def setUp(self): def setUp(self):
@@ -1324,23 +1323,21 @@ class TestSubcontractingSerialMassReceipt(TransactionCase):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = sum(quantities) move.product_uom_qty = sum(quantities)
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
# Process the delivery of the components # Process the delivery of the components
picking_deliver = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]).picking_ids picking_deliver = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]).picking_ids
picking_deliver.action_assign() picking_deliver.action_assign()
picking_deliver.button_validate() picking_deliver.button_validate()
wizard_data = picking_deliver.button_validate()
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
wizard.process()
# Receive # Receive
for quantity in quantities: for quantity in quantities:
# Receive <quantity> finished products # Receive <quantity> finished products
picking_receipt.action_clear_quantities_to_zero()
Form(self.env['stock.assign.serial'].with_context( Form(self.env['stock.assign.serial'].with_context(
default_move_id=picking_receipt.move_ids[0].id, default_move_id=picking_receipt.move_ids[0].id,
default_next_serial_number=self.env['stock.lot']._get_next_serial(picking_receipt.company_id, picking_receipt.move_ids[0].product_id) or 'sn#1', default_next_serial_number=self.env['stock.lot']._get_next_serial(picking_receipt.company_id, picking_receipt.move_ids[0].product_id) or 'sn#1',
default_next_serial_count=quantity, default_next_serial_count=quantity,
)).save().generate_serial_numbers() )).save().generate_serial_numbers()
picking_receipt.move_ids.picked = True
wizard_data = picking_receipt.button_validate() wizard_data = picking_receipt.button_validate()
if wizard_data is not True: if wizard_data is not True:
# Create backorder # Create backorder
@@ -1361,14 +1358,17 @@ class TestSubcontractingSerialMassReceipt(TransactionCase):
picking_form.partner_id = self.subcontractor picking_form.partner_id = self.subcontractor
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = quantity move.product_uom_qty = quantity
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# Receive finished products # Receive finished products
Form(self.env['stock.assign.serial'].with_context( Form(self.env['stock.assign.serial'].with_context(
default_move_id=picking_receipt.move_ids[0].id, default_move_id=picking_receipt.move_ids[0].id,
default_next_serial_number=self.env['stock.lot']._get_next_serial(picking_receipt.company_id, picking_receipt.move_ids[0].product_id) or 'sn#1', default_next_serial_number=self.env['stock.lot']._get_next_serial(picking_receipt.company_id, picking_receipt.move_ids[0].product_id) or 'sn#1',
default_next_serial_count=quantity, default_next_serial_count=quantity,
)).save().generate_serial_numbers() )).save().generate_serial_numbers()
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
self.assertEqual(picking_receipt.state, 'done') self.assertEqual(picking_receipt.state, 'done')
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.raw_material, self.env.ref('stock.stock_location_stock')), 0) self.assertEqual(self.env['stock.quant']._get_available_quantity(self.raw_material, self.env.ref('stock.stock_location_stock')), 0)
@@ -54,9 +54,9 @@ class TestSubcontractingPortalUi(HttpCase):
picking_form.partner_id = self.partner_portal picking_form.partner_id = self.partner_portal
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product move.product_id = self.finished_product
move.quantity_done = 2 move.quantity = 2
move.picked = True
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm() picking_receipt.action_confirm()
self.start_tour("/my/productions", 'subcontracting_portal_tour', login="georges1") self.start_tour("/my/productions", 'subcontracting_portal_tour', login="georges1")
@@ -23,10 +23,7 @@
invisible="tracking not in ('serial', 'lot')" invisible="tracking not in ('serial', 'lot')"
required="tracking in ('serial', 'lot')" required="tracking in ('serial', 'lot')"
context="{'default_product_id': product_id, 'default_company_id': company_id}"/> context="{'default_product_id': product_id, 'default_company_id': company_id}"/>
<field name="reserved_uom_qty" readonly="1" force_save="1"/> <field name="quantity"/>
<field name="qty_done"
decoration-warning="reserved_uom_qty &lt; qty_done"
decoration-success="reserved_uom_qty == qty_done"/>
<field name="product_uom_id" groups="uom.group_uom"/> <field name="product_uom_id" groups="uom.group_uom"/>
</tree> </tree>
</field> </field>
@@ -70,18 +67,7 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="mrp_subcontracting_view_stock_move_nosuggest_operations" model="ir.ui.view">
<field name="name">mrp.subcontracting.stock.move.operations.nosuggest.form</field>
<field name="model">stock.move</field>
<field name="mode">primary</field>
<field name="priority">1000</field>
<field name="inherit_id" ref="stock.view_stock_move_nosuggest_operations" />
<field name="arch" type="xml">
<xpath expr="//field[@name='move_line_nosuggest_ids']" position="attributes">
<attribute name="context">{'tree_view_ref': 'mrp_subcontracting.mrp_subcontracting_view_stock_move_line_operation_tree','default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_company_id': company_id}</attribute>
</xpath>
</field>
</record>
<record id="mrp_subcontracting_move_form_view" model="ir.ui.view"> <record id="mrp_subcontracting_move_form_view" model="ir.ui.view">
<field name="name">mrp.subcontracting.move.form.view</field> <field name="name">mrp.subcontracting.move.form.view</field>
<field name="model">stock.move</field> <field name="model">stock.move</field>
@@ -105,7 +91,7 @@
<group> <group>
<field name="order_finished_lot_id"/> <field name="order_finished_lot_id"/>
<field name="product_uom" groups="uom.group_uom"/> <field name="product_uom" groups="uom.group_uom"/>
<field name="quantity_done" string="Total Consumed" readonly="1"/> <field name="quantity" string="Total Consumed" readonly="1"/>
</group> </group>
<field name="move_line_ids" <field name="move_line_ids"
readonly="state in ['done', 'cancel']" readonly="state in ['done', 'cancel']"
@@ -121,7 +107,7 @@
<field name="location_id" column_invisible="True"/> <field name="location_id" column_invisible="True"/>
<field name="location_dest_id" column_invisible="True"/> <field name="location_dest_id" column_invisible="True"/>
<field name="product_id" readonly="1" force_save="1"/> <field name="product_id" readonly="1" force_save="1"/>
<field name="qty_done"/> <field name="quantity"/>
<field name="lot_id" column_invisible="parent.has_tracking not in ('serial', 'lot')" required="tracking in ('serial', 'lot')" context="{'default_product_id': product_id, 'default_company_id': company_id}" groups="stock.group_production_lot"/> <field name="lot_id" column_invisible="parent.has_tracking not in ('serial', 'lot')" required="tracking in ('serial', 'lot')" context="{'default_product_id': product_id, 'default_company_id': company_id}" groups="stock.group_production_lot"/>
</tree> </tree>
</field> </field>
@@ -152,7 +138,7 @@
<field name="model">stock.move</field> <field name="model">stock.move</field>
<field name="priority">1000</field> <field name="priority">1000</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree delete="0" create="0" decoration-muted="is_done" decoration-warning="quantity_done - product_uom_qty &gt; 0.0001" decoration-success="not is_done and quantity_done - product_uom_qty &lt; 0.0001" js_class="subcontracting_portal_move_list_view"> <tree delete="0" create="0" decoration-muted="is_done" decoration-warning="quantity - product_uom_qty &gt; 0.0001" decoration-success="not is_done and quantity - product_uom_qty &lt; 0.0001" js_class="subcontracting_portal_move_list_view">
<field name="company_id" column_invisible="True"/> <field name="company_id" column_invisible="True"/>
<field name="sequence" column_invisible="True"/> <field name="sequence" column_invisible="True"/>
<field name="product_uom_category_id" column_invisible="True"/> <field name="product_uom_category_id" column_invisible="True"/>
@@ -172,8 +158,7 @@
<field name="raw_material_production_id" column_invisible="True"/> <field name="raw_material_production_id" column_invisible="True"/>
<field name="product_id" required="1" readonly="state == 'done'"/> <field name="product_id" required="1" readonly="state == 'done'"/>
<field name="order_finished_lot_id"/> <field name="order_finished_lot_id"/>
<field name="reserved_availability" invisible="is_done" string="Reserved"/> <field name="quantity" string="Consumed" readonly="1"/>
<field name="quantity_done" string="Consumed" readonly="1"/>
<field name="product_uom" groups="uom.group_uom"/> <field name="product_uom" groups="uom.group_uom"/>
</tree> </tree>
</field> </field>
@@ -32,7 +32,7 @@
<field name="product_packaging_id" groups="product.group_stock_packaging"/> <field name="product_packaging_id" groups="product.group_stock_packaging"/>
<field name="product_uom_qty" string="Demand" readonly="1"/> <field name="product_uom_qty" string="Demand" readonly="1"/>
<field name="product_qty" readonly="1" column_invisible="True"/> <field name="product_qty" readonly="1" column_invisible="True"/>
<field name="quantity_done" string="Done"/> <field name="quantity" string="Done"/>
<field name="product_uom" groups="uom.group_uom"/> <field name="product_uom" groups="uom.group_uom"/>
<button name="action_show_details" type="object" icon="fa-list" title="Details" <button name="action_show_details" type="object" icon="fa-list" title="Details"
invisible="not show_details_visible" context="{'is_subcontracting_portal': 1}"/> invisible="not show_details_visible" context="{'is_subcontracting_portal': 1}"/>
@@ -8,7 +8,7 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production' _inherit = 'mrp.production'
def _cal_price(self, consumed_moves): def _cal_price(self, consumed_moves):
finished_move = self.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity_done > 0) finished_move = self.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity > 0)
# Take the price unit of the reception move # Take the price unit of the reception move
last_done_receipt = finished_move.move_dest_ids.filtered(lambda m: m.state == 'done')[-1:] last_done_receipt = finished_move.move_dest_ids.filtered(lambda m: m.state == 'done')[-1:]
if last_done_receipt.is_subcontract: if last_done_receipt.is_subcontract:
@@ -36,7 +36,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
}) })
move1._action_confirm() move1._action_confirm()
move1._action_assign() move1._action_assign()
move1.move_line_ids.qty_done = 10.0 move1.move_line_ids.quantity = 10.0
move1.picked = True
move1._action_done() move1._action_done()
move2 = self.env['stock.move'].create({ move2 = self.env['stock.move'].create({
'name': 'IN 10 units @ 20.00 per unit', 'name': 'IN 10 units @ 20.00 per unit',
@@ -49,7 +50,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
}) })
move2._action_confirm() move2._action_confirm()
move2._action_assign() move2._action_assign()
move2.move_line_ids.qty_done = 10.0 move2.move_line_ids.quantity = 10.0
move2.picked = True
move2._action_done() move2._action_done()
all_amls_ids = self.env['account.move.line'].search([]).ids all_amls_ids = self.env['account.move.line'].search([]).ids
@@ -66,7 +68,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.action_confirm() picking_receipt.action_confirm()
# Suppose the additional cost changes: # Suppose the additional cost changes:
picking_receipt.move_ids.price_unit = 30.0 picking_receipt.move_ids.price_unit = 30.0
picking_receipt.move_ids.quantity_done = 1.0 picking_receipt.move_ids.quantity = 1.0
picking_receipt.move_ids.picked = True
picking_receipt._action_done() picking_receipt._action_done()
mo = picking_receipt._get_subcontract_production() mo = picking_receipt._get_subcontract_production()
@@ -106,7 +109,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.move_ids.price_unit = 0 picking_receipt.move_ids.price_unit = 0
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.move_ids.quantity_done = 1.0 picking_receipt.move_ids.quantity = 1.0
picking_receipt.move_ids.picked = True
picking_receipt._action_done() picking_receipt._action_done()
mo = picking_receipt._get_subcontract_production() mo = picking_receipt._get_subcontract_production()
@@ -168,7 +172,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
}) })
move1._action_confirm() move1._action_confirm()
move1._action_assign() move1._action_assign()
move1.move_line_ids.qty_done = 10.0 move1.move_line_ids.quantity = 10.0
move1.picked = True
move1._action_done() move1._action_done()
move2 = self.env['stock.move'].create({ move2 = self.env['stock.move'].create({
'name': 'IN 10 units @ 20.00 per unit', 'name': 'IN 10 units @ 20.00 per unit',
@@ -181,7 +186,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
}) })
move2._action_confirm() move2._action_confirm()
move2._action_assign() move2._action_assign()
move2.move_line_ids.qty_done = 10.0 move2.move_line_ids.quantity = 10.0
move2.picked = True
move2._action_done() move2._action_done()
all_amls_ids = self.env['account.move.line'].search([]).ids all_amls_ids = self.env['account.move.line'].search([]).ids
@@ -195,7 +201,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.move_ids.price_unit = 30.0 picking_receipt.move_ids.price_unit = 30.0
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.move_ids.quantity_done = 1.0 picking_receipt.move_ids.quantity = 1.0
picking_receipt.move_ids.picked = True
picking_receipt._action_done() picking_receipt._action_done()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)]) amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@@ -231,12 +238,12 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1 picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished move.product_id = self.finished
move.quantity_done = todo_nb move.quantity = todo_nb
picking_receipt = picking_form.save() picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
# Mimic the extra cost on the po line # Mimic the extra cost on the po line
picking_receipt.move_ids.price_unit = 50 picking_receipt.move_ids.price_unit = 50
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# We should be able to call the 'record_components' button # We should be able to call the 'record_components' button
self.assertTrue(picking_receipt.display_action_record_components) self.assertTrue(picking_receipt.display_action_record_components)
@@ -275,7 +282,7 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
mo.subcontracting_record_component() mo.subcontracting_record_component()
# We should not be able to call the 'record_components' button # We should not be able to call the 'record_components' button
picking_receipt.move_ids.picked = True
picking_receipt.button_validate() picking_receipt.button_validate()
f_layers = self.finished.stock_valuation_layer_ids f_layers = self.finished.stock_valuation_layer_ids
@@ -307,7 +314,6 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished move.product_id = self.finished
move.product_uom_qty = 10 move.product_uom_qty = 10
receipt = receipt_form.save() receipt = receipt_form.save()
receipt.action_reset_draft()
# add an extra cost # add an extra cost
receipt.move_ids.price_unit = 50 receipt.move_ids.price_unit = 50
receipt.action_confirm() receipt.action_confirm()
@@ -371,7 +377,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.move_ids.price_unit = 15.0 picking_receipt.move_ids.price_unit = 15.0
picking_receipt.action_confirm() picking_receipt.action_confirm()
picking_receipt.move_ids.quantity_done = 1.0 picking_receipt.move_ids.quantity = 1.0
picking_receipt.move_ids.picked = True
picking_receipt._action_done() picking_receipt._action_done()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)]) amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@@ -69,8 +69,7 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
po.button_confirm() po.button_confirm()
delivery = po.picking_ids delivery = po.picking_ids
res = delivery.button_validate() delivery.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
stock_in_acc_id = self.categ_fifo_auto.property_stock_account_input_categ_id.id stock_in_acc_id = self.categ_fifo_auto.property_stock_account_input_categ_id.id
stock_out_acc_id = self.categ_fifo_auto.property_stock_account_output_categ_id.id stock_out_acc_id = self.categ_fifo_auto.property_stock_account_output_categ_id.id
@@ -104,7 +103,8 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
return_wizard = return_form.save() return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns() return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id) return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.quantity = 1
return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)]) amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@@ -125,7 +125,8 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
return_wizard = return_form.save() return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns() return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id) return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.quantity = 1
return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)]) amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@@ -241,7 +241,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id) self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id)
delivery = po.picking_ids delivery = po.picking_ids
delivery.move_line_ids.qty_done = 2.0 delivery.move_line_ids.quantity = 2.0
delivery.move_ids.picked = True
delivery.button_validate() delivery.button_validate()
self.assertEqual(delivery.state, 'done') self.assertEqual(delivery.state, 'done')
@@ -259,7 +260,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
return_picking_id, _pick_type_id = return_wizard._create_returns() return_picking_id, _pick_type_id = return_wizard._create_returns()
delivery_return01 = self.env['stock.picking'].browse(return_picking_id) delivery_return01 = self.env['stock.picking'].browse(return_picking_id)
delivery_return01.move_line_ids.qty_done = 1.0 delivery_return01.move_line_ids.quantity = 1.0
delivery_return01.move_ids.picked = True
delivery_return01.button_validate() delivery_return01.button_validate()
self.assertEqual(delivery_return01.state, 'done') self.assertEqual(delivery_return01.state, 'done')
@@ -276,7 +278,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
return_picking_id, _pick_type_id = return_wizard._create_returns() return_picking_id, _pick_type_id = return_wizard._create_returns()
delivery_return02 = self.env['stock.picking'].browse(return_picking_id) delivery_return02 = self.env['stock.picking'].browse(return_picking_id)
delivery_return02.move_line_ids.qty_done = 1.0 delivery_return02.move_line_ids.quantity = 1.0
delivery_return02.move_ids.picked = True
delivery_return02.button_validate() delivery_return02.button_validate()
self.assertEqual(delivery_return02.state, 'done') self.assertEqual(delivery_return02.state, 'done')
@@ -343,7 +346,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(delivery.location_dest_id, super_subcontractor.property_stock_subcontractor) self.assertEqual(delivery.location_dest_id, super_subcontractor.property_stock_subcontractor)
self.assertTrue(delivery.is_dropship) self.assertTrue(delivery.is_dropship)
delivery.move_line_ids.qty_done = 1.0 delivery.move_line_ids.quantity = 1.0
delivery.move_ids.picked = True
delivery.button_validate() delivery.button_validate()
self.assertEqual(po.order_line.qty_received, 1.0) self.assertEqual(po.order_line.qty_received, 1.0)
@@ -57,16 +57,12 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
# Deliver the first one # Deliver the first one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[0]) picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[0])
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
self.assertEqual(sale_order.order_line.qty_delivered, 0) self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Deliver the third one # Deliver the third one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[2]) picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[2])
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
self.assertEqual(sale_order.order_line.qty_delivered, 0) self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Cancel the second one # Cancel the second one
@@ -106,9 +102,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(sale_order.order_line.qty_delivered, 0.0) self.assertEqual(sale_order.order_line.qty_delivered, 0.0)
picking = sale_order.picking_ids picking = sale_order.picking_ids
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
self.assertEqual(sale_order.order_line.qty_delivered, 1.0) self.assertEqual(sale_order.order_line.qty_delivered, 1.0)
for case in ['return', 'deliver again']: for case in ['return', 'deliver again']:
@@ -120,9 +114,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
picking = self.env['stock.picking'].browse(action['res_id']) picking = self.env['stock.picking'].browse(action['res_id'])
self.assertEqual(sale_order.order_line.qty_delivered, delivered_before_case, "Incorrect delivered qty for case '%s'" % case) self.assertEqual(sale_order.order_line.qty_delivered, delivered_before_case, "Incorrect delivered qty for case '%s'" % case)
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
self.assertEqual(sale_order.order_line.qty_delivered, delivered_after_case, "Incorrect delivered qty for case '%s'" % case) self.assertEqual(sale_order.order_line.qty_delivered, delivered_after_case, "Incorrect delivered qty for case '%s'" % case)
def test_partial_return_kit_and_delivered_qty(self): def test_partial_return_kit_and_delivered_qty(self):
@@ -166,7 +158,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4") self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
picking01 = sale_order.picking_ids picking01 = sale_order.picking_ids
picking01.move_ids.quantity_done = 2 picking01.move_ids.quantity = 2
picking01.move_ids.picked = True
action = picking01.button_validate() action = picking01.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process() wizard.process()
@@ -179,19 +172,22 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
res = wizard.create_returns() res = wizard.create_returns()
return01 = self.env['stock.picking'].browse(res['res_id']) return01 = self.env['stock.picking'].browse(res['res_id'])
return01.move_ids.quantity_done = 2 return01.move_ids.quantity = 2
return01.move_ids.picked = True
return01.button_validate() return01.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4") self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
picking02 = picking01.backorder_ids picking02 = picking01.backorder_ids
picking02.move_ids.quantity_done = 1 picking02.move_ids.quantity = 1
picking02.move_ids.picked = True
action = picking02.button_validate() action = picking02.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process() wizard.process()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 1/4") self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 1/4")
picking03 = picking02.backorder_ids picking03 = picking02.backorder_ids
picking03.move_ids.quantity_done = 1 picking03.move_ids.quantity = 1
picking03.move_ids.picked = True
picking03.button_validate() picking03.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 2/4") self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 2/4")
@@ -202,7 +198,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
res = wizard.create_returns() res = wizard.create_returns()
picking04 = self.env['stock.picking'].browse(res['res_id']) picking04 = self.env['stock.picking'].browse(res['res_id'])
picking04.move_ids.quantity_done = 1 picking04.move_ids.quantity = 1
picking04.move_ids.picked = True
picking04.button_validate() picking04.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 3/4") self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 3/4")
@@ -213,7 +210,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
res = wizard.create_returns() res = wizard.create_returns()
picking04 = self.env['stock.picking'].browse(res['res_id']) picking04 = self.env['stock.picking'].browse(res['res_id'])
picking04.move_ids.quantity_done = 1 picking04.move_ids.quantity = 1
picking04.move_ids.picked = True
picking04.button_validate() picking04.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 1, "Delivered components: 4/4") self.assertEqual(sale_order.order_line.qty_delivered, 1, "Delivered components: 4/4")
@@ -286,7 +284,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
sale_order.action_confirm() sale_order.action_confirm()
self.env['purchase.order'].search([], order='id desc', limit=1).button_confirm() self.env['purchase.order'].search([], order='id desc', limit=1).button_confirm()
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.quantity = 1
sale_order.picking_ids.move_ids.picked = True
sale_order.picking_ids[0].button_validate() sale_order.picking_ids[0].button_validate()
sale_order.picking_ids[1].button_validate() sale_order.picking_ids[1].button_validate()
@@ -98,7 +98,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
# check that a neg qty can't proprogate once receipt is done # check that a neg qty can't proprogate once receipt is done
for move in receipt.move_ids: for move in receipt.move_ids:
move.move_line_ids.qty_done = move.product_qty move.move_line_ids.quantity = move.product_qty
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
self.assertEqual(receipt.state, 'done') self.assertEqual(receipt.state, 'done')
self.assertEqual(sub_mos.state, 'done') self.assertEqual(sub_mos.state, 'done')
@@ -127,7 +128,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
self.assertTrue(mo) self.assertTrue(mo)
receipt = po.picking_ids receipt = po.picking_ids
receipt.move_ids.quantity_done = 10 receipt.move_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
return_form = Form(self.env['stock.return.picking'].with_context(active_id=receipt.id, active_model='stock.picking')) return_form = Form(self.env['stock.return.picking'].with_context(active_id=receipt.id, active_model='stock.picking'))
@@ -137,7 +139,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
return_id, _ = return_wizard._create_returns() return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id) return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 3 return_picking.move_ids.quantity = 3
return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
self.assertEqual(self.finished2.qty_available, 7.0) self.assertEqual(self.finished2.qty_available, 7.0)
@@ -169,7 +172,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
self.assertTrue(mo) self.assertTrue(mo)
receipt = po.picking_ids receipt = po.picking_ids
receipt.move_ids.quantity_done = 10 receipt.move_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
return_form = Form(self.env['stock.return.picking'].with_context(active_id=receipt.id, active_model='stock.picking')) return_form = Form(self.env['stock.return.picking'].with_context(active_id=receipt.id, active_model='stock.picking'))
@@ -180,7 +184,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
return_id, _ = return_wizard._create_returns() return_id, _ = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_id) return_picking = self.env['stock.picking'].browse(return_id)
return_picking.move_ids.quantity_done = 3 return_picking.move_ids.quantity = 3
return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
self.assertEqual(self.finished2.qty_available, 7.0) self.assertEqual(self.finished2.qty_available, 7.0)
@@ -276,12 +281,14 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
action = po.action_view_subcontracting_resupply() action = po.action_view_subcontracting_resupply()
resupply_picking = self.env[action['res_model']].browse(action['res_id']) resupply_picking = self.env[action['res_model']].browse(action['res_id'])
resupply_picking.move_ids.quantity_done = 1 resupply_picking.move_ids.quantity = 1
resupply_picking.move_ids.picked = True
resupply_picking.button_validate() resupply_picking.button_validate()
action = po.action_view_picking() action = po.action_view_picking()
final_picking = self.env[action['res_model']].browse(action['res_id']) final_picking = self.env[action['res_model']].browse(action['res_id'])
final_picking.move_ids.quantity_done = 1 final_picking.move_ids.quantity = 1
final_picking.move_ids.picked = True
final_picking.button_validate() final_picking.button_validate()
action = po.action_create_invoice() action = po.action_create_invoice()
@@ -308,16 +315,15 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
'order_line': [Command.create({ 'order_line': [Command.create({
'name': self.finished.name, 'name': self.finished.name,
'product_id': self.finished.id, 'product_id': self.finished.id,
'product_uom_qty': 10, 'product_qty': 10,
'product_uom': self.finished.uom_id.id, 'product_uom': self.finished.uom_id.id,
'price_unit': 1, 'price_unit': 1,
})], })],
}) })
purchase.button_confirm() purchase.button_confirm()
# receive product # receive product
receipt = purchase.picking_ids receipt = purchase.picking_ids
receipt.move_ids.quantity_done = 10 receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# create bill # create bill
purchase.action_create_invoice() purchase.action_create_invoice()
+2 -2
View File
@@ -804,7 +804,7 @@ class PosSession(models.Model):
signed_product_qty = move.product_qty signed_product_qty = move.product_qty
if move._is_in(): if move._is_in():
signed_product_qty *= -1 signed_product_qty *= -1
amount = signed_product_qty * move.product_id._compute_average_price(0, move.quantity_done, move) amount = signed_product_qty * move.product_id._compute_average_price(0, move.quantity, move)
stock_expense[exp_key] = self._update_amounts(stock_expense[exp_key], {'amount': amount}, move.picking_id.date, force_company_currency=True) stock_expense[exp_key] = self._update_amounts(stock_expense[exp_key], {'amount': amount}, move.picking_id.date, force_company_currency=True)
if move._is_in(): if move._is_in():
stock_return[out_key] = self._update_amounts(stock_return[out_key], {'amount': amount}, move.picking_id.date, force_company_currency=True) stock_return[out_key] = self._update_amounts(stock_return[out_key], {'amount': amount}, move.picking_id.date, force_company_currency=True)
@@ -833,7 +833,7 @@ class PosSession(models.Model):
signed_product_qty = move.product_qty signed_product_qty = move.product_qty
if move._is_in(): if move._is_in():
signed_product_qty *= -1 signed_product_qty *= -1
amount = signed_product_qty * move.product_id._compute_average_price(0, move.quantity_done, move) amount = signed_product_qty * move.product_id._compute_average_price(0, move.quantity, move)
stock_expense[exp_key] = self._update_amounts(stock_expense[exp_key], {'amount': amount}, move.picking_id.date, force_company_currency=True) stock_expense[exp_key] = self._update_amounts(stock_expense[exp_key], {'amount': amount}, move.picking_id.date, force_company_currency=True)
if move._is_in(): if move._is_in():
stock_return[out_key] = self._update_amounts(stock_return[out_key], {'amount': amount}, move.picking_id.date, force_company_currency=True) stock_return[out_key] = self._update_amounts(stock_return[out_key], {'amount': amount}, move.picking_id.date, force_company_currency=True)
+6 -62
View File
@@ -81,7 +81,6 @@ class StockPicking(models.Model):
'picking_type_id': self.picking_type_id.id, 'picking_type_id': self.picking_type_id.id,
'product_id': first_line.product_id.id, 'product_id': first_line.product_id.id,
'product_uom_qty': abs(sum(order_lines.mapped('qty'))), 'product_uom_qty': abs(sum(order_lines.mapped('qty'))),
'state': 'draft',
'location_id': self.location_id.id, 'location_id': self.location_id.id,
'location_dest_id': self.location_dest_id.id, 'location_dest_id': self.location_dest_id.id,
'company_id': self.company_id.id, 'company_id': self.company_id.id,
@@ -97,6 +96,7 @@ class StockPicking(models.Model):
moves = self.env['stock.move'].create(move_vals) moves = self.env['stock.move'].create(move_vals)
confirmed_moves = moves._action_confirm() confirmed_moves = moves._action_confirm()
confirmed_moves._add_mls_related_to_order(lines, are_qties_done=True) confirmed_moves._add_mls_related_to_order(lines, are_qties_done=True)
confirmed_moves.picked = True
self._link_owner_on_return_picking(lines) self._link_owner_on_return_picking(lines)
def _link_owner_on_return_picking(self, lines): def _link_owner_on_return_picking(self, lines):
@@ -105,12 +105,12 @@ class StockPicking(models.Model):
returned_lines_picking = lines[0].order_id.refunded_order_ids.picking_ids returned_lines_picking = lines[0].order_id.refunded_order_ids.picking_ids
returnable_qty_by_product = {} returnable_qty_by_product = {}
for move_line in returned_lines_picking.move_line_ids: for move_line in returned_lines_picking.move_line_ids:
returnable_qty_by_product[(move_line.product_id.id, move_line.owner_id.id or 0)] = move_line.qty_done returnable_qty_by_product[(move_line.product_id.id, move_line.owner_id.id or 0)] = move_line.quantity
for move in self.move_line_ids: for move in self.move_line_ids:
for keys in returnable_qty_by_product: for keys in returnable_qty_by_product:
if move.product_id.id == keys[0] and keys[1] and returnable_qty_by_product[keys] > 0: if move.product_id.id == keys[0] and keys[1] and returnable_qty_by_product[keys] > 0:
move.write({'owner_id': keys[1]}) move.write({'owner_id': keys[1]})
returnable_qty_by_product[keys] -= move.qty_done returnable_qty_by_product[keys] -= move.quantity
def _send_confirmation_email(self): def _send_confirmation_email(self):
@@ -198,22 +198,6 @@ class StockMove(models.Model):
lines_data[product_id].update({'order_lines': self.env['pos.order.line'].concat(*olines)}) lines_data[product_id].update({'order_lines': self.env['pos.order.line'].concat(*olines)})
return lines_data return lines_data
def _complete_done_qties(self, set_quantity_done_on_move=False):
self._action_assign()
for move_line in self.move_line_ids:
move_line.qty_done = move_line.reserved_uom_qty
mls_vals = []
moves_to_set = set()
for move in self:
if float_compare(move.product_uom_qty, move.quantity_done, precision_rounding=move.product_uom.rounding) > 0:
remaining_qty = move.product_uom_qty - move.quantity_done
mls_vals.append(dict(move._prepare_move_line_vals(), qty_done=remaining_qty))
moves_to_set.add(move.id)
self.env['stock.move.line'].create(mls_vals)
if set_quantity_done_on_move:
for move in self.env['stock.move'].browse(moves_to_set):
move.quantity_done = move.product_uom_qty
def _create_production_lots_for_pos_order(self, lines): def _create_production_lots_for_pos_order(self, lines):
''' Search for existing lots and create missing ones. ''' Search for existing lots and create missing ones.
@@ -251,56 +235,16 @@ class StockMove(models.Model):
def _add_mls_related_to_order(self, related_order_lines, are_qties_done=True): def _add_mls_related_to_order(self, related_order_lines, are_qties_done=True):
lines_data = self._prepare_lines_data_dict(related_order_lines) lines_data = self._prepare_lines_data_dict(related_order_lines)
qty_fname = 'qty_done' if are_qties_done else 'reserved_uom_qty'
# Moves with product_id not in related_order_lines. This can happend e.g. when product_id has a phantom-type bom. # Moves with product_id not in related_order_lines. This can happend e.g. when product_id has a phantom-type bom.
moves_to_assign = self.filtered(lambda m: m.product_id.id not in lines_data or m.product_id.tracking == 'none' moves_to_assign = self.filtered(lambda m: m.product_id.id not in lines_data or m.product_id.tracking == 'none'
or (not m.picking_type_id.use_existing_lots and not m.picking_type_id.use_create_lots)) or (not m.picking_type_id.use_existing_lots and not m.picking_type_id.use_create_lots))
moves_to_assign._complete_done_qties(set_quantity_done_on_move=True) for move in moves_to_assign:
move.quantity = move.product_uom_qty
moves_remaining = self - moves_to_assign moves_remaining = self - moves_to_assign
existing_lots = moves_remaining._create_production_lots_for_pos_order(related_order_lines) existing_lots = moves_remaining._create_production_lots_for_pos_order(related_order_lines)
move_lines_to_create = []
mls_qties = []
if are_qties_done: if are_qties_done:
for move in moves_remaining: for move in moves_remaining:
for line in lines_data[move.product_id.id]['order_lines']: move.quantity = move.product_uom_qty
sum_of_lots = 0
for lot in line.pack_lot_ids.filtered(lambda l: l.lot_name):
if line.product_id.tracking == 'serial':
qty = 1
else:
qty = abs(line.qty)
ml_vals = dict(move._prepare_move_line_vals())
if existing_lots:
existing_lot = existing_lots.filtered_domain([('product_id', '=', line.product_id.id), ('name', '=', lot.lot_name)])
quant = self.env['stock.quant']
if existing_lot:
quant = self.env['stock.quant'].search(
[('lot_id', '=', existing_lot.id), ('quantity', '>', '0.0'), ('location_id', 'child_of', move.location_id.id)],
order='id desc',
limit=1
)
ml_vals.update({
'lot_id': existing_lot.id,
'location_id': quant.location_id.id or move.location_id.id,
'owner_id': quant.owner_id.id or False,
})
else:
ml_vals.update({'lot_name': lot.lot_name})
move_lines_to_create.append(ml_vals)
mls_qties.append(qty)
sum_of_lots += qty
if abs(line.qty) != sum_of_lots:
difference_qty = abs(line.qty) - sum_of_lots
ml_vals = move._prepare_move_line_vals()
if line.product_id.tracking == 'serial':
move_lines_to_create.extend([ml_vals for i in range(int(difference_qty))])
mls_qties.extend([1]*int(difference_qty))
else:
move_lines_to_create.append(ml_vals)
mls_qties.append(difference_qty)
move_lines = self.env['stock.move.line'].create(move_lines_to_create)
for move_line, qty in zip(move_lines, mls_qties):
move_line.write({qty_fname: qty})
else: else:
for move in moves_remaining: for move in moves_remaining:
for line in lines_data[move.product_id.id]['order_lines']: for line in lines_data[move.product_id.id]['order_lines']:
@@ -277,7 +277,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
self.assertEqual(current_session_id.state, 'closed', 'Check that session is closed') self.assertEqual(current_session_id.state, 'closed', 'Check that session is closed')
self.assertEqual(len(current_session.picking_ids), 2, "There should be 2 pickings") self.assertEqual(len(current_session.picking_ids), 2, "There should be 2 pickings")
current_session.picking_ids.move_ids_without_package.quantity_done = 1 current_session.picking_ids.move_ids_without_package.write({'quantity': 1, 'picked': True})
current_session.picking_ids.button_validate() current_session.picking_ids.button_validate()
# I test that the generated journal entries are correct. # I test that the generated journal entries are correct.
@@ -289,7 +289,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo() }).sudo()
move1._action_confirm() move1._action_confirm()
move1._action_assign() move1._action_assign()
move1.move_line_ids.qty_done = 2 move1.move_line_ids.quantity = 2
move1.picked = True
move1._action_done() move1._action_done()
move2 = self.env['stock.move'].create({ move2 = self.env['stock.move'].create({
@@ -303,7 +304,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo() }).sudo()
move2._action_confirm() move2._action_confirm()
move2._action_assign() move2._action_assign()
move2.move_line_ids.qty_done = 1 move2.move_line_ids.quantity = 1
move2.picked = True
move2._action_done() move2._action_done()
# open a session # open a session
@@ -349,7 +351,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo() }).sudo()
move1._action_confirm() move1._action_confirm()
move1._action_assign() move1._action_assign()
move1.move_line_ids.qty_done = 2 move1.move_line_ids.quantity = 2
move1.picked = True
move1._action_done() move1._action_done()
move2 = self.env['stock.move'].create({ move2 = self.env['stock.move'].create({
@@ -363,7 +366,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo() }).sudo()
move2._action_confirm() move2._action_confirm()
move2._action_assign() move2._action_assign()
move2.move_line_ids.qty_done = 1 move2.move_line_ids.quantity = 1
move2.picked = True
move2._action_done() move2._action_done()
# open a session # open a session
+8 -7
View File
@@ -56,7 +56,8 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
}) })
sale_order.action_confirm() sale_order.action_confirm()
picking = sale_order.picking_ids picking = sale_order.picking_ids
picking.move_ids.quantity_done = 300 picking.move_ids.quantity = 300
picking.move_ids.picked = True
action = picking.button_validate() action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])) wizard = Form(self.env[action['res_model']].with_context(action['context']))
wizard.save().process() wizard.save().process()
@@ -75,9 +76,9 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
#assert that sales order qty are correctly updated #assert that sales order qty are correctly updated
self.assertEqual(sale_order.order_line.qty_delivered, 3) self.assertEqual(sale_order.order_line.qty_delivered, 3)
self.assertEqual(sale_order.picking_ids[0].move_ids.product_qty, 2100) # 7 left to deliver => 300 * 7 = 2100 self.assertEqual(sale_order.picking_ids[0].move_ids.product_qty, 2100) # 7 left to deliver => 300 * 7 = 2100
self.assertEqual(sale_order.picking_ids[0].move_ids.quantity_done, 0) self.assertEqual(sale_order.picking_ids[0].move_ids.quantity, 0)
self.assertEqual(sale_order.picking_ids[1].move_ids.product_qty, 300) self.assertEqual(sale_order.picking_ids[1].move_ids.product_qty, 300)
self.assertEqual(sale_order.picking_ids[1].move_ids.quantity_done, 300) # 1 delivered => 300 * 2 = 600 self.assertEqual(sale_order.picking_ids[1].move_ids.quantity, 300) # 1 delivered => 300 * 2 = 600
def test_settle_order_with_incompatible_partner(self): def test_settle_order_with_incompatible_partner(self):
""" If the partner of the sale order is not compatible with the current pos order, """ If the partner of the sale order is not compatible with the current pos order,
@@ -242,9 +243,9 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
}) })
sale_order.action_confirm() sale_order.action_confirm()
self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].reserved_qty, 2) self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].quantity, 2)
self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].location_id.id, self.shelf_1.id) self.assertEqual(sale_order.order_line.move_ids.move_line_ids[0].location_id.id, self.shelf_1.id)
self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].reserved_qty, 2) self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].quantity, 2)
self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].location_id.id, self.shelf_2.id) self.assertEqual(sale_order.order_line.move_ids.move_line_ids[1].location_id.id, self.shelf_2.id)
self.main_pos_config.company_id.write({'point_of_sale_update_stock_quantities': 'real'}) self.main_pos_config.company_id.write({'point_of_sale_update_stock_quantities': 'real'})
@@ -252,9 +253,9 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderRealTime', login="accountman") self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderRealTime', login="accountman")
self.main_pos_config.current_session_id.close_session_from_ui() self.main_pos_config.current_session_id.close_session_from_ui()
pos_order = self.env['pos.order'].search([], order='id desc', limit=1) pos_order = self.env['pos.order'].search([], order='id desc', limit=1)
self.assertEqual(pos_order.picking_ids.move_line_ids[0].qty_done, 2) self.assertEqual(pos_order.picking_ids.move_line_ids[0].quantity, 2)
self.assertEqual(pos_order.picking_ids.move_line_ids[0].location_id.id, self.shelf_1.id) self.assertEqual(pos_order.picking_ids.move_line_ids[0].location_id.id, self.shelf_1.id)
self.assertEqual(pos_order.picking_ids.move_line_ids[1].qty_done, 2) self.assertEqual(pos_order.picking_ids.move_line_ids[1].quantity, 2)
self.assertEqual(pos_order.picking_ids.move_line_ids[1].location_id.id, self.shelf_2.id) self.assertEqual(pos_order.picking_ids.move_line_ids[1].location_id.id, self.shelf_2.id)
self.assertEqual(sale_order.order_line.move_ids.move_lines_count, 0) self.assertEqual(sale_order.order_line.move_ids.move_lines_count, 0)
@@ -43,7 +43,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"]) self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
# Same test but with with the "month/day/year" date format this time. # Same test but with with the "month/day/year" date format this time.
@@ -61,7 +61,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"]) self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
def test_set_multiple_lot_name_with_expiration_date_02_product_dont_use_expiration_date(self): def test_set_multiple_lot_name_with_expiration_date_02_product_dont_use_expiration_date(self):
@@ -90,7 +90,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, False) self.assertEqual(move_line.expiration_date, False)
def test_set_multiple_lot_name_with_expiration_date_03_adaptive_date_format(self): def test_set_multiple_lot_name_with_expiration_date_03_adaptive_date_format(self):
@@ -119,7 +119,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"]) self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
# Now, tries with day first but the year is at the first place in the given dates. # Now, tries with day first but the year is at the first place in the given dates.
@@ -137,7 +137,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"]) self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
def test_set_multiple_lot_name_with_expiration_date_04_written_months(self): def test_set_multiple_lot_name_with_expiration_date_04_written_months(self):
@@ -160,7 +160,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
for i, move_line in enumerate(move.move_line_ids): for i, move_line in enumerate(move.move_line_ids):
self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name']) self.assertEqual(move_line.lot_name, list_lot_and_qty[i]['lot_name'])
self.assertEqual(move_line.qty_done, 1) self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"]) self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
@freeze_time('2023-04-17') @freeze_time('2023-04-17')
@@ -191,35 +191,35 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty)) self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
self.assertEqual(move.move_line_ids[0].lot_name, "ln01") self.assertEqual(move.move_line_ids[0].lot_name, "ln01")
self.assertEqual(move.move_line_ids[0].qty_done, 1) self.assertEqual(move.move_line_ids[0].quantity, 1)
self.assertEqual(move.move_line_ids[0].expiration_date, datetime(day=31, month=12, year=2023)) self.assertEqual(move.move_line_ids[0].expiration_date, datetime(day=31, month=12, year=2023))
self.assertEqual(move.move_line_ids[1].lot_name, "ln02") self.assertEqual(move.move_line_ids[1].lot_name, "ln02")
self.assertEqual(move.move_line_ids[1].qty_done, 1) self.assertEqual(move.move_line_ids[1].quantity, 1)
self.assertEqual(move.move_line_ids[1].expiration_date, datetime(day=17, month=4, year=1989)) self.assertEqual(move.move_line_ids[1].expiration_date, datetime(day=17, month=4, year=1989))
self.assertEqual(move.move_line_ids[2].lot_name, "ln03") self.assertEqual(move.move_line_ids[2].lot_name, "ln03")
self.assertEqual(move.move_line_ids[2].qty_done, 1) self.assertEqual(move.move_line_ids[2].quantity, 1)
self.assertEqual(move.move_line_ids[2].expiration_date, today) self.assertEqual(move.move_line_ids[2].expiration_date, today)
self.assertEqual(move.move_line_ids[3].lot_name, "ln04") self.assertEqual(move.move_line_ids[3].lot_name, "ln04")
self.assertEqual(move.move_line_ids[3].qty_done, 1989) self.assertEqual(move.move_line_ids[3].quantity, 1989)
self.assertEqual(move.move_line_ids[3].expiration_date, today) self.assertEqual(move.move_line_ids[3].expiration_date, today)
self.assertEqual(move.move_line_ids[4].lot_name, "ln05") self.assertEqual(move.move_line_ids[4].lot_name, "ln05")
self.assertEqual(move.move_line_ids[4].qty_done, 1989.04) self.assertEqual(move.move_line_ids[4].quantity, 1989.04)
self.assertEqual(move.move_line_ids[4].expiration_date, today) self.assertEqual(move.move_line_ids[4].expiration_date, today)
self.assertEqual(move.move_line_ids[5].lot_name, "ln06\t1989+04") self.assertEqual(move.move_line_ids[5].lot_name, "ln06\t1989+04")
self.assertEqual(move.move_line_ids[5].qty_done, 1) self.assertEqual(move.move_line_ids[5].quantity, 1)
self.assertEqual(move.move_line_ids[5].expiration_date, today) self.assertEqual(move.move_line_ids[5].expiration_date, today)
self.assertEqual(move.move_line_ids[6].lot_name, "ln07\tdacember") self.assertEqual(move.move_line_ids[6].lot_name, "ln07\tdacember")
self.assertEqual(move.move_line_ids[6].qty_done, 1) self.assertEqual(move.move_line_ids[6].quantity, 1)
self.assertEqual(move.move_line_ids[6].expiration_date, today) self.assertEqual(move.move_line_ids[6].expiration_date, today)
self.assertEqual(move.move_line_ids[7].lot_name, "ln08") self.assertEqual(move.move_line_ids[7].lot_name, "ln08")
self.assertEqual(move.move_line_ids[7].qty_done, 1) self.assertEqual(move.move_line_ids[7].quantity, 1)
self.assertEqual(move.move_line_ids[7].expiration_date, datetime(day=17, month=12, year=2023)) self.assertEqual(move.move_line_ids[7].expiration_date, datetime(day=17, month=12, year=2023))
def test_set_multiple_lot_name_with_expiration_date_06_one_line(self): def test_set_multiple_lot_name_with_expiration_date_06_one_line(self):
@@ -237,5 +237,5 @@ class TestStockLot(StockGenerateCommon):
move = self.get_new_move(product=product_lot) move = self.get_new_move(product=product_lot)
self._import_lots(lot_name, move) self._import_lots(lot_name, move)
self.assertEqual(move.move_line_ids.lot_name, "lot-001") self.assertEqual(move.move_line_ids.lot_name, "lot-001")
self.assertEqual(move.move_line_ids.qty_done, 20) self.assertEqual(move.move_line_ids.quantity, 20)
self.assertEqual(move.move_line_ids.expiration_date, datetime(day=4, month=8, year=2048)) self.assertEqual(move.move_line_ids.expiration_date, datetime(day=4, month=8, year=2048))
+16 -14
View File
@@ -51,7 +51,6 @@ class TestStockLot(TestStockCommon):
'location_id': self.supplier_location, 'location_id': self.supplier_location,
'location_dest_id': self.stock_location, 'location_dest_id': self.stock_location,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
move_a = self.MoveObj.create({ move_a = self.MoveObj.create({
@@ -70,10 +69,11 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments. # Replace pack operation of incoming shipments.
picking_in.action_assign() picking_in.action_assign()
move_a.move_line_ids.qty_done = 33 move_a.move_line_ids.quantity = 33
move_a.move_line_ids.lot_id = self.lot1_productAAA.id move_a.move_line_ids.lot_id = self.lot1_productAAA.id
# Transfer Incoming Shipment. # Transfer Incoming Shipment.
move_a.picked = True
picking_in._action_done() picking_in._action_done()
# run scheduled tasks # run scheduled tasks
@@ -148,7 +148,6 @@ class TestStockLot(TestStockCommon):
'picking_type_id': self.picking_type_in, 'picking_type_id': self.picking_type_in,
'location_id': self.supplier_location, 'location_id': self.supplier_location,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
'location_dest_id': self.stock_location}) 'location_dest_id': self.stock_location})
move_b = self.MoveObj.create({ move_b = self.MoveObj.create({
@@ -166,7 +165,7 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments. # Replace pack operation of incoming shipments.
picking_in.action_assign() picking_in.action_assign()
move_b.move_line_ids.qty_done = 44 move_b.move_line_ids.quantity = 44
move_b.move_line_ids.lot_id = self.lot1_productBBB.id move_b.move_line_ids.lot_id = self.lot1_productBBB.id
# Transfer Incoming Shipment. # Transfer Incoming Shipment.
@@ -197,7 +196,6 @@ class TestStockLot(TestStockCommon):
'picking_type_id': self.picking_type_in, 'picking_type_id': self.picking_type_in,
'location_id': self.supplier_location, 'location_id': self.supplier_location,
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
'location_dest_id': self.stock_location}) 'location_dest_id': self.stock_location})
move_c = self.MoveObj.create({ move_c = self.MoveObj.create({
@@ -215,7 +213,7 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments. # Replace pack operation of incoming shipments.
picking_in.action_assign() picking_in.action_assign()
move_c.move_line_ids.qty_done = 55 move_c.move_line_ids.quantity = 55
move_c.move_line_ids.lot_id = self.lot1_productCCC.id move_c.move_line_ids.lot_id = self.lot1_productCCC.id
# Transfer Incoming Shipment. # Transfer Incoming Shipment.
@@ -313,12 +311,12 @@ class TestStockLot(TestStockCommon):
# Defines a date during the receipt. # Defines a date during the receipt.
move_form = Form(receipt.move_ids_without_package, view="stock.view_stock_move_operations") move_form = Form(receipt.move_ids_without_package, view="stock.view_stock_move_operations")
with move_form.move_line_nosuggest_ids.new() as line: with move_form.move_line_ids.edit(0) as line:
line.lot_name = 'Apple Box #2' line.lot_name = 'Apple Box #2'
line.expiration_date = expiration_date line.expiration_date = expiration_date
line.qty_done = 4
move = move_form.save() move = move_form.save()
move.picked = True
receipt._action_done() receipt._action_done()
# Get back the lot created when the picking was done... # Get back the lot created when the picking was done...
apple_lot = self.env['stock.lot'].search( apple_lot = self.env['stock.lot'].search(
@@ -355,7 +353,8 @@ class TestStockLot(TestStockCommon):
picking_form.picking_type_id = self.env.ref('stock.picking_type_in') picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
with picking_form.move_ids_without_package.new() as move: with picking_form.move_ids_without_package.new() as move:
move.product_id = self.apple_product move.product_id = self.apple_product
move.quantity_done = 4 move.quantity = 4
move.picked = True
receipt = picking_form.save() receipt = picking_form.save()
# Defines a date during the receipt. # Defines a date during the receipt.
@@ -424,8 +423,9 @@ class TestStockLot(TestStockCommon):
'lot_id': good_lot.id, 'lot_id': good_lot.id,
'product_id': self.apple_product.id, 'product_id': self.apple_product.id,
'product_uom_id': self.apple_product.uom_id.id, 'product_uom_id': self.apple_product.uom_id.id,
'qty_done': 4, 'quantity': 4,
})] })]
delivery_1.move_ids.picked = True
res = delivery_1.button_validate() res = delivery_1.button_validate()
# Validate a delivery for good products must not raise anything. # Validate a delivery for good products must not raise anything.
self.assertEqual(res, True) self.assertEqual(res, True)
@@ -449,7 +449,7 @@ class TestStockLot(TestStockCommon):
'lot_id': good_lot.id, 'lot_id': good_lot.id,
'product_id': self.apple_product.id, 'product_id': self.apple_product.id,
'product_uom_id': self.apple_product.uom_id.id, 'product_uom_id': self.apple_product.uom_id.id,
'qty_done': 4, 'quantity': 4,
}), (0, 0, { }), (0, 0, {
'company_id': self.env.company.id, 'company_id': self.env.company.id,
'location_id': delivery_2.move_ids.location_id.id, 'location_id': delivery_2.move_ids.location_id.id,
@@ -457,8 +457,9 @@ class TestStockLot(TestStockCommon):
'lot_id': expired_lot_1.id, 'lot_id': expired_lot_1.id,
'product_id': self.apple_product.id, 'product_id': self.apple_product.id,
'product_uom_id': self.apple_product.uom_id.id, 'product_uom_id': self.apple_product.uom_id.id,
'qty_done': 4, 'quantity': 4,
})] })]
delivery_2.move_ids.picked = True
res = delivery_2.button_validate() res = delivery_2.button_validate()
# Validate a delivery containing expired products must raise a confirmation wizard. # Validate a delivery containing expired products must raise a confirmation wizard.
self.assertNotEqual(res, True) self.assertNotEqual(res, True)
@@ -482,8 +483,9 @@ class TestStockLot(TestStockCommon):
'lot_id': expired_lot_1.id, 'lot_id': expired_lot_1.id,
'product_id': self.apple_product.id, 'product_id': self.apple_product.id,
'product_uom_id': self.apple_product.uom_id.id, 'product_uom_id': self.apple_product.uom_id.id,
'qty_done': 4, 'quantity': 4,
})] })]
delivery_3.move_ids.picked = True
res = delivery_3.button_validate() res = delivery_3.button_validate()
# Validate a delivery containing expired products must raise a confirmation wizard. # Validate a delivery containing expired products must raise a confirmation wizard.
self.assertNotEqual(res, True) self.assertNotEqual(res, True)
@@ -535,7 +537,7 @@ class TestStockLot(TestStockCommon):
'location_id': self.supplier_location, 'location_id': self.supplier_location,
'location_dest_id': self.stock_location, 'location_dest_id': self.stock_location,
'product_id': self.apple_product.id, 'product_id': self.apple_product.id,
'qty_done': 3, 'quantity': 3,
'product_uom_id': self.apple_product.uom_id.id, 'product_uom_id': self.apple_product.uom_id.id,
'lot_id': lot.id, 'lot_id': lot.id,
'company_id': self.env.company.id, 'company_id': self.env.company.id,
@@ -11,17 +11,6 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="view_stock_move_operations_expiry_immediate" model="ir.ui.view">
<field name="name">stock.move.operations.inherit.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_stock_move_operations_immediate"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='product_id']" position="after" >
<field name="picking_code" invisible="1"/>
<field name="use_expiration_date" invisible="1"/>
</xpath>
</field>
</record>
<record id="view_stock_move_line_operation_tree_expiry" model="ir.ui.view"> <record id="view_stock_move_line_operation_tree_expiry" model="ir.ui.view">
<field name="name">stock.move.line.inherit.tree</field> <field name="name">stock.move.line.inherit.tree</field>
+1 -1
View File
@@ -1275,7 +1275,7 @@ class PurchaseOrderLine(models.Model):
line.product_packaging_id = False line.product_packaging_id = False
# suggest biggest suitable packaging # suggest biggest suitable packaging
if line.product_id and line.product_qty and line.product_uom: if line.product_id and line.product_qty and line.product_uom:
line.product_packaging_id = line.product_id.packaging_ids.filtered('purchase')._find_suitable_product_packaging(line.product_qty, line.product_uom) or line.product_packaging_id line.product_packaging_id = line.product_id.packaging_ids.filtered('purchase').with_context(myCompany=line.company_id)._find_suitable_product_packaging(line.product_qty, line.product_uom) or line.product_packaging_id
@api.onchange('product_packaging_id') @api.onchange('product_packaging_id')
def _onchange_product_packaging_id(self): def _onchange_product_packaging_id(self):
@@ -60,9 +60,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
po = po_form.save() po = po_form.save()
po.button_confirm() po.button_confirm()
action = po.picking_ids.button_validate() po.picking_ids.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
action = po.action_create_invoice() action = po.action_create_invoice()
invoice = self.env['account.move'].browse(action['res_id']) invoice = self.env['account.move'].browse(action['res_id'])
@@ -133,7 +131,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
po.button_confirm() po.button_confirm()
receipt = po.picking_ids receipt = po.picking_ids
receipt.move_line_ids.qty_done = 1 receipt.move_line_ids.quantity = 1
receipt.button_validate() receipt.button_validate()
self.assertEqual(receipt.state, 'done') self.assertEqual(receipt.state, 'done')
@@ -156,7 +154,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
})], })],
}) })
delivery.action_confirm() delivery.action_confirm()
delivery.move_ids.move_line_ids.qty_done = 1 delivery.move_ids.move_line_ids.quantity = 1
delivery.button_validate() delivery.button_validate()
self.assertEqual(component01.stock_valuation_layer_ids.mapped('value'), [25, -25]) self.assertEqual(component01.stock_valuation_layer_ids.mapped('value'), [25, -25])
@@ -173,7 +171,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
wizard = wizard_form.save() wizard = wizard_form.save()
action = wizard.create_returns() action = wizard.create_returns()
return_picking = self.env["stock.picking"].browse(action["res_id"]) return_picking = self.env["stock.picking"].browse(action["res_id"])
return_picking.move_ids.move_line_ids.qty_done = 1 return_picking.move_ids.move_line_ids.quantity = 1
return_picking.button_validate() return_picking.button_validate()
self.assertEqual(component01.stock_valuation_layer_ids.mapped('value'), [25, -25, 25]) self.assertEqual(component01.stock_valuation_layer_ids.mapped('value'), [25, -25, 25])
@@ -179,7 +179,8 @@ class TestPurchaseMrpFlow(TransactionCase):
""" """
moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys()) moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys())
for move in moves_to_process: for move in moves_to_process:
move.write({'quantity_done': quantities_to_process[move.product_id]}) move.quantity = quantities_to_process[move.product_id]
move.picked = True
def _assert_quantities(self, moves, quantities_to_process): def _assert_quantities(self, moves, quantities_to_process):
""" Helper to check expected quantities based on a dict following this structure : """ Helper to check expected quantities based on a dict following this structure :
@@ -214,7 +215,7 @@ class TestPurchaseMrpFlow(TransactionCase):
move._action_confirm() move._action_confirm()
move._action_assign() move._action_assign()
move_line = move.move_line_ids[0] move_line = move.move_line_ids[0]
move_line.qty_done = qty_to_process[comp][0] move_line.quantity = qty_to_process[comp][0]
move._action_done() move._action_done()
def test_kit_component_cost(self): def test_kit_component_cost(self):
@@ -232,7 +233,6 @@ class TestPurchaseMrpFlow(TransactionCase):
line.price_unit = 1260 line.price_unit = 1260
po = po.save() po = po.save()
po.button_confirm() po.button_confirm()
po.picking_ids.action_set_quantities_to_reservation()
po.picking_ids.button_validate() po.picking_ids.button_validate()
# Unit price equaly dived among bom lines (cost share not set) # Unit price equaly dived among bom lines (cost share not set)
@@ -285,7 +285,6 @@ class TestPurchaseMrpFlow(TransactionCase):
po = po.save() po = po.save()
po.button_confirm() po.button_confirm()
po.picking_ids.action_set_quantities_to_reservation()
po.picking_ids.button_validate() po.picking_ids.button_validate()
layer = po.picking_ids.move_ids.stock_valuation_layer_ids layer = po.picking_ids.move_ids.stock_valuation_layer_ids
@@ -349,7 +348,7 @@ class TestPurchaseMrpFlow(TransactionCase):
# Process only 7 units of each component # Process only 7 units of each component
qty_to_process = 7 qty_to_process = 7
move_ids.write({'quantity_done': qty_to_process}) move_ids.write({'quantity': qty_to_process, 'picked': True})
# Create a backorder for the missing componenents # Create a backorder for the missing componenents
pick = po.picking_ids[0] pick = po.picking_ids[0]
@@ -456,9 +455,7 @@ class TestPurchaseMrpFlow(TransactionCase):
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
# Process all components and validate the picking # Process all components and validate the picking
wiz_act = return_pick.button_validate() return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# Now quantity received should be 3 again # Now quantity received should be 3 again
self.assertEqual(order_line.qty_received, 3) self.assertEqual(order_line.qty_received, 3)
@@ -475,7 +472,7 @@ class TestPurchaseMrpFlow(TransactionCase):
# Process all components except one of each # Process all components except one of each
for move in return_of_return_pick.move_ids: for move in return_of_return_pick.move_ids:
move.write({ move.write({
'quantity_done': expected_quantities[move.product_id] - 1, 'quantity': expected_quantities[move.product_id] - 1,
'to_refund': True 'to_refund': True
}) })
@@ -283,7 +283,7 @@ class TestPurchaseRequisitionStock(TestPurchaseRequisitionCommon):
self.env['procurement.group'].run_scheduler() self.env['procurement.group'].run_scheduler()
# Find the internal move (Input -> Stock) that has been generated # Find the internal move (Input -> Stock) that has been generated
int_move = self.env['stock.move'].search([('product_id', '=', product.id)]) int_move = self.env['stock.move'].search([('product_id', '=', product.id)])
self.assertEqual(int_move.reserved_availability, 0, "No product should be available, as no products were received at this point.") self.assertEqual(int_move.quantity, 0, "No product should be available, as no products were received at this point.")
# Find the Purchase Order generated by the orderpoint # Find the Purchase Order generated by the orderpoint
orig_po = self.env['purchase.order'].search([('partner_id', '=', vendor_1.id)]) orig_po = self.env['purchase.order'].search([('partner_id', '=', vendor_1.id)])
self.assertEqual(len(orig_po.ids), 1, "Only one PO should have been generated.") self.assertEqual(len(orig_po.ids), 1, "Only one PO should have been generated.")
@@ -308,8 +308,9 @@ class TestPurchaseRequisitionStock(TestPurchaseRequisitionCommon):
# Set all qty as done and validate the alternative PO's picking # Set all qty as done and validate the alternative PO's picking
in_picking = alt_po.picking_ids in_picking = alt_po.picking_ids
self.assertEqual(in_picking.picking_type_id.code, 'incoming', "Must be the reception picking.") self.assertEqual(in_picking.picking_type_id.code, 'incoming', "Must be the reception picking.")
in_picking.move_ids.quantity_done = 10 in_picking.move_ids.quantity = 10
in_picking.move_ids.picked = True
in_picking.button_validate() in_picking.button_validate()
# Quantity should be reserved in the internal transfer's move # Quantity should be reserved in the internal transfer's move
self.assertEqual(int_move.reserved_availability, 10, "Quantity should be reserved in the original internal move.") self.assertEqual(int_move.quantity, 10, "Quantity should be reserved in the original internal move.")
self.assertEqual(int_move.move_orig_ids.id, in_picking.move_ids.id, "Both moves should be correctly chained together.") self.assertEqual(int_move.move_orig_ids.id, in_picking.move_ids.id, "Both moves should be correctly chained together.")
+4 -6
View File
@@ -223,8 +223,6 @@ class PurchaseOrder(models.Model):
'location_dest_id': self._get_destination_location(), 'location_dest_id': self._get_destination_location(),
'location_id': self.partner_id.property_stock_supplier.id, 'location_id': self.partner_id.property_stock_supplier.id,
'company_id': self.company_id.id, 'company_id': self.company_id.id,
'state': 'draft',
'immediate_transfer': False,
} }
def _create_picking(self): def _create_picking(self):
@@ -339,7 +337,7 @@ class PurchaseOrderLine(models.Model):
if move.state == 'done': if move.state == 'done':
if move._is_purchase_return(): if move._is_purchase_return():
if move.to_refund: if move.to_refund:
total -= move.product_uom._compute_quantity(move.quantity_done, line.product_uom, rounding_method='HALF-UP') total -= move.product_uom._compute_quantity(move.quantity, line.product_uom, rounding_method='HALF-UP')
elif move.origin_returned_move_id and move.origin_returned_move_id._is_dropshipped() and not move._is_dropshipped_returned(): elif move.origin_returned_move_id and move.origin_returned_move_id._is_dropshipped() and not move._is_dropshipped_returned():
# Edge case: the dropship is returned to the stock, no to the supplier. # Edge case: the dropship is returned to the stock, no to the supplier.
# In this case, the received quantity on the PO is set although we didn't # In this case, the received quantity on the PO is set although we didn't
@@ -349,7 +347,7 @@ class PurchaseOrderLine(models.Model):
elif move.origin_returned_move_id and move.origin_returned_move_id._is_purchase_return() and not move.to_refund: elif move.origin_returned_move_id and move.origin_returned_move_id._is_purchase_return() and not move.to_refund:
pass pass
else: else:
total += move.product_uom._compute_quantity(move.quantity_done, line.product_uom, rounding_method='HALF-UP') total += move.product_uom._compute_quantity(move.quantity, line.product_uom, rounding_method='HALF-UP')
line._track_qty_received(total) line._track_qty_received(total)
line.qty_received = total line.qty_received = total
@@ -523,10 +521,10 @@ class PurchaseOrderLine(models.Model):
qty = 0.0 qty = 0.0
outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves() outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves()
for move in outgoing_moves: for move in outgoing_moves:
qty_to_compute = move.quantity_done if move.state == 'done' else move.product_uom_qty qty_to_compute = move.quantity if move.state == 'done' else move.product_uom_qty
qty -= move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP') qty -= move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP')
for move in incoming_moves: for move in incoming_moves:
qty_to_compute = move.quantity_done if move.state == 'done' else move.product_uom_qty qty_to_compute = move.quantity if move.state == 'done' else move.product_uom_qty
qty += move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP') qty += move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP')
return qty return qty
+4 -4
View File
@@ -26,7 +26,7 @@ class ResPartner(models.Model):
('order_id.state', 'in', ['done', 'purchase']), ('order_id.state', 'in', ['done', 'purchase']),
('product_id', 'in', self.env['product.product'].sudo()._search([('type', '!=', 'service')])) ('product_id', 'in', self.env['product.product'].sudo()._search([('type', '!=', 'service')]))
]) ])
lines_qty_done = defaultdict(lambda: 0) lines_quantity = defaultdict(lambda: 0)
moves = self.env['stock.move'].search([ moves = self.env['stock.move'].search([
('purchase_line_id', 'in', order_lines.ids), ('purchase_line_id', 'in', order_lines.ids),
('state', '=', 'done')]) ('state', '=', 'done')])
@@ -34,13 +34,13 @@ class ResPartner(models.Model):
order_lines.read(['date_planned', 'partner_id', 'product_uom_qty'], load='') order_lines.read(['date_planned', 'partner_id', 'product_uom_qty'], load='')
moves.read(['purchase_line_id', 'date'], load='') moves.read(['purchase_line_id', 'date'], load='')
moves = moves.filtered(lambda m: m.date.date() <= m.purchase_line_id.date_planned.date()) moves = moves.filtered(lambda m: m.date.date() <= m.purchase_line_id.date_planned.date())
for move, qty_done in zip(moves, moves.mapped('quantity_done')): for move, quantity in zip(moves, moves.mapped('quantity')):
lines_qty_done[move.purchase_line_id.id] += qty_done lines_quantity[move.purchase_line_id.id] += quantity
partner_dict = {} partner_dict = {}
for line in order_lines: for line in order_lines:
on_time, ordered = partner_dict.get(line.partner_id, (0, 0)) on_time, ordered = partner_dict.get(line.partner_id, (0, 0))
ordered += line.product_uom_qty ordered += line.product_uom_qty
on_time += lines_qty_done[line.id] on_time += lines_quantity[line.id]
partner_dict[line.partner_id] = (on_time, ordered) partner_dict[line.partner_id] = (on_time, ordered)
seen_partner = self.env['res.partner'] seen_partner = self.env['res.partner']
for partner, numbers in partner_dict.items(): for partner, numbers in partner_dict.items():
+1 -1
View File
@@ -46,7 +46,7 @@ class StockMove(models.Model):
order = line.order_id order = line.order_id
received_qty = line.qty_received received_qty = line.qty_received
if self.state == 'done': if self.state == 'done':
received_qty -= self.product_uom._compute_quantity(self.quantity_done, line.product_uom, rounding_method='HALF-UP') received_qty -= self.product_uom._compute_quantity(self.quantity, line.product_uom, rounding_method='HALF-UP')
if float_compare(line.qty_invoiced, received_qty, precision_rounding=line.product_uom.rounding) > 0: if float_compare(line.qty_invoiced, received_qty, precision_rounding=line.product_uom.rounding) > 0:
move_layer = line.move_ids.sudo().stock_valuation_layer_ids move_layer = line.move_ids.sudo().stock_valuation_layer_ids
invoiced_layer = line.sudo().invoice_lines.stock_valuation_layer_ids invoiced_layer = line.sudo().invoice_lines.stock_valuation_layer_ids
@@ -30,7 +30,7 @@ SELECT m.id AS id,
Min(po.partner_id) AS partner_id, Min(po.partner_id) AS partner_id,
Min(m.product_qty) AS qty_total, Min(m.product_qty) AS qty_total,
Sum(CASE Sum(CASE
WHEN (m.state = 'done' and pol.date_planned::date >= m.date::date) THEN (ml.qty_done / ml_uom.factor * pt_uom.factor) WHEN (m.state = 'done' and pol.date_planned::date >= m.date::date) THEN (ml.quantity / ml_uom.factor * pt_uom.factor)
ELSE 0 ELSE 0
END) AS qty_on_time END) AS qty_on_time
FROM stock_move m FROM stock_move m
@@ -100,7 +100,8 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon):
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign() return_pick.action_assign()
return_pick.move_ids.quantity_done = 1 return_pick.move_ids.quantity = 1
return_pick.move_ids.picked = True
return_pick._action_done() return_pick._action_done()
# Refund the invoice # Refund the invoice
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
from odoo.tests import tagged, Form from odoo.tests import tagged
import time import time
@@ -52,8 +52,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1 # Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0] picking = purchase_order_1.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check the average_price of the product (average icecream). # Check the average_price of the product (average icecream).
self.assertEqual(product_cable_management_box.qty_available, 10.0, 'Wrong quantity in stock after first reception') self.assertEqual(product_cable_management_box.qty_available, 10.0, 'Wrong quantity in stock after first reception')
@@ -76,8 +75,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
purchase_order_2.button_confirm() purchase_order_2.button_confirm()
# Process the reception of purchase order 2 # Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0] picking = purchase_order_2.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
# Check the standard price # Check the standard price
self.assertEqual(product_cable_management_box.standard_price, 75.0, 'After second reception, we should have an average price of 75.0 on the product') self.assertEqual(product_cable_management_box.standard_price, 75.0, 'After second reception, we should have an average price of 75.0 on the product')
@@ -97,10 +95,8 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
}) })
# Assign this outgoing shipment and process the delivery # Assign this outgoing shipment and process the delivery
outgoing_shipment.action_reset_draft()
outgoing_shipment.action_assign() outgoing_shipment.action_assign()
res = outgoing_shipment.button_validate() outgoing_shipment.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
# Check the average price (60 * 10 + 30 * 80) / 40 = 75.0€ did not change # Check the average price (60 * 10 + 30 * 80) / 40 = 75.0€ did not change
self.assertEqual(product_cable_management_box.standard_price, 75.0, 'Average price should not have changed with outgoing picking!') self.assertEqual(product_cable_management_box.standard_price, 75.0, 'Average price should not have changed with outgoing picking!')
@@ -124,8 +120,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 3 in grams # Process the reception of purchase order 3 in grams
picking = purchase_order_3.picking_ids[0] picking = purchase_order_3.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check price is (75.0 * 20 + 200*0.5) / 20.5 = 78.04878€ # Check price is (75.0 * 20 + 200*0.5) / 20.5 = 78.04878€
self.assertEqual(product_cable_management_box.qty_available, 20.5, 'Reception of purchase order in grams leads to wrong quantity in stock') self.assertEqual(product_cable_management_box.qty_available, 20.5, 'Reception of purchase order in grams leads to wrong quantity in stock')
@@ -163,7 +158,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
self.assertEqual(purchase_order.order_line[0].qty_invoiced, 1.0, 'QTY invoiced should have been set to 1 on the purchase order line') self.assertEqual(purchase_order.order_line[0].qty_invoiced, 1.0, 'QTY invoiced should have been set to 1 on the purchase order line')
picking = purchase_order.picking_ids[0] picking = purchase_order.picking_ids[0]
picking.action_set_quantities_to_reservation() picking.move_ids.picked = True
# clear cash to ensure access rights verification # clear cash to ensure access rights verification
self.env.invalidate_all() self.env.invalidate_all()
picking.with_user(self.res_users_stock_user).button_validate() picking.with_user(self.res_users_stock_user).button_validate()
@@ -55,8 +55,7 @@ class TestCreatePicking(common.TestProductCommon):
# Validate first shipment # Validate first shipment
self.picking = self.po.picking_ids[0] self.picking = self.po.picking_ids[0]
for ml in self.picking.move_line_ids: self.picking.move_ids.picked = True
ml.qty_done = ml.reserved_uom_qty
self.picking._action_done() self.picking._action_done()
self.assertEqual(self.po.order_line.mapped('qty_received'), [7.0], 'Purchase: all products should be received') self.assertEqual(self.po.order_line.mapped('qty_received'), [7.0], 'Purchase: all products should be received')
@@ -169,7 +168,8 @@ class TestCreatePicking(common.TestProductCommon):
# Process pickings # Process pickings
picking.action_confirm() picking.action_confirm()
picking.move_ids.quantity_done = 100.0 picking.move_ids.quantity = 100.0
picking.move_ids.picked = True
picking.button_validate() picking.button_validate()
# mts move will be automatically assigned # mts move will be automatically assigned
@@ -219,8 +219,11 @@ class TestCreatePicking(common.TestProductCommon):
self.assertEqual(move2.product_qty, 12) self.assertEqual(move2.product_qty, 12)
# deliver everything # deliver everything
move1.quantity_done = 24 move1.quantity = 24
move2.quantity_done = 1 move1.picked = True
move2.quantity = 1
move2.picked = True
po.picking_ids.button_validate() po.picking_ids.button_validate()
# check the delivered quantity # check the delivered quantity
@@ -273,7 +276,6 @@ class TestCreatePicking(common.TestProductCommon):
'procure_method': 'make_to_order', 'procure_method': 'make_to_order',
'picking_id': customer_picking.id, 'picking_id': customer_picking.id,
}) })
customer_picking.action_reset_draft()
customer_picking.action_confirm() customer_picking.action_confirm()
purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)]) purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)])
@@ -308,13 +310,15 @@ class TestCreatePicking(common.TestProductCommon):
purchase_order_2.button_confirm() purchase_order_2.button_confirm()
purchase_order.picking_ids.move_ids.quantity_done = 80.0 purchase_order.picking_ids.move_ids.quantity = 80.0
purchase_order.picking_ids.move_ids.picked = True
purchase_order.picking_ids.button_validate() purchase_order.picking_ids.button_validate()
purchase_order_2.picking_ids.move_ids.quantity_done = 20.0 purchase_order_2.picking_ids.move_ids.quantity = 20.0
purchase_order_2.picking_ids.move_ids.picked = True
purchase_order_2.picking_ids.button_validate() purchase_order_2.picking_ids.button_validate()
self.assertEqual(sum(customer_picking.move_ids.mapped('reserved_availability')), 100.0, 'The total quantity for the customer move should be available and reserved.') self.assertEqual(sum(customer_picking.move_ids.mapped('quantity')), 100.0, 'The total quantity for the customer move should be available and reserved.')
def test_04_rounding(self): def test_04_rounding(self):
""" We set the Unit(s) rounding to 1.0 and ensure buying 1.2 units in a PO is rounded to 1.0 """ We set the Unit(s) rounding to 1.0 and ensure buying 1.2 units in a PO is rounded to 1.0
@@ -342,7 +346,8 @@ class TestCreatePicking(common.TestProductCommon):
self.assertEqual(move1.product_qty, 2.0) self.assertEqual(move1.product_qty, 2.0)
# deliver everything # deliver everything
move1.quantity_done = 2.0 move1.quantity = 2.0
move1.picked = True
po.picking_ids.button_validate() po.picking_ids.button_validate()
# check the delivered quantity # check the delivered quantity
@@ -420,7 +425,6 @@ class TestCreatePicking(common.TestProductCommon):
'picking_id': delivery_order.id, 'picking_id': delivery_order.id,
}) })
delivery_order.action_reset_draft()
delivery_order.action_confirm() delivery_order.action_confirm()
# find created po the product # find created po the product
purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)]) purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)])
@@ -471,7 +475,8 @@ class TestCreatePicking(common.TestProductCommon):
po.button_approve() po.button_approve()
po.picking_ids.move_line_ids.write({ po.picking_ids.move_line_ids.write({
'qty_done': 1.0 'quantity': 1.0,
'picked': True
}) })
po.picking_ids.button_validate() po.picking_ids.button_validate()
@@ -486,7 +491,8 @@ class TestCreatePicking(common.TestProductCommon):
po.button_confirm() po.button_confirm()
first_picking = po.picking_ids first_picking = po.picking_ids
first_picking.move_ids.quantity_done = 5 first_picking.move_ids.quantity = 5
first_picking.move_ids.picked = True
# create the backorder # create the backorder
backorder_wizard_dict = first_picking.button_validate() backorder_wizard_dict = first_picking.button_validate()
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save() backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
@@ -507,7 +513,8 @@ class TestCreatePicking(common.TestProductCommon):
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign() return_pick.action_assign()
return_pick.move_ids.quantity_done = 2 return_pick.move_ids.quantity = 2
return_pick.move_ids.picked = True
return_pick._action_done() return_pick._action_done()
self.assertEqual(po.order_line.qty_received, 3) self.assertEqual(po.order_line.qty_received, 3)
@@ -672,12 +679,13 @@ class TestCreatePicking(common.TestProductCommon):
line.product_qty = 10 line.product_qty = 10
po = po_form.save() po = po_form.save()
po.button_approve() po.button_approve()
first_picking = po.picking_ids first_picking = po.picking_ids
first_picking.move_ids.quantity_done = 10 first_picking.move_ids.quantity = 10
first_picking.move_ids.picked = True
first_picking.button_validate() first_picking.button_validate()
second_picking = first_picking.move_ids.move_dest_ids.picking_id second_picking = first_picking.move_ids.move_dest_ids.picking_id
second_picking.move_ids.quantity_done = 10 second_picking.move_ids.quantity = 10
second_picking.move_ids.picked = True
second_picking.button_validate() second_picking.button_validate()
self.assertEqual(po.order_line.qty_received, 10) self.assertEqual(po.order_line.qty_received, 10)
@@ -695,11 +703,13 @@ class TestCreatePicking(common.TestProductCommon):
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign() return_pick.action_assign()
return_pick.move_ids.quantity_done = 2 return_pick.move_ids.quantity = 2
return_pick.move_ids.picked = True
return_pick._action_done() return_pick._action_done()
push_pick = return_pick.move_ids.move_dest_ids.picking_id push_pick = return_pick.move_ids.move_dest_ids.picking_id
push_pick.action_assign() push_pick.action_assign()
push_pick.move_ids.quantity_done = 2 push_pick.move_ids.quantity = 2
push_pick.move_ids.picked = True
push_pick._action_done() push_pick._action_done()
self.assertEqual(po.order_line.qty_received, 8) self.assertEqual(po.order_line.qty_received, 8)
+15 -27
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
from odoo.tests import tagged, Form from odoo.tests import tagged
import time import time
@@ -50,8 +50,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1 and set date # Process the reception of purchase order 1 and set date
picking = purchase_order_1.picking_ids[0] picking = purchase_order_1.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check the standard price of the product (fifo icecream), that should have changed # Check the standard price of the product (fifo icecream), that should have changed
# because the unit cost of the purchase order is 50 # because the unit cost of the purchase order is 50
@@ -75,8 +74,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 2 # Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0] picking = purchase_order_2.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check the standard price of the product, that should have not changed because we # Check the standard price of the product, that should have not changed because we
# still have icecream in stock # still have icecream in stock
@@ -99,12 +97,10 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
}) })
# I assign this outgoing shipment # I assign this outgoing shipment
outgoing_shipment.action_reset_draft()
outgoing_shipment.action_assign() outgoing_shipment.action_assign()
# Process the delivery of the outgoing shipment # Process the delivery of the outgoing shipment
res = outgoing_shipment.button_validate() outgoing_shipment.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check stock value became 1600 . # Check stock value became 1600 .
self.assertEqual(product_cable_management_box.value_svl, 1600.0, 'Stock valuation should be 1600') self.assertEqual(product_cable_management_box.value_svl, 1600.0, 'Stock valuation should be 1600')
@@ -125,12 +121,10 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
}) })
# I assign this outgoing shipment # I assign this outgoing shipment
outgoing_shipment_uom.action_reset_draft()
outgoing_shipment_uom.action_assign() outgoing_shipment_uom.action_assign()
# Process the delivery of the outgoing shipment # Process the delivery of the outgoing shipment
res = outgoing_shipment_uom.button_validate() outgoing_shipment_uom.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check stock valuation and qty in stock # Check stock valuation and qty in stock
self.assertEqual(product_cable_management_box.value_svl, 1560.0, 'Stock valuation should be 1560') self.assertEqual(product_cable_management_box.value_svl, 1560.0, 'Stock valuation should be 1560')
@@ -167,8 +161,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
purchase_order_usd.button_confirm() purchase_order_usd.button_confirm()
# Process the reception of purchase order with USD # Process the reception of purchase order with USD
picking = purchase_order_usd.picking_ids[0] picking = purchase_order_usd.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Create delivery order of 49.5 kg # Create delivery order of 49.5 kg
outgoing_shipment_cur = self.env['stock.picking'].create({ outgoing_shipment_cur = self.env['stock.picking'].create({
@@ -186,12 +179,10 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
}) })
# I assign this outgoing shipment # I assign this outgoing shipment
outgoing_shipment_cur.action_reset_draft()
outgoing_shipment_cur.action_assign() outgoing_shipment_cur.action_assign()
# Process the delivery of the outgoing shipment # Process the delivery of the outgoing shipment
res = outgoing_shipment_cur.button_validate() outgoing_shipment_cur.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Do a delivery of an extra 10 kg # Do a delivery of an extra 10 kg
outgoing_shipment_ret = self.env['stock.picking'].create({ outgoing_shipment_ret = self.env['stock.picking'].create({
@@ -209,10 +200,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
}) })
# I assign this outgoing shipment # I assign this outgoing shipment
outgoing_shipment_ret.action_reset_draft()
outgoing_shipment_ret.action_assign() outgoing_shipment_ret.action_assign()
res = outgoing_shipment_ret.button_validate() outgoing_shipment_ret.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check rounded price is 150.0 / 1.2834 # Check rounded price is 150.0 / 1.2834
self.assertEqual(round(product_cable_management_box.qty_available), 0.0, 'Wrong quantity in stock after first reception.') self.assertEqual(round(product_cable_management_box.qty_available), 0.0, 'Wrong quantity in stock after first reception.')
@@ -248,7 +237,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the delivery of the first outgoing shipment # Process the delivery of the first outgoing shipment
outgoing_shipment_neg.action_confirm() outgoing_shipment_neg.action_confirm()
outgoing_shipment_neg.move_ids[0].quantity_done = 100.0 outgoing_shipment_neg.move_ids[0].quantity = 100.0
outgoing_shipment_neg.move_ids[0].picked = True
outgoing_shipment_neg._action_done() outgoing_shipment_neg._action_done()
# Check qty available = -100 # Check qty available = -100
@@ -274,7 +264,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the delivery of the outgoing shipments # Process the delivery of the outgoing shipments
outgoing_shipment_neg2.action_confirm() outgoing_shipment_neg2.action_confirm()
outgoing_shipment_neg2.move_ids[0].quantity_done = 400.0 outgoing_shipment_neg2.move_ids[0].quantity = 400.0
outgoing_shipment_neg2.move_ids[0].picked = True
outgoing_shipment_neg2._action_done() outgoing_shipment_neg2._action_done()
# Check qty available = -500 # Check qty available = -500
@@ -297,8 +288,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order neg # Process the reception of purchase order neg
picking = purchase_order_neg.picking_ids[0] picking = purchase_order_neg.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Receive purchase order with 600 kg FIFO Ice Cream at 80 euro/kg # Receive purchase order with 600 kg FIFO Ice Cream at 80 euro/kg
purchase_order_neg2 = self.env['purchase.order'].create({ purchase_order_neg2 = self.env['purchase.order'].create({
@@ -317,8 +307,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order neg2 # Process the reception of purchase order neg2
picking = purchase_order_neg2.picking_ids[0] picking = purchase_order_neg2.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
original_out_move = outgoing_shipment_neg.move_ids[0] original_out_move = outgoing_shipment_neg.move_ids[0]
self.assertEqual(original_out_move.product_id.value_svl, 12000.0, 'Value of the move should be 12000') self.assertEqual(original_out_move.product_id.value_svl, 12000.0, 'Value of the move should be 12000')
@@ -362,8 +351,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
self.assertEqual(purchase_order.state, 'purchase') self.assertEqual(purchase_order.state, 'purchase')
picking = purchase_order.picking_ids[0] picking = purchase_order.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
self.assertEqual(super_product.standard_price, 0.035) self.assertEqual(super_product.standard_price, 0.035)
self.assertEqual(super_product.value_svl, 35.0) self.assertEqual(super_product.value_svl, 35.0)
@@ -58,8 +58,7 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1 # Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0] picking = purchase_order_1.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Check the standard price of the product (fifo icecream) # Check the standard price of the product (fifo icecream)
self.assertAlmostEqual(product_fiforet_icecream.standard_price, 50) self.assertAlmostEqual(product_fiforet_icecream.standard_price, 50)
@@ -67,8 +66,7 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Confirm the second purchase order # Confirm the second purchase order
purchase_order_2.button_confirm() purchase_order_2.button_confirm()
picking = purchase_order_2.picking_ids[0] picking = purchase_order_2.picking_ids[0]
res = picking.button_validate() picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
# Return the goods of purchase order 2 # Return the goods of purchase order 2
picking = purchase_order_2.picking_ids[0] picking = purchase_order_2.picking_ids[0]
@@ -81,7 +79,8 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Important to pass through confirmation and assignation # Important to pass through confirmation and assignation
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.action_confirm() return_picking.action_confirm()
return_picking.move_ids[0].quantity_done = return_picking.move_ids[0].product_uom_qty return_picking.move_ids[0].quantity = return_picking.move_ids[0].product_uom_qty
return_picking.move_ids[0].picked = True
return_picking._action_done() return_picking._action_done()
# After the return only 10 of the second purchase order should still be in stock as it applies fifo on the return too # After the return only 10 of the second purchase order should still be in stock as it applies fifo on the return too
@@ -52,7 +52,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'ship_only', 'reception_steps': 'one_step'}) self.warehouse.write({'delivery_steps': 'ship_only', 'reception_steps': 'one_step'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find PO related to picking. # Find PO related to picking.
@@ -82,7 +81,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'ship_only', 'reception_steps': 'one_step'}) self.warehouse.write({'delivery_steps': 'ship_only', 'reception_steps': 'one_step'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find PO related to picking. # Find PO related to picking.
@@ -113,7 +111,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'pick_ship', 'reception_steps': 'two_steps'}) self.warehouse.write({'delivery_steps': 'pick_ship', 'reception_steps': 'two_steps'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find purchase order related to picking. # Find purchase order related to picking.
@@ -148,7 +145,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'pick_ship', 'reception_steps': 'two_steps'}) self.warehouse.write({'delivery_steps': 'pick_ship', 'reception_steps': 'two_steps'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find PO related to picking. # Find PO related to picking.
@@ -187,7 +183,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'pick_pack_ship', 'reception_steps': 'three_steps'}) self.warehouse.write({'delivery_steps': 'pick_pack_ship', 'reception_steps': 'three_steps'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find PO related to picking. # Find PO related to picking.
@@ -224,7 +219,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
self.warehouse.write({'delivery_steps': 'pick_pack_ship', 'reception_steps': 'three_steps'}) self.warehouse.write({'delivery_steps': 'pick_pack_ship', 'reception_steps': 'three_steps'})
self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.picking_out.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id}) self.move.write({'location_id': self.warehouse.out_type_id.default_location_src_id.id})
self.picking_out.action_reset_draft()
self.picking_out.action_confirm() self.picking_out.action_confirm()
# Find PO related to picking. # Find PO related to picking.
@@ -71,7 +71,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
self.assertEqual(self.po.incoming_picking_count, 1, 'Purchase: one picking should be created"') self.assertEqual(self.po.incoming_picking_count, 1, 'Purchase: one picking should be created"')
self.picking = self.po.picking_ids[0] self.picking = self.po.picking_ids[0]
self.picking.move_line_ids.write({'qty_done': 5.0}) self.picking.move_line_ids.write({'quantity': 5.0})
self.picking.move_ids.picked = True
self.picking.button_validate() self.picking.button_validate()
self.assertEqual(self.po.order_line.mapped('qty_received'), [5.0, 5.0], 'Purchase: all products should be received"') self.assertEqual(self.po.order_line.mapped('qty_received'), [5.0, 5.0], 'Purchase: all products should be received"')
@@ -102,7 +103,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
self.assertEqual(self.po.state, 'purchase', 'Purchase: PO state should be "Purchase') self.assertEqual(self.po.state, 'purchase', 'Purchase: PO state should be "Purchase')
self.assertEqual(self.po.incoming_picking_count, 1, 'Purchase: one picking should be created"') self.assertEqual(self.po.incoming_picking_count, 1, 'Purchase: one picking should be created"')
self.picking = self.po.picking_ids[0] self.picking = self.po.picking_ids[0]
self.picking.move_line_ids.write({'qty_done': 5.0}) self.picking.move_line_ids.write({'quantity': 5.0})
self.picking.move_ids.picked = True
self.picking.button_validate() self.picking.button_validate()
self.assertEqual(self.po.order_line.mapped('qty_received'), [5.0, 5.0], 'Purchase: all products should be received"') self.assertEqual(self.po.order_line.mapped('qty_received'), [5.0, 5.0], 'Purchase: all products should be received"')
@@ -131,8 +133,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
# Validate picking # Validate picking
return_pick.move_line_ids.write({'qty_done': 2}) return_pick.move_line_ids.write({'quantity': 2})
return_pick.move_ids.picked = True
return_pick.button_validate() return_pick.button_validate()
# Check Received quantity # Check Received quantity
@@ -188,9 +190,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po1.button_confirm() po1.button_confirm()
picking = po1.picking_ids picking = po1.picking_ids
wiz_act = picking.button_validate() picking.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# Return 5 units # Return 5 units
stock_return_picking_form = Form(self.env['stock.return.picking'].with_context( stock_return_picking_form = Form(self.env['stock.return.picking'].with_context(
@@ -206,9 +206,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
}) })
res = return_wiz.create_returns() res = return_wiz.create_returns()
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
wiz_act = return_pick.button_validate() return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
self.assertEqual(po1.order_line.qty_received, 5) self.assertEqual(po1.order_line.qty_received, 5)
@@ -246,9 +244,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
) )
# receive products # receive products
wiz_act = po.picking_ids.button_validate() po.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# update second line # update second line
old_date = po.order_line[1].date_planned old_date = po.order_line[1].date_planned
@@ -300,12 +296,13 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm() po.button_confirm()
picking = po.picking_ids[0] picking = po.picking_ids[0]
# Process 9.0 out of the 10.0 ordered qty # Process 9.0 out of the 10.0 ordered qty
picking.move_line_ids.write({'qty_done': 9.0}) picking.move_line_ids.write({'quantity': 9.0})
picking.move_ids.picked = True
res_dict = picking.button_validate() res_dict = picking.button_validate()
# No backorder # No backorder
self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder() self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder()
# `on_time_rate` should be equals to the ratio of quantity received against quantity ordered # `on_time_rate` should be equals to the ratio of quantity received against quantity ordered
expected_rate = sum(picking.move_line_ids.mapped("qty_done")) / sum(po.order_line.mapped("product_qty")) * 100 expected_rate = sum(picking.move_line_ids.mapped("quantity")) / sum(po.order_line.mapped("product_qty")) * 100
self.assertEqual(expected_rate, po.on_time_rate) self.assertEqual(expected_rate, po.on_time_rate)
# Create a purchase order with 80% qty received for company B # Create a purchase order with 80% qty received for company B
@@ -319,12 +316,13 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm() po.button_confirm()
picking = po.picking_ids[0] picking = po.picking_ids[0]
# Process 8.0 out of the 10.0 ordered qty # Process 8.0 out of the 10.0 ordered qty
picking.move_line_ids.write({'qty_done': 8.0}) picking.move_line_ids.write({'quantity': 8.0})
picking.move_ids.picked = True
res_dict = picking.button_validate() res_dict = picking.button_validate()
# No backorder # No backorder
self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder() self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder()
# `on_time_rate` should be equal to the ratio of quantity received against quantity ordered # `on_time_rate` should be equal to the ratio of quantity received against quantity ordered
expected_rate = sum(picking.move_line_ids.mapped("qty_done")) / sum(po.order_line.mapped("product_qty")) * 100 expected_rate = sum(picking.move_line_ids.mapped("quantity")) / sum(po.order_line.mapped("product_qty")) * 100
self.assertEqual(expected_rate, po.on_time_rate) self.assertEqual(expected_rate, po.on_time_rate)
# Tricky corner case # Tricky corner case
@@ -372,7 +370,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
}) })
po.button_confirm() po.button_confirm()
picking = po.picking_ids[0] picking = po.picking_ids[0]
picking.move_line_ids.write({'qty_done': 3.66}) picking.move_line_ids.write({'quantity': 3.66})
picking.move_ids.picked = True
picking.button_validate() picking.button_validate()
self.assertEqual(po.order_line.mapped('qty_received'), [4.0], 'Purchase: no conversion error on receipt in different uom"') self.assertEqual(po.order_line.mapped('qty_received'), [4.0], 'Purchase: no conversion error on receipt in different uom"')
@@ -413,7 +412,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
self.assertEqual(purchase_order.state, 'purchase') self.assertEqual(purchase_order.state, 'purchase')
self.assertEqual(len(purchase_order.picking_ids), 1) self.assertEqual(len(purchase_order.picking_ids), 1)
self.assertEqual(len(purchase_order.picking_ids.move_line_ids), 1) self.assertEqual(len(purchase_order.picking_ids.move_line_ids), 1)
self.assertEqual(purchase_order.picking_ids.move_line_ids.reserved_qty, 7) self.assertEqual(purchase_order.picking_ids.move_line_ids.quantity, 7)
purchase_order.order_line.product_qty = 4 purchase_order.order_line.product_qty = 4
@@ -422,7 +421,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
purchase_order.button_confirm() purchase_order.button_confirm()
self.assertEqual(len(purchase_order.picking_ids), 1) self.assertEqual(len(purchase_order.picking_ids), 1)
self.assertEqual(len(purchase_order.picking_ids.move_line_ids), 1) self.assertEqual(len(purchase_order.picking_ids.move_line_ids), 1)
self.assertEqual(purchase_order.picking_ids.move_line_ids.reserved_qty, 4) self.assertEqual(purchase_order.picking_ids.move_line_ids.quantity, 4)
def test_message_qty_already_received(self): def test_message_qty_already_received(self):
self.env.user.write({'company_id': self.company_data['company'].id}) self.env.user.write({'company_id': self.company_data['company'].id})
@@ -441,19 +440,19 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
_purchase_order.button_confirm() _purchase_order.button_confirm()
first_picking = _purchase_order.picking_ids[0] first_picking = _purchase_order.picking_ids[0]
first_picking.move_ids.quantity_done = 5 first_picking.move_ids.quantity = 5
backorder_wizard_dict = first_picking.button_validate() backorder_wizard_dict = first_picking.button_validate()
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save() backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process() backorder_wizard.process()
second_picking = _purchase_order.picking_ids[1] second_picking = _purchase_order.picking_ids[1]
second_picking.move_ids.quantity_done = 5 second_picking.move_ids.quantity = 5
backorder_wizard_dict = second_picking.button_validate() backorder_wizard_dict = second_picking.button_validate()
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save() backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process() backorder_wizard.process()
third_picking = _purchase_order.picking_ids[2] third_picking = _purchase_order.picking_ids[2]
third_picking.move_ids.quantity_done = 5 third_picking.move_ids.quantity = 5
backorder_wizard_dict = third_picking.button_validate() backorder_wizard_dict = third_picking.button_validate()
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save() backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process() backorder_wizard.process()
@@ -604,7 +603,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
picking = po.picking_ids picking = po.picking_ids
self.assertEqual(po.state, "purchase") self.assertEqual(po.state, "purchase")
self.assertEqual(picking.move_line_ids_without_package.location_dest_id.id, sub_loc_01.id) self.assertEqual(picking.move_line_ids_without_package.location_dest_id.id, sub_loc_01.id)
picking.move_line_ids_without_package.write({'qty_done': 1}) picking.move_line_ids_without_package.write({'quantity': 1})
picking.move_ids.write({'picked': True})
res_dict = picking.button_validate() res_dict = picking.button_validate()
self.env[res_dict['res_model']].with_context(res_dict['context']).process() self.env[res_dict['res_model']].with_context(res_dict['context']).process()
backorder = picking.backorder_ids backorder = picking.backorder_ids
@@ -623,7 +623,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
line.product_qty = 5 line.product_qty = 5
po = po_form.save() po = po_form.save()
po.button_confirm() po.button_confirm()
po.picking_ids.move_ids.quantity_done = 5 po.picking_ids.move_ids.quantity = 5
po.picking_ids.move_ids.picked = True
po.picking_ids.button_validate() po.picking_ids.button_validate()
self.assertEqual(po.picking_ids.state, 'done') self.assertEqual(po.picking_ids.state, 'done')
quant = self.env['stock.quant'].search([('product_id', '=', product.id), ('location_id.usage', '=', 'internal')]) quant = self.env['stock.quant'].search([('product_id', '=', product.id), ('location_id.usage', '=', 'internal')])
@@ -632,34 +633,14 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
self.assertEqual(quant.quantity, 5) self.assertEqual(quant.quantity, 5)
def test_po_edit_after_receive(self): def test_po_edit_after_receive(self):
po_1 = self.env['purchase.order'].create(self.po_vals) self.po = self.env['purchase.order'].create(self.po_vals)
po_1.button_confirm() self.po.button_confirm()
po_1.picking_ids.move_ids.quantity_done = 5 self.po.picking_ids.move_ids.quantity = 5
po_1.picking_ids.button_validate() self.po.picking_ids.move_ids.picked = True
self.assertEqual(po_1.picking_ids.move_ids.mapped('product_uom_qty'), [5.0, 5.0]) self.po.picking_ids.button_validate()
po_1.with_context(import_file=True).order_line[0].product_qty = 10 self.assertEqual(self.po.picking_ids.move_ids.mapped('product_uom_qty'), [5.0, 5.0])
self.assertEqual(po_1.picking_ids.move_ids.mapped('product_uom_qty'), [5.0, 5.0, 5.0]) self.po.with_context(import_file=True).order_line[0].product_qty = 10
self.assertEqual(self.po.picking_ids.move_ids.mapped('product_uom_qty'), [5.0, 5.0, 5.0])
### Without backorder ###
po_2 = self.env['purchase.order'].create(self.po_vals)
po_2.button_confirm()
self.assertEqual(len(po_2.picking_ids), 1)
receipt_picking = po_2.picking_ids
receipt_picking.move_ids.quantity_done = 2
backorder_wizard_dict = receipt_picking.button_validate()
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process_cancel_backorder()
self.assertEqual(po_2.order_line.mapped('product_uom_qty'), [5.0, 5.0])
self.assertEqual(po_2.order_line.mapped('qty_received'), [2.0, 2.0])
po_2.write({
'order_line': [
(1, po_2.order_line[0].id, {'product_qty': 2}),
(1, po_2.order_line[1].id, {'product_qty': 2})
]
})
self.assertEqual(len(po_2.picking_ids), 1)
def test_receive_returned_product_without_po_update(self): def test_receive_returned_product_without_po_update(self):
""" """
@@ -670,7 +651,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm() po.button_confirm()
receipt01 = po.picking_ids receipt01 = po.picking_ids
receipt01.move_ids.quantity_done = 5 receipt01.move_ids.quantity = 5
receipt01.button_validate() receipt01.button_validate()
wizard = Form(self.env['stock.return.picking'].with_context(active_ids=receipt01.ids, active_id=receipt01.id, active_model='stock.picking')).save() wizard = Form(self.env['stock.return.picking'].with_context(active_ids=receipt01.ids, active_id=receipt01.id, active_model='stock.picking')).save()
@@ -678,7 +659,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
res = wizard.create_returns() res = wizard.create_returns()
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
return_pick.move_ids.quantity_done = 5 return_pick.move_ids.quantity = 5
return_pick.button_validate() return_pick.button_validate()
wizard = Form(self.env['stock.return.picking'].with_context(active_ids=return_pick.ids, active_id=return_pick.id, active_model='stock.picking')).save() wizard = Form(self.env['stock.return.picking'].with_context(active_ids=return_pick.ids, active_id=return_pick.id, active_model='stock.picking')).save()
@@ -686,7 +667,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
res = wizard.create_returns() res = wizard.create_returns()
receipt02 = self.env['stock.picking'].browse(res['res_id']) receipt02 = self.env['stock.picking'].browse(res['res_id'])
receipt02.move_ids.quantity_done = 5 receipt02.move_ids.quantity = 5
receipt02.button_validate() receipt02.button_validate()
self.assertEqual(po.order_line[0].qty_received, 5) self.assertEqual(po.order_line[0].qty_received, 5)
@@ -41,7 +41,10 @@ class TestAccruedPurchaseStock(AccountTestInvoicingCommon):
def test_purchase_stock_accruals(self): def test_purchase_stock_accruals(self):
# receive 2 on 2020-01-02 # receive 2 on 2020-01-02
pick = self.purchase_order.picking_ids pick = self.purchase_order.picking_ids
pick.move_ids.write({'quantity_done': 2}) pick.move_ids.write({
'quantity': 2,
'picked': True,
})
pick.button_validate() pick.button_validate()
wiz_act = pick.button_validate() wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
@@ -50,7 +53,7 @@ class TestAccruedPurchaseStock(AccountTestInvoicingCommon):
# receive 3 on 2020-01-06 # receive 3 on 2020-01-06
pick = pick.copy() pick = pick.copy()
pick.move_ids.write({'quantity_done': 3}) pick.move_ids.write({'quantity': 3, 'picked': True})
wiz_act = pick.button_validate() wiz_act = pick.button_validate()
pick.move_ids.write({'date': fields.Date.to_date('2020-01-06')}) pick.move_ids.write({'date': fields.Date.to_date('2020-01-06')})
@@ -90,7 +93,7 @@ class TestAccruedPurchaseStock(AccountTestInvoicingCommon):
def test_purchase_stock_invoiced_accrued_entries(self): def test_purchase_stock_invoiced_accrued_entries(self):
# deliver 2 on 2020-01-02 # deliver 2 on 2020-01-02
pick = self.purchase_order.picking_ids pick = self.purchase_order.picking_ids
pick.move_ids.write({'quantity_done': 2}) pick.move_ids.write({'quantity': 2, 'picked': True})
pick.button_validate() pick.button_validate()
wiz_act = pick.button_validate() wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
@@ -104,7 +107,7 @@ class TestAccruedPurchaseStock(AccountTestInvoicingCommon):
# deliver 3 on 2020-01-06 # deliver 3 on 2020-01-06
pick = pick.copy() pick = pick.copy()
pick.move_ids.write({'quantity_done': 3}) pick.move_ids.write({'quantity': 3, 'picked': True})
wiz_act = pick.button_validate() wiz_act = pick.button_validate()
pick.move_ids.write({'date': fields.Date.to_date('2020-01-06')}) pick.move_ids.write({'date': fields.Date.to_date('2020-01-06')})
@@ -43,9 +43,7 @@ class TestPurchaseStockReports(TestReportsCommon):
# Receives 5 products. # Receives 5 products.
receipt = po.picking_ids receipt = po.picking_ids
res_dict = receipt.button_validate() receipt.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids) report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
draft_picking_qty_in = docs['draft_picking_qty']['in'] draft_picking_qty_in = docs['draft_picking_qty']['in']
draft_purchase_qty = docs['draft_purchase_qty'] draft_purchase_qty = docs['draft_purchase_qty']
@@ -118,9 +116,7 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt = po.picking_ids receipt = po.picking_ids
# Receives 4 products. # Receives 4 products.
res_dict = receipt.button_validate() receipt.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids) report_values, docs, lines = self.get_report_forecast(product_template_ids=self.product_template.ids)
draft_picking_qty_in = docs['draft_picking_qty']['in'] draft_picking_qty_in = docs['draft_picking_qty']['in']
draft_purchase_qty = docs['draft_purchase_qty'] draft_purchase_qty = docs['draft_purchase_qty']
@@ -227,9 +223,10 @@ class TestPurchaseStockReports(TestReportsCommon):
'location_dest_id': receipt_move.location_dest_id.id, 'location_dest_id': receipt_move.location_dest_id.id,
'product_id': self.product.id, 'product_id': self.product.id,
'product_uom_id': uom_12.id, 'product_uom_id': uom_12.id,
'qty_done': 1, 'quantity': 1,
'picking_id': receipt.id, 'picking_id': receipt.id,
})] })]
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
data = self.env['vendor.delay.report'].read_group( data = self.env['vendor.delay.report'].read_group(
@@ -267,16 +264,17 @@ class TestPurchaseStockReports(TestReportsCommon):
'location_dest_id': child_loc_01.id, 'location_dest_id': child_loc_01.id,
'product_id': self.product.id, 'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id, 'product_uom_id': self.product.uom_id.id,
'qty_done': 6, 'quantity': 6,
'picking_id': receipt.id, 'picking_id': receipt.id,
}), (0, 0, { }), (0, 0, {
'location_id': receipt_move.location_id.id, 'location_id': receipt_move.location_id.id,
'location_dest_id': child_loc_02.id, 'location_dest_id': child_loc_02.id,
'product_id': self.product.id, 'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id, 'product_uom_id': self.product.uom_id.id,
'qty_done': 4, 'quantity': 4,
'picking_id': receipt.id, 'picking_id': receipt.id,
})] })]
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
data = self.env['vendor.delay.report'].read_group( data = self.env['vendor.delay.report'].read_group(
@@ -306,7 +304,7 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt01 = po.picking_ids receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6 receipt01_move.quantity = 6
action = receipt01.button_validate() action = receipt01.button_validate()
Form(self.env[action['res_model']].with_context(action['context'])).save().process() Form(self.env[action['res_model']].with_context(action['context'])).save().process()
@@ -320,7 +318,8 @@ class TestPurchaseStockReports(TestReportsCommon):
self.assertEqual(data['on_time_rate'], 60) self.assertEqual(data['on_time_rate'], 60)
receipt02 = receipt01.backorder_ids receipt02 = receipt01.backorder_ids
receipt02.move_ids.quantity_done = 4 receipt02.move_ids.quantity = 4
receipt02.move_ids.picked = True
receipt02.button_validate() receipt02.button_validate()
(receipt01 | receipt02).move_ids.invalidate_recordset() (receipt01 | receipt02).move_ids.invalidate_recordset()
@@ -349,7 +348,8 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt01 = po.picking_ids receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6 receipt01_move.quantity = 6
receipt01_move.picked = True
action = receipt01.button_validate() action = receipt01.button_validate()
Form(self.env[action['res_model']].with_context(action['context'])).save().process_cancel_backorder() Form(self.env[action['res_model']].with_context(action['context'])).save().process_cancel_backorder()
@@ -68,6 +68,7 @@ class TestReorderingRule(TransactionCase):
move.product_id = self.product_01 move.product_id = self.product_01
move.product_uom_qty = 10.0 move.product_uom_qty = 10.0
customer_picking = picking_form.save() customer_picking = picking_form.save()
customer_picking.action_confirm()
# Run scheduler # Run scheduler
self.env['procurement.group'].run_scheduler() self.env['procurement.group'].run_scheduler()
@@ -150,6 +151,7 @@ class TestReorderingRule(TransactionCase):
customer_picking.move_ids[1].location_id = subloc_2.id customer_picking.move_ids[1].location_id = subloc_2.id
# picking confirm # picking confirm
customer_picking.action_confirm()
self.assertEqual(self.product_01.with_context(location=subloc_1.id).virtual_available, -10) self.assertEqual(self.product_01.with_context(location=subloc_1.id).virtual_available, -10)
self.assertEqual(self.product_01.with_context(location=subloc_2.id).virtual_available, -10) self.assertEqual(self.product_01.with_context(location=subloc_2.id).virtual_available, -10)
@@ -360,7 +362,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0 move.product_uom_qty = 10.0
customer_picking = picking_form.save() customer_picking = picking_form.save()
customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order' customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order'
customer_picking.action_reset_draft()
customer_picking.action_confirm() customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint']._get_orderpoint_action() self.env['stock.warehouse.orderpoint']._get_orderpoint_action()
self.env['stock.warehouse.orderpoint']._get_orderpoint_action() self.env['stock.warehouse.orderpoint']._get_orderpoint_action()
@@ -404,7 +405,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0 move.product_uom_qty = 10.0
customer_picking = picking_form.save() customer_picking = picking_form.save()
customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order' customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order'
customer_picking.action_reset_draft()
customer_picking.action_confirm() customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint'].flush_model() self.env['stock.warehouse.orderpoint'].flush_model()
@@ -460,7 +460,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0 move.product_uom_qty = 10.0
customer_picking = picking_form.save() customer_picking = picking_form.save()
customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order' customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order'
customer_picking.action_reset_draft()
customer_picking.action_confirm() customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint']._get_orderpoint_action() self.env['stock.warehouse.orderpoint']._get_orderpoint_action()
orderpoint_product = self.env['stock.warehouse.orderpoint'].search( orderpoint_product = self.env['stock.warehouse.orderpoint'].search(
@@ -503,7 +502,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0 move.product_uom_qty = 10.0
customer_picking = picking_form.save() customer_picking = picking_form.save()
customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order' customer_picking.move_ids.filtered(lambda m: m.product_id == product_buy_mto).procure_method = 'make_to_order'
customer_picking.action_reset_draft()
customer_picking.action_confirm() customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint'].flush_model() self.env['stock.warehouse.orderpoint'].flush_model()
@@ -722,13 +720,11 @@ class TestReorderingRule(TransactionCase):
po.button_confirm() po.button_confirm()
picking = po.picking_ids picking = po.picking_ids
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[(action.get('res_model'))].with_context(action['context'])).save()
wizard.process()
self.assertRecordValues(picking.move_line_ids, [ self.assertRecordValues(picking.move_line_ids, [
{'product_id': self.product_01.id, 'qty_done': 1.0, 'state': 'done', 'location_dest_id': stock_location.id}, {'product_id': self.product_01.id, 'quantity': 1.0, 'state': 'done', 'location_dest_id': stock_location.id},
{'product_id': self.product_01.id, 'qty_done': 2.0, 'state': 'done', 'location_dest_id': sub_location.id}, {'product_id': self.product_01.id, 'quantity': 2.0, 'state': 'done', 'location_dest_id': sub_location.id},
]) ])
def test_2steps_and_partner_on_orderpoint(self): def test_2steps_and_partner_on_orderpoint(self):
@@ -1020,9 +1016,7 @@ class TestReorderingRule(TransactionCase):
'location_dest_id': self.env.ref('stock.stock_location_customers').id, 'location_dest_id': self.env.ref('stock.stock_location_customers').id,
})], })],
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
picking.with_user(user).action_reset_draft()
picking.with_user(user).action_assign() picking.with_user(user).action_assign()
# check that the PO line quantity has been updated # check that the PO line quantity has been updated
self.assertEqual(po_line.product_qty, 6, 'The PO line quantity should be 6') self.assertEqual(po_line.product_qty, 6, 'The PO line quantity should be 6')
@@ -127,7 +127,8 @@ class TestStockValuation(TransactionCase):
last_po_id.button_confirm() last_po_id.button_confirm()
picking = last_po_id.picking_ids[0] picking = last_po_id.picking_ids[0]
move = picking.move_ids[0] move = picking.move_ids[0]
move.quantity_done = move.product_uom_qty move.quantity = move.product_uom_qty
move.picked = True
picking.button_validate() picking.button_validate()
self.assertEqual(move.stock_valuation_layer_ids.unit_cost, self.assertEqual(move.stock_valuation_layer_ids.unit_cost,
@@ -166,9 +167,7 @@ class TestStockValuation(TransactionCase):
# validate the receipt # validate the receipt
res_dict = picking1.button_validate() picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
# the unit price of the valuationlayer used the latest value # the unit price of the valuationlayer used the latest value
self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 200) self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 200)
@@ -214,9 +213,7 @@ class TestStockValuation(TransactionCase):
self.assertEqual(move1.price_unit, 11) self.assertEqual(move1.price_unit, 11)
# validate the receipt # validate the receipt
res_dict = picking1.button_validate() picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
# the unit price of the valuation layer used the latest value # the unit price of the valuation layer used the latest value
self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 12) self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 12)
@@ -245,14 +242,16 @@ class TestStockValuation(TransactionCase):
picking1 = po1.picking_ids[0] picking1 = po1.picking_ids[0]
move1 = picking1.move_ids[0] move1 = picking1.move_ids[0]
move1.quantity_done = 15 move1.quantity = 15
move1.picked = True
picking1.button_validate() picking1.button_validate()
# there should be only one move # there should be only one move
self.assertEqual(len(picking1.move_ids), 1) self.assertEqual(len(picking1.move_ids), 1)
self.assertEqual(move1.price_unit, 100) self.assertEqual(move1.price_unit, 100)
self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 100) self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 100)
self.assertEqual(move1.product_qty, 15) self.assertEqual(move1.product_qty, 10)
self.assertEqual(move1.quantity, 15)
self.assertEqual(self.product1.value_svl, 1500) self.assertEqual(self.product1.value_svl, 1500)
def test_backorder_fifo_1(self): def test_backorder_fifo_1(self):
@@ -277,7 +276,8 @@ class TestStockValuation(TransactionCase):
picking1 = po1.picking_ids[0] picking1 = po1.picking_ids[0]
move1 = picking1.move_ids[0] move1 = picking1.move_ids[0]
move1.quantity_done = 5 move1.quantity = 5
move1.picked = True
res_dict = picking1.button_validate() res_dict = picking1.button_validate()
self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation') self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation')
wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context']) wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context'])
@@ -411,7 +411,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
wizard.product_return_moves.quantity = qty wizard.product_return_moves.quantity = qty
action = wizard.create_returns() action = wizard.create_returns()
return_picking = self.env["stock.picking"].browse(action["res_id"]) return_picking = self.env["stock.picking"].browse(action["res_id"])
return_picking.move_ids.move_line_ids.qty_done = qty return_picking.move_ids.move_line_ids.quantity = qty
return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
return return_picking return return_picking
@@ -476,9 +477,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
self.assertAlmostEqual(move1.price_unit, price_unit_usd, places=2) self.assertAlmostEqual(move1.price_unit, price_unit_usd, places=2)
# validate the receipt # validate the receipt
res_dict = picking1.button_validate() picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
# the unit price of the valuation layer used the latest value # the unit price of the valuation layer used the latest value
self.assertAlmostEqual(move1.stock_valuation_layer_ids.unit_cost, price_unit_usd_new_rate) self.assertAlmostEqual(move1.stock_valuation_layer_ids.unit_cost, price_unit_usd_new_rate)
@@ -506,7 +505,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
}) })
po1.button_confirm() po1.button_confirm()
receipt_po1 = po1.picking_ids[0] receipt_po1 = po1.picking_ids[0]
receipt_po1.move_ids.quantity_done = 10 receipt_po1.move_ids.quantity = 10
receipt_po1.move_ids.picked = True
receipt_po1.button_validate() receipt_po1.button_validate()
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice')) move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
@@ -532,7 +532,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
}) })
po2.button_confirm() po2.button_confirm()
receipt_po2 = po2.picking_ids[0] receipt_po2 = po2.picking_ids[0]
receipt_po2.move_ids.quantity_done = 10 receipt_po2.move_ids.quantity = 10
receipt_po2.move_ids.picked = True
receipt_po2.button_validate() receipt_po2.button_validate()
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice')) move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
@@ -552,7 +553,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
stock_return_picking.product_return_moves.quantity = 10 stock_return_picking.product_return_moves.quantity = 10
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.move_ids[0].move_line_ids[0].qty_done = 10 return_pick.move_ids[0].move_line_ids[0].quantity = 10
return_pick.move_ids[0].picked = True
return_pick.button_validate() return_pick.button_validate()
# valuation of product1 should be 200 as the first items will be sent out # valuation of product1 should be 200 as the first items will be sent out
@@ -598,7 +600,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = order.picking_ids[0] receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1 receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
stock_valuation_aml = self.env['account.move.line'].search([('account_id', '=', self.stock_valuation_account.id)]) stock_valuation_aml = self.env['account.move.line'].search([('account_id', '=', self.stock_valuation_account.id)])
@@ -665,7 +668,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
}) })
po1.button_confirm() po1.button_confirm()
receipt_po1 = po1.picking_ids[0] receipt_po1 = po1.picking_ids[0]
receipt_po1.move_ids.quantity_done = 10 receipt_po1.move_ids.quantity = 10
receipt_po1.move_ids.picked = True
receipt_po1.button_validate() receipt_po1.button_validate()
# valuation of product1 should be 15 as the tax with no account set # valuation of product1 should be 15 as the tax with no account set
@@ -719,7 +723,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = po.picking_ids[0] receipt = po.picking_ids[0]
receipt.move_line_ids.qty_done = 1 receipt.move_line_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# Create a vendor bill # Create a vendor bill
@@ -818,7 +823,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = po.picking_ids[0] receipt = po.picking_ids[0]
receipt.move_line_ids.qty_done = 1 receipt.move_line_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# Create a vendor bill # Create a vendor bill
@@ -1195,7 +1201,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids picking = po.picking_ids
(picking.move_ids (picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg) .filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 1.0})) .write({'quantity': 1.0, 'picked': True}))
picking.button_validate() picking.button_validate()
# 5 Units received at rate 0.7 = 42.86 # 5 Units received at rate 0.7 = 42.86
@@ -1294,7 +1300,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids picking = po.picking_ids
(picking.move_ids (picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg) .filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 1.0})) .write({'quantity': 1.0, 'picked': True}))
picking.button_validate() picking.button_validate()
# 5 Units received at rate 2 = 42.86 # 5 Units received at rate 2 = 42.86
@@ -1422,7 +1428,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids picking = po.picking_ids
(picking.move_ids (picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg) .filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 5.0})) .write({'quantity': 5.0, 'picked': True}))
picking.button_validate() picking.button_validate()
picking._action_done() # Create Backorder picking._action_done() # Create Backorder
@@ -1456,7 +1462,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
backorder_picking = self.env['stock.picking'].search([('backorder_id', '=', picking.id)]) backorder_picking = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
(backorder_picking.move_ids (backorder_picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg) .filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 5.0})) .write({'quantity': 5.0, 'picked': True}))
backorder_picking.button_validate() backorder_picking.button_validate()
# 5 Units invoiced at rate 2 (10) + 5 Units received at rate 0.8 (37.50) = 23.75 # 5 Units invoiced at rate 2 (10) + 5 Units received at rate 0.8 (37.50) = 23.75
self.assertAlmostEqual(product_avg.standard_price, 23.75) self.assertAlmostEqual(product_avg.standard_price, 23.75)
@@ -1545,7 +1551,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = order.picking_ids[0] receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1 receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# Create an invoice with a different price and a discount # Create an invoice with a different price and a discount
@@ -1594,7 +1601,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = order.picking_ids[0] receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1 receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# Create an invoice with a different price and a discount # Create an invoice with a different price and a discount
@@ -1641,7 +1649,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods # Receive the goods
receipt = order.picking_ids[0] receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1 receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
# Create an invoice with a different price and a discount # Create an invoice with a different price and a discount
@@ -1754,7 +1763,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive 7 Hundred # Receive 7 Hundred
receipt01 = po.picking_ids[0] receipt01 = po.picking_ids[0]
receipt01.move_ids.move_line_ids.qty_done = 700 receipt01.move_ids.move_line_ids.quantity = 700
receipt01.move_ids.picked = True
action = receipt01.button_validate() action = receipt01.button_validate()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save() backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process() backorder_wizard.process()
@@ -1764,7 +1774,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive 3 Hundred # Receive 3 Hundred
receipt02 = receipt01.backorder_ids receipt02 = receipt01.backorder_ids
receipt02.move_ids._set_quantities_to_reservation() receipt02.move_ids.picked = True
receipt02.button_validate() receipt02.button_validate()
expected_svl_values += [3 * 50] expected_svl_values += [3 * 50]
@@ -1784,11 +1794,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id, 'location_dest_id': customer_location.id,
})], })],
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
delivery01.action_reset_draft()
delivery01.action_confirm() delivery01.action_confirm()
delivery01.move_ids._set_quantities_to_reservation() delivery01.move_ids.picked = True
delivery01.button_validate() delivery01.button_validate()
expected_svl_values += [-5 * 50] expected_svl_values += [-5 * 50]
@@ -1858,11 +1866,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id, 'location_dest_id': customer_location.id,
})], })],
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
delivery02.action_reset_draft()
delivery02.action_confirm() delivery02.action_confirm()
delivery02.move_ids._set_quantities_to_reservation() delivery02.move_ids.picked = True
delivery02.button_validate() delivery02.button_validate()
expected_svl_values += [-2 * 50 + -2 * 10] expected_svl_values += [-2 * 50 + -2 * 10]
@@ -1899,7 +1905,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
receipt = po.picking_ids[0] receipt = po.picking_ids[0]
receipt.move_ids._set_quantities_to_reservation() receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
delivery = self.env['stock.picking'].create({ delivery = self.env['stock.picking'].create({
@@ -1914,11 +1920,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id, 'location_dest_id': customer_location.id,
})], })],
'state': 'draft', 'state': 'draft',
'immediate_transfer': False,
}) })
delivery.action_reset_draft()
delivery.action_confirm() delivery.action_confirm()
delivery.move_ids._set_quantities_to_reservation() delivery.move_ids.picked = True
delivery.button_validate() delivery.button_validate()
bill01_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice')) bill01_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
@@ -1973,19 +1977,22 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
receipt01 = po.picking_ids receipt01 = po.picking_ids
receipt01.move_ids.move_line_ids.qty_done = 4 receipt01.move_ids.move_line_ids.quantity = 4
receipt01.move_ids.picked = True
action = receipt01.button_validate() action = receipt01.button_validate()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save() backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process() backorder_wizard.process()
receipt02 = receipt01.backorder_ids receipt02 = receipt01.backorder_ids
receipt02.move_ids.move_line_ids.qty_done = 3 receipt02.move_ids.move_line_ids.quantity = 3
receipt02.move_ids.picked = True
action = receipt02.button_validate() action = receipt02.button_validate()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save() backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process() backorder_wizard.process()
receipt03 = receipt02.backorder_ids receipt03 = receipt02.backorder_ids
receipt03.move_ids.move_line_ids.qty_done = 5 receipt03.move_ids.move_line_ids.quantity = 5
receipt03.move_ids.picked = True
receipt03.button_validate() receipt03.button_validate()
expected_svl_values = [40, 30, 50] expected_svl_values = [40, 30, 50]
@@ -2121,7 +2128,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
receipt = po.picking_ids receipt = po.picking_ids
receipt.move_ids.move_line_ids.qty_done = 10 receipt.move_ids.move_line_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
expected_svl_values += [100.0] expected_svl_values += [100.0]
expected_svl_remaining_values += [100.0] expected_svl_remaining_values += [100.0]
@@ -2149,7 +2157,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})], })],
}) })
delivery.action_confirm() delivery.action_confirm()
delivery.move_ids.quantity_done = 3.0 delivery.move_ids.quantity = 3.0
delivery.move_ids.picked = True
delivery.button_validate() delivery.button_validate()
expected_svl_values += [-36.0] expected_svl_values += [-36.0]
expected_svl_remaining_values += [0.0] expected_svl_remaining_values += [0.0]
@@ -2185,7 +2194,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})], })],
}) })
delivery.action_confirm() delivery.action_confirm()
delivery.move_ids.quantity_done = 1.0 delivery.move_ids.quantity = 1.0
delivery.move_ids.picked = True
delivery.button_validate() delivery.button_validate()
expected_svl_values += [-9.0] expected_svl_values += [-9.0]
expected_svl_remaining_values += [0.0] expected_svl_remaining_values += [0.0]
@@ -2274,7 +2284,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
receipt = po.picking_ids receipt = po.picking_ids
receipt.move_ids.move_line_ids.qty_done = 10 receipt.move_ids.move_line_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate() receipt.button_validate()
expected_svl_values += [100.0] expected_svl_values += [100.0]
expected_svl_remaining_values += [100.0] expected_svl_remaining_values += [100.0]
@@ -2367,13 +2378,15 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
receipt01 = po.picking_ids receipt01 = po.picking_ids
receipt01.move_ids.move_line_ids.qty_done = 1 receipt01.move_ids.move_line_ids.quantity = 1
receipt01.move_ids.picked = True
action = receipt01.button_validate() action = receipt01.button_validate()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save() backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process() backorder_wizard.process()
receipt02 = receipt01.backorder_ids receipt02 = receipt01.backorder_ids
receipt02.move_ids.move_line_ids.qty_done = 1 receipt02.move_ids.move_line_ids.quantity = 1
receipt02.move_ids.picked = True
receipt02.button_validate() receipt02.button_validate()
bill01 = self._bill(po, 1.0, 11) bill01 = self._bill(po, 1.0, 11)
@@ -2471,7 +2484,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
picking = po.picking_ids[0] picking = po.picking_ids[0]
move = picking.move_ids[0] move = picking.move_ids[0]
move.quantity_done = 10 move.quantity = 10
move.picked = True
res_dict = picking.button_validate() res_dict = picking.button_validate()
self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation') self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation')
wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context']) wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context'])
@@ -2482,7 +2496,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking2 = po.picking_ids.filtered(lambda p: p.backorder_id) picking2 = po.picking_ids.filtered(lambda p: p.backorder_id)
move2 = picking2.move_ids[0] move2 = picking2.move_ids[0]
move2.quantity_done = 2 move2.quantity = 2
move2.picked = True
picking2.button_validate() picking2.button_validate()
self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD) self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD)
@@ -2515,7 +2530,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm() po.button_confirm()
picking = po.picking_ids[0] picking = po.picking_ids[0]
move = picking.move_ids[0] move = picking.move_ids[0]
move.quantity_done = 10 move.quantity = 10
move.picked = True
res_dict = picking.button_validate() res_dict = picking.button_validate()
self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation') self.assertEqual(res_dict['res_model'], 'stock.backorder.confirmation')
wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context']) wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')).with_context(res_dict['context'])
@@ -2526,6 +2542,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking2 = po.picking_ids.filtered(lambda p: p.backorder_id) picking2 = po.picking_ids.filtered(lambda p: p.backorder_id)
move2 = picking2.move_ids[0] move2 = picking2.move_ids[0]
move2.quantity_done = 2 move2.quantity = 2
move2.picked = True
picking2.button_validate() picking2.button_validate()
self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD) self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD)
@@ -26,7 +26,8 @@ class TestUninstallPurchaseStock(PurchaseTestCommon):
self.assertEqual(order_line.product_id.detailed_type, 'product') self.assertEqual(order_line.product_id.detailed_type, 'product')
stock_move.quantity_done = 1 stock_move.quantity = 1
stock_move.picked = True
stock_move.picking_id.button_validate() stock_move.picking_id.button_validate()
self.assertEqual(purchase_order.order_line.qty_received, 1) self.assertEqual(purchase_order.order_line.qty_received, 1)
+2 -2
View File
@@ -9,9 +9,9 @@ class Product(models.Model):
def _count_returned_sn_products(self, sn_lot): def _count_returned_sn_products(self, sn_lot):
res = self.env['stock.move'].search_count([ res = self.env['stock.move'].search_count([
('repair_line_type', '=', 'remove'), ('repair_line_type', 'in', ['remove', 'recycle']),
('product_uom_qty', '=', 1), ('product_uom_qty', '=', 1),
('move_line_ids.lot_id', 'in', sn_lot.id), ('move_line_ids.lot_id', '=', sn_lot.id),
('state', '=', 'done'), ('state', '=', 'done'),
('location_dest_usage', '=', 'internal'), ('location_dest_usage', '=', 'internal'),
]) ])
+10 -27
View File
@@ -251,22 +251,20 @@ class Repair(models.Model):
returned = self.filtered(lambda r: r.picking_id and r.picking_id.state == 'done') returned = self.filtered(lambda r: r.picking_id and r.picking_id.state == 'done')
returned.is_returned = True returned.is_returned = True
@api.depends('state', @api.depends('state', 'move_ids.quantity', 'move_ids.product_uom_qty')
'move_ids.reserved_availability',
'move_ids.quantity_done')
def _compute_show_qty_button(self): def _compute_show_qty_button(self):
self.show_set_qty_button = False self.show_set_qty_button = False
self.show_clear_qty_button = False self.show_clear_qty_button = False
for repair in self.filtered(lambda r: r.state not in ['cancel', 'done']): for repair in self.filtered(lambda r: r.state not in ['cancel', 'done']):
if any(float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding) and not float_is_zero(m.reserved_availability, precision_rounding=m.product_uom.rounding) for m in repair.move_ids): if any(float_is_zero(m.quantity, precision_rounding=m.product_uom.rounding) and not float_is_zero(m.product_uom_qty, precision_rounding=m.product_uom.rounding) for m in repair.move_ids):
repair.show_set_qty_button = True repair.show_set_qty_button = True
elif any(not float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding) and float_compare(m.quantity_done, m.reserved_availability, precision_rounding=m.product_uom.rounding) == 0 for m in repair.move_ids): elif any(not float_is_zero(m.quantity, precision_rounding=m.product_uom.rounding) for m in repair.move_ids):
repair.show_clear_qty_button = True repair.show_clear_qty_button = True
@api.depends('move_ids', 'state', 'move_ids.product_uom_qty') @api.depends('move_ids', 'state', 'move_ids.product_uom_qty')
def _compute_unreserve_visible(self): def _compute_unreserve_visible(self):
for repair in self: for repair in self:
already_reserved = repair.state not in ('done', 'cancel') and any(repair.mapped('move_ids.move_line_ids.reserved_qty')) already_reserved = repair.state not in ('done', 'cancel') and any(repair.mapped('move_ids.move_line_ids.quantity'))
repair.unreserve_visible = already_reserved repair.unreserve_visible = already_reserved
repair.reserve_visible = repair.state in ('confirmed', 'under_repair') and any(move.product_uom_qty and move.state in ['confirmed', 'partially_available'] for move in repair.move_ids) repair.reserve_visible = repair.state in ('confirmed', 'under_repair') and any(move.product_uom_qty and move.state in ['confirmed', 'partially_available'] for move in repair.move_ids)
@@ -326,7 +324,7 @@ class Repair(models.Model):
return self.move_ids._action_assign() return self.move_ids._action_assign()
def action_clear_quantities_to_zero(self): def action_clear_quantities_to_zero(self):
return self.move_ids.filtered(lambda m: float_compare(m.quantity_done, m.reserved_availability, precision_rounding=m.product_uom.rounding) == 0)._clear_quantities_to_zero() return self.move_ids.filtered(lambda m: float_compare(m.quantity, m.reserved_availability, precision_rounding=m.product_uom.rounding) == 0)._clear_quantities_to_zero()
def action_create_sale_order(self): def action_create_sale_order(self):
if any(repair.sale_order_id for repair in self): if any(repair.sale_order_id for repair in self):
@@ -377,16 +375,15 @@ class Repair(models.Model):
# Clean the context to get rid of residual default_* keys that could cause issues # Clean the context to get rid of residual default_* keys that could cause issues
# during the creation of stock.move. # during the creation of stock.move.
self = self.with_context(clean_context(self._context)) self = self.with_context(clean_context(self._context))
self._check_product_tracking()
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
product_move_vals = [] product_move_vals = []
# Cancel moves with 0 quantity_done # Cancel moves with 0 quantity
self.move_ids.filtered(lambda m: float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding))._action_cancel() self.move_ids.filtered(lambda m: float_is_zero(m.quantity, precision_rounding=m.product_uom.rounding))._action_cancel()
no_service_policy = 'service_policy' not in self.env['product.template'] no_service_policy = 'service_policy' not in self.env['product.template']
#SOL qty delivered = repair.move_ids.quantity_done #SOL qty delivered = repair.move_ids.quantity
for repair in self: for repair in self:
if repair.sale_order_line_id: if repair.sale_order_line_id:
ro_origin_product = repair.sale_order_line_id.product_template_id ro_origin_product = repair.sale_order_line_id.product_template_id
@@ -418,9 +415,8 @@ class Repair(models.Model):
'move_line_ids': [(0, 0, { 'move_line_ids': [(0, 0, {
'product_id': repair.product_id.id, 'product_id': repair.product_id.id,
'lot_id': repair.lot_id.id, 'lot_id': repair.lot_id.id,
'reserved_uom_qty': 0, # bypass reservation here
'product_uom_id': repair.product_uom.id or repair.product_id.uom_id.id, 'product_uom_id': repair.product_uom.id or repair.product_id.uom_id.id,
'qty_done': repair.product_qty, 'quantity': repair.product_qty,
'package_id': False, 'package_id': False,
'result_package_id': False, 'result_package_id': False,
'owner_id': owner_id, 'owner_id': owner_id,
@@ -436,7 +432,6 @@ class Repair(models.Model):
product_moves = self.env['stock.move'].create(product_move_vals) product_moves = self.env['stock.move'].create(product_move_vals)
self.move_id = product_moves.id self.move_id = product_moves.id
self.sale_order_line_id.move_ids._set_quantities_to_reservation()
all_moves = self.move_ids + product_moves all_moves = self.move_ids + product_moves
all_moves._action_done() all_moves._action_done()
@@ -454,7 +449,7 @@ class Repair(models.Model):
""" """
if self.filtered(lambda repair: repair.state != 'under_repair'): if self.filtered(lambda repair: repair.state != 'under_repair'):
raise UserError(_("Repair must be under repair in order to end reparation.")) raise UserError(_("Repair must be under repair in order to end reparation."))
if any(float_compare(move.quantity_done, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0 for move in self.move_ids): if any(float_compare(move.quantity, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0 for move in self.move_ids):
ctx = dict(self.env.context or {}) ctx = dict(self.env.context or {})
ctx['default_repair_ids'] = self.ids ctx['default_repair_ids'] = self.ids
return { return {
@@ -476,9 +471,6 @@ class Repair(models.Model):
self._action_repair_confirm() self._action_repair_confirm()
return self.write({'state': 'under_repair'}) return self.write({'state': 'under_repair'})
def action_set_quantities_to_reservation(self):
return self.move_ids.filtered(lambda m: float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding))._set_quantities_to_reservation()
def action_unreserve(self): def action_unreserve(self):
return self.move_ids.filtered(lambda m: m.state in ('assigned', 'partially_available'))._do_unreserve() return self.move_ids.filtered(lambda m: m.state in ('assigned', 'partially_available'))._do_unreserve()
@@ -537,15 +529,6 @@ class Repair(models.Model):
repairs_to_confirm.write({'state': 'confirmed'}) repairs_to_confirm.write({'state': 'confirmed'})
return True return True
def _check_product_tracking(self):
invalid_lines = self.move_ids.filtered(lambda x: x.has_tracking != 'none' and not x.lot_ids)
if invalid_lines:
products = invalid_lines.product_id
raise ValidationError(_(
"Serial number is required for operation lines with products: %s",
", ".join(products.mapped('display_name')),
))
def _get_location(self, field): def _get_location(self, field):
return self.picking_type_id[MAP_REPAIR_TO_PICKING_LOCATIONS[field]] return self.picking_type_id[MAP_REPAIR_TO_PICKING_LOCATIONS[field]]
+1 -1
View File
@@ -57,7 +57,7 @@ class SaleOrderLine(models.Model):
if len(move) != 1: if len(move) != 1:
continue continue
remaining_so_lines -= so_line remaining_so_lines -= so_line
so_line.qty_delivered = move.quantity_done so_line.qty_delivered = move.quantity
return super(SaleOrderLine, remaining_so_lines)._compute_qty_delivered() return super(SaleOrderLine, remaining_so_lines)._compute_qty_delivered()
@api.model_create_multi @api.model_create_multi
+1 -1
View File
@@ -113,7 +113,7 @@ class StockMove(models.Model):
for move in self: for move in self:
if move.sale_line_id or move.repair_line_type != 'add' or not move.repair_id.sale_order_id: if move.sale_line_id or move.repair_line_type != 'add' or not move.repair_id.sale_order_id:
continue continue
product_qty = move.product_uom_qty if move.repair_id.state != 'done' else move.quantity_done product_qty = move.product_uom_qty if move.repair_id.state != 'done' else move.quantity
so_line_vals.append({ so_line_vals.append({
'order_id': move.repair_id.sale_order_id.id, 'order_id': move.repair_id.sale_order_id.id,
'product_id': move.product_id.id, 'product_id': move.product_id.id,
+27 -28
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError
from odoo.tests import tagged, common, Form from odoo.tests import tagged, common, Form
from odoo.tools import float_compare, float_is_zero from odoo.tools import float_compare, float_is_zero
@@ -215,7 +215,7 @@ class TestRepair(common.TransactionCase):
# No partner warning -> working case -> already linked warning # No partner warning -> working case -> already linked warning
# Ensure SO doesn't exist # Ensure SO doesn't exist
self.assertEqual(repair.sale_order_id.__len__(), 0) self.assertEqual(len(repair.sale_order_id), 0)
repair.partner_id = None repair.partner_id = None
with self.assertRaises(UserError) as err: with self.assertRaises(UserError) as err:
repair.action_create_sale_order() repair.action_create_sale_order()
@@ -223,11 +223,10 @@ class TestRepair(common.TransactionCase):
repair.partner_id = self.res_partner_12.id repair.partner_id = self.res_partner_12.id
repair.action_create_sale_order() repair.action_create_sale_order()
# Ensure SO and SOL were created # Ensure SO and SOL were created
self.assertNotEqual(repair.sale_order_id.__len__(), 0) self.assertNotEqual(len(repair.sale_order_id), 0)
self.assertEqual(repair.sale_order_id.order_line.__len__(), 3) self.assertEqual(len(repair.sale_order_id.order_line), 3)
with self.assertRaises(UserError) as err: with self.assertRaises(UserError) as err:
repair.action_create_sale_order() repair.action_create_sale_order()
self.assertIn("You cannot create a quotation for a repair order that is already linked", err.exception.args[0])
# (*) -> cancel (action_repair_cancel) # (*) -> cancel (action_repair_cancel)
# PRE # PRE
@@ -271,15 +270,16 @@ class TestRepair(common.TransactionCase):
# under_repair -> done (action_repair_end -> action_repair_done) # under_repair -> done (action_repair_end -> action_repair_done)
# PRE # PRE
# state == under_repair !-> UserError # state == under_repair !-> UserError
# lines' qty_done >= lines' product_uom_qty !-> Warning # lines' quantity >= lines' product_uom_qty !-> Warning
# line tracked => line has lot_ids !-> ValidationError # line tracked => line has lot_ids !-> ValidationError
# POST # POST
# lines with qty_done == 0 are cancelled (related sol product_uom_qty is consequently set to 0) # lines with quantity == 0 are cancelled (related sol product_uom_qty is consequently set to 0)
# repair.product_id => repair.move_id # repair.product_id => repair.move_id
# move_ids.state == (done || cancel) # move_ids.state == (done || cancel)
# state == done # state == done
# move_ids with qty_done (LOWER or HIGHER than) product_uom_qty MUST NOT be splitted # move_ids with quantity (LOWER or HIGHER than) product_uom_qty MUST NOT be splitted
# Any line with qty_done < product_uom_qty => Warning # Any line with quantity < product_uom_qty => Warning
repair.move_ids.picked = True
end_action = repair.action_repair_end() end_action = repair.action_repair_end()
self.assertEqual(end_action.get("res_model"), "repair.warn.uncomplete.move") self.assertEqual(end_action.get("res_model"), "repair.warn.uncomplete.move")
warn_uncomplete_wizard = Form( warn_uncomplete_wizard = Form(
@@ -287,22 +287,22 @@ class TestRepair(common.TransactionCase):
.with_context(**end_action['context']) .with_context(**end_action['context'])
).save() ).save()
# LineB : no serial => ValidationError # LineB : no serial => ValidationError
with self.assertRaises(ValidationError) as err: lot = lineB.move_line_ids.lot_id
with self.assertRaises(UserError) as err:
lineB.move_line_ids.lot_id = False
warn_uncomplete_wizard.action_validate() warn_uncomplete_wizard.action_validate()
self.assertIn("Serial number is required for operation lines", err.exception.args[0])
# LineB with lots # LineB with lots
move_line = lineB.move_line_ids[0] lineB.move_line_ids.lot_id = lot
move_line.qty_done = move_line.reserved_uom_qty
lineA.quantity_done = 2 # qty_done = product_uom_qty lineA.quantity = 2 # quantity = product_uom_qty
lineC.quantity_done = 2 # qty_done > product_uom_qty (No warning) lineC.quantity = 2 # quantity > product_uom_qty (No warning)
lineD = self._create_simple_part_move(repair.id, 0.0) lineD = self._create_simple_part_move(repair.id, 0.0)
repair.move_ids |= lineD # product_uom_qty = 0 : state is cancelled repair.move_ids |= lineD # product_uom_qty = 0 : state is cancelled
self.assertEqual(lineD.state, 'assigned') self.assertEqual(lineD.state, 'assigned')
num_of_lines = repair.move_ids.__len__() num_of_lines = len(repair.move_ids)
self.assertEqual(repair.move_id.__len__(), 0) self.assertFalse(repair.move_id)
repair.action_repair_end() repair.action_repair_end()
self.assertEqual(repair.state, "done") self.assertEqual(repair.state, "done")
@@ -310,15 +310,14 @@ class TestRepair(common.TransactionCase):
#line a,b,c are 'done', line d is 'cancel' #line a,b,c are 'done', line d is 'cancel'
self.assertTrue(all(m.state == 'done' for m in done_moves)) self.assertTrue(all(m.state == 'done' for m in done_moves))
self.assertEqual(lineD.state, 'cancel') self.assertEqual(lineD.state, 'cancel')
self.assertEqual(repair.move_id.__len__(), 1) self.assertEqual(len(repair.move_id), 1)
self.assertEqual(repair.move_ids.__len__(), num_of_lines) # No split self.assertEqual(len(repair.move_ids), num_of_lines) # No split
# (*) -> cancel (action_repair_cancel) # (*) -> cancel (action_repair_cancel)
# PRE # PRE
# state != done !-> UserError # state != done !-> UserError
with self.assertRaises(UserError) as err: with self.assertRaises(UserError) as err:
repair.action_repair_cancel() repair.action_repair_cancel()
self.assertEqual(err.exception.args[0], "You cannot cancel a Repair Order that's already been completed")
def test_02_repair_sale_order_binding(self): def test_02_repair_sale_order_binding(self):
# Binding from SO to RO(s) # Binding from SO to RO(s)
@@ -411,13 +410,14 @@ class TestRepair(common.TransactionCase):
# repair_order.action_repair_end() # repair_order.action_repair_end()
# -> order_line.qty_delivered == order_line.product_uom_qty # -> order_line.qty_delivered == order_line.product_uom_qty
# -> "RO Lines"'s SOL.qty_delivered == move.quantity_done # -> "RO Lines"'s SOL.qty_delivered == move.quantity
for line in repair_order.move_ids:
line.quantity_done = line.product_uom_qty
repair_order.action_repair_start() repair_order.action_repair_start()
for line in repair_order.move_ids:
line.quantity = line.product_uom_qty
repair_order.move_ids.picked = True
repair_order.action_repair_end() repair_order.action_repair_end()
self.assertTrue(float_is_zero(order_line.qty_delivered, 2)) self.assertTrue(float_is_zero(order_line.qty_delivered, 2))
self.assertEqual(float_compare(sol_part_0.product_uom_qty, ro_line_0.quantity_done, 2), 0) self.assertEqual(float_compare(sol_part_0.product_uom_qty, ro_line_0.quantity, 2), 0)
self.assertTrue(float_is_zero(sol_part_1.qty_delivered, 2)) self.assertTrue(float_is_zero(sol_part_1.qty_delivered, 2))
def test_03_sale_order_delivered_qty(self): def test_03_sale_order_delivered_qty(self):
@@ -546,9 +546,8 @@ class TestRepair(common.TransactionCase):
picking = picking_form.save() picking = picking_form.save()
picking.action_confirm() picking.action_confirm()
picking.action_assign() picking.action_assign()
res_dict = picking.button_validate() picking.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
self.assertEqual(picking.state, 'done') self.assertEqual(picking.state, 'done')
# Create a return # Create a return
stock_return_picking_form = Form(self.env['stock.return.picking'] stock_return_picking_form = Form(self.env['stock.return.picking']
@@ -558,7 +557,7 @@ class TestRepair(common.TransactionCase):
stock_return_picking.product_return_moves.quantity = 1.0 stock_return_picking.product_return_moves.quantity = 1.0
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_picking = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_picking = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_picking.action_set_quantities_to_reservation() return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
self.assertEqual(return_picking.state, 'done') self.assertEqual(return_picking.state, 'done')
+2 -6
View File
@@ -67,7 +67,6 @@
<button name="action_validate" invisible="state != 'draft'" type="object" string="Confirm Repair" class="oe_highlight" data-hotkey="v"/> <button name="action_validate" invisible="state != 'draft'" type="object" string="Confirm Repair" class="oe_highlight" data-hotkey="v"/>
<button name="action_repair_start" invisible="state != 'confirmed'" type="object" string="Start Repair" class="oe_highlight" data-hotkey="q"/> <button name="action_repair_start" invisible="state != 'confirmed'" type="object" string="Start Repair" class="oe_highlight" data-hotkey="q"/>
<button name="action_repair_end" invisible="state != 'under_repair'" type="object" string="End Repair" class="oe_highlight" data-hotkey="x"/> <button name="action_repair_end" invisible="state != 'under_repair'" type="object" string="End Repair" class="oe_highlight" data-hotkey="x"/>
<button name="action_set_quantities_to_reservation" invisible="not show_set_qty_button" string="Set quantities" type="object" groups="stock.group_stock_user" class="o_btn_validate" data-hotkey="g"/>
<button name="action_clear_quantities_to_zero" invisible="not show_clear_qty_button" string="Clear quantities" type="object" groups="stock.group_stock_user" class="o_btn_validate" data-hotkey="g"/> <button name="action_clear_quantities_to_zero" invisible="not show_clear_qty_button" string="Clear quantities" type="object" groups="stock.group_stock_user" class="o_btn_validate" data-hotkey="g"/>
<button name="action_assign" invisible="state in ('draft', 'done', 'cancel') or not reserve_visible" string="Check availability" type="object"/> <button name="action_assign" invisible="state in ('draft', 'done', 'cancel') or not reserve_visible" string="Check availability" type="object"/>
<button name="action_unreserve" type="object" string="Unreserve" invisible="not unreserve_visible" data-hotkey="w"/> <button name="action_unreserve" type="object" string="Unreserve" invisible="not unreserve_visible" data-hotkey="w"/>
@@ -147,7 +146,6 @@
<field name="picking_code" column_invisible="True"/> <field name="picking_code" column_invisible="True"/>
<field name="product_type" column_invisible="True"/> <field name="product_type" column_invisible="True"/>
<field name="show_details_visible" column_invisible="True"/> <field name="show_details_visible" column_invisible="True"/>
<field name="show_reserved_availability" column_invisible="True"/>
<field name="show_operations" readonly="1" column_invisible="True"/> <field name="show_operations" readonly="1" column_invisible="True"/>
<field name="additional" column_invisible="True"/> <field name="additional" column_invisible="True"/>
<field name="move_lines_count" column_invisible="True"/> <field name="move_lines_count" column_invisible="True"/>
@@ -159,18 +157,16 @@
<field name="description_picking" string="Description" optional="hide"/> <field name="description_picking" string="Description" optional="hide"/>
<field name="date" optional="hide"/> <field name="date" optional="hide"/>
<field name="date_deadline" optional="hide"/> <field name="date_deadline" optional="hide"/>
<field name="is_initial_demand_editable" column_invisible="True"/>
<field name="is_quantity_done_editable" column_invisible="True"/>
<field name="product_packaging_id" groups="product.group_stock_packaging"/> <field name="product_packaging_id" groups="product.group_stock_packaging"/>
<field name="product_uom_qty" string="Demand" readonly="state in ('done', 'cancel')"/> <field name="product_uom_qty" string="Demand" readonly="state in ('done', 'cancel')"/>
<field name="forecast_expected_date" column_invisible="True"/> <field name="forecast_expected_date" column_invisible="True"/>
<field name="reserved_availability" column_invisible="True"/>
<button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart" column_invisible="parent.state != 'draft'" invisible="forecast_availability &lt; 0 and repair_line_type == 'add'"/> <button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart" column_invisible="parent.state != 'draft'" invisible="forecast_availability &lt; 0 and repair_line_type == 'add'"/>
<button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart text-danger" column_invisible="parent.state != 'draft'" invisible="forecast_availability &gt;= 0 or repair_line_type != 'add'"/> <button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart text-danger" column_invisible="parent.state != 'draft'" invisible="forecast_availability &gt;= 0 or repair_line_type != 'add'"/>
<field name="forecast_availability" string="Reserved" column_invisible="parent.state in ('draft', 'done')" widget="forecast_widget"/> <field name="forecast_availability" string="Reserved" column_invisible="parent.state in ('draft', 'done')" widget="forecast_widget"/>
<field name="product_qty" readonly="1" column_invisible="True"/> <field name="product_qty" readonly="1" column_invisible="True"/>
<field name="quantity_done" string="Done" readonly="not product_id"/> <field name="quantity" string="Done" readonly="not product_id"/>
<field name="product_uom" readonly="state != 'draft' and not additional" options="{'no_open': True, 'no_create': True}" string="Unit of Measure" groups="uom.group_uom"/> <field name="product_uom" readonly="state != 'draft' and not additional" options="{'no_open': True, 'no_create': True}" string="Unit of Measure" groups="uom.group_uom"/>
<field name="picked" string="Used"/>
<field name="lot_ids" widget="many2many_tags" <field name="lot_ids" widget="many2many_tags"
groups="stock.group_production_lot" groups="stock.group_production_lot"
invisible="not show_details_visible or has_tracking != 'serial'" invisible="not show_details_visible or has_tracking != 'serial'"
+2 -2
View File
@@ -56,8 +56,8 @@ class SaleOrderLine(models.Model):
if any((m.location_dest_id.usage == 'customer' and m.state != 'done') if any((m.location_dest_id.usage == 'customer' and m.state != 'done')
or (m.location_dest_id.usage != 'customer' or (m.location_dest_id.usage != 'customer'
and m.state == 'done' and m.state == 'done'
and float_compare(m.quantity_done, and float_compare(m.quantity,
sum(sub_m.product_uom._compute_quantity(sub_m.quantity_done, m.product_uom) for sub_m in m.returned_move_ids if sub_m.state == 'done'), sum(sub_m.product_uom._compute_quantity(sub_m.quantity, m.product_uom) for sub_m in m.returned_move_ids if sub_m.state == 'done'),
precision_rounding=m.product_uom.rounding) > 0) precision_rounding=m.product_uom.rounding) > 0)
for m in moves) or not moves: for m in moves) or not moves:
order_line.qty_delivered = 0 order_line.qty_delivered = 0
+1 -1
View File
@@ -20,6 +20,6 @@ class StockMoveLine(models.Model):
kit_lines = self.filtered(lambda move_line: move_line.move_id.bom_line_id.bom_id.type == 'phantom') kit_lines = self.filtered(lambda move_line: move_line.move_id.bom_line_id.bom_id.type == 'phantom')
for move_line in kit_lines: for move_line in kit_lines:
unit_price = move_line.product_id.list_price unit_price = move_line.product_id.list_price
qty = move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id) qty = move_line.product_uom_id._compute_quantity(move_line.quantity, move_line.product_id.uom_id)
move_line.sale_price = unit_price * qty move_line.sale_price = unit_price * qty
super(StockMoveLine, self - kit_lines)._compute_sale_price() super(StockMoveLine, self - kit_lines)._compute_sale_price()
@@ -96,7 +96,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so.action_confirm() so.action_confirm()
so.picking_ids.move_ids.quantity_done = 1 so.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
so.picking_ids.button_validate() so.picking_ids.button_validate()
invoice = so.with_context(default_journal_id=self.company_data['default_journal_sale'].id)._create_invoices() invoice = so.with_context(default_journal_id=self.company_data['default_journal_sale'].id)._create_invoices()
@@ -190,9 +190,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
# Deliver the three finished products # Deliver the three finished products
pick = so.picking_ids pick = so.picking_ids
# To check the products on the picking # To check the products on the picking
wiz_act = pick.button_validate() pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# Create the invoice # Create the invoice
so._create_invoices() so._create_invoices()
invoice = so.invoice_ids invoice = so.invoice_ids
@@ -244,7 +242,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 20, 60]]) } for p in [10, 20, 60]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Sell 3 kits # Sell 3 kits
@@ -267,7 +265,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids picking = so.picking_ids
while picking: while picking:
pickings.append(picking) pickings.append(picking)
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -288,7 +286,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100, 'price_unit': 100,
}) })
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Return the second picking (i.e. one component @20) # Return the second picking (i.e. one component @20)
@@ -296,7 +294,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save() return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
return_picking_id, dummy = return_wizard._create_returns() return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.write({'quantity': 1, 'picked': True})
return_picking.button_validate() return_picking.button_validate()
# Add a credit note for the returned kit # Add a credit note for the returned kit
@@ -349,7 +347,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 20, 60]]) } for p in [10, 20, 60]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Sell 3 kits # Sell 3 kits
@@ -372,7 +370,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids picking = so.picking_ids
while picking: while picking:
pickings.append(picking) pickings.append(picking)
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -393,7 +391,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100, 'price_unit': 100,
}) })
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Return the second picking (i.e. one component @20) # Return the second picking (i.e. one component @20)
@@ -401,7 +399,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save() return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
return_picking_id, dummy = return_wizard._create_returns() return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.write({'quantity': 1, 'picked': True})
return_picking.button_validate() return_picking.button_validate()
# Create a new invoice for the returned kit # Create a new invoice for the returned kit
@@ -460,7 +458,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so.action_confirm() so.action_confirm()
so.picking_ids.move_ids.quantity_done = 1 so.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
so.picking_ids.button_validate() so.picking_ids.button_validate()
invoice = so._create_invoices() invoice = so._create_invoices()
@@ -513,7 +511,8 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so.action_confirm() so.action_confirm()
so.picking_ids.move_line_ids.qty_done = 1 so.picking_ids.move_line_ids.quantity = 1
so.picking_ids.move_ids.picked = True
so.picking_ids.button_validate() so.picking_ids.button_validate()
invoice = so._create_invoices() invoice = so._create_invoices()
+34 -42
View File
@@ -196,7 +196,10 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
""" """
moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys()) moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys())
for move in moves_to_process: for move in moves_to_process:
move.write({'quantity_done': quantities_to_process[move.product_id]}) move.write({
'quantity': quantities_to_process[move.product_id],
'picked': True
})
def _assert_quantities(self, moves, quantities_to_process): def _assert_quantities(self, moves, quantities_to_process):
""" Helper to check expected quantities based on a dict following this structure : """ Helper to check expected quantities based on a dict following this structure :
@@ -231,7 +234,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
move._action_confirm() move._action_confirm()
move._action_assign() move._action_assign()
move_line = move.move_line_ids[0] move_line = move.move_line_ids[0]
move_line.qty_done = qty_to_process[comp][0] move_line.quantity = qty_to_process[comp][0]
move._action_done() move._action_done()
def test_00_sale_mrp_flow(self): def test_00_sale_mrp_flow(self):
@@ -549,7 +552,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# deliver partially (1 of each instead of 5), check the so's invoice_status and delivered quantities # deliver partially (1 of each instead of 5), check the so's invoice_status and delivered quantities
pick = so.picking_ids pick = so.picking_ids
pick.move_ids.write({'quantity_done': 1}) pick.move_ids.write({'quantity': 1, 'picked': True})
wiz_act = pick.button_validate() wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process() wiz.process()
@@ -561,9 +564,9 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
pick_2 = so.picking_ids.filtered('backorder_id') pick_2 = so.picking_ids.filtered('backorder_id')
for move in pick_2.move_ids: for move in pick_2.move_ids:
if move.product_id.id == product_desk_bolt.id: if move.product_id.id == product_desk_bolt.id:
move.write({'quantity_done': 19}) move.write({'quantity': 19, 'picked': True})
else: else:
move.write({'quantity_done': 4}) move.write({'quantity': 4, 'picked': True})
pick_2.button_validate() pick_2.button_validate()
del_qty = sum(sol.qty_delivered for sol in so.order_line) del_qty = sum(sol.qty_delivered for sol in so.order_line)
@@ -670,9 +673,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
pick = self.so.picking_ids pick = self.so.picking_ids
# To check the products on the picking # To check the products on the picking
self.assertEqual(pick.move_ids.mapped('product_id'), self.component1 | self.component2) self.assertEqual(pick.move_ids.mapped('product_id'), self.component1 | self.component2)
wiz_act = pick.button_validate() pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# Create the invoice # Create the invoice
self.so._create_invoices() self.so._create_invoices()
self.invoice = self.so.invoice_ids self.invoice = self.so.invoice_ids
@@ -749,7 +750,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self._assert_quantities(move_ids, expected_quantities) self._assert_quantities(move_ids, expected_quantities)
# Process only x1 of the first component then create a backorder for the missing components # Process only x1 of the first component then create a backorder for the missing components
picking_original.move_ids.sorted()[0].write({'quantity_done': 1}) picking_original.move_ids.sorted()[0].write({'quantity': 1, 'picked': True})
wiz_act = so.picking_ids[0].button_validate() wiz_act = so.picking_ids[0].button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process()
@@ -762,7 +763,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Process only x6 each componenent in the picking # Process only x6 each componenent in the picking
# Then create a backorder for the missing components # Then create a backorder for the missing components
backorder_1.move_ids.write({'quantity_done': 6}) backorder_1.move_ids.write({'quantity': 6, 'picked': True})
wiz_act = backorder_1.button_validate() wiz_act = backorder_1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process()
@@ -778,7 +779,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Process x3 more unit of each components : # Process x3 more unit of each components :
# - Now only 3 kits should be delivered # - Now only 3 kits should be delivered
# - A backorder will be created, the SO should have 3 picking_ids linked to it. # - A backorder will be created, the SO should have 3 picking_ids linked to it.
backorder_2.move_ids.write({'quantity_done': 3}) backorder_2.move_ids.write({'quantity': 3, 'picked': True})
wiz_act = backorder_2.button_validate() wiz_act = backorder_2.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process() wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process()
@@ -870,7 +871,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Process only 7 units of each component # Process only 7 units of each component
qty_to_process = 7 qty_to_process = 7
move_ids.write({'quantity_done': qty_to_process}) move_ids.write({'quantity': qty_to_process, 'picked': True})
# Create a backorder for the missing componenents # Create a backorder for the missing componenents
wiz_act = picking_original.button_validate() wiz_act = picking_original.button_validate()
@@ -976,9 +977,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
# Process all components and validate the picking # Process all components and validate the picking
wiz_act = return_pick.button_validate() return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# Now quantity delivered should be 3 again # Now quantity delivered should be 3 again
self.assertEqual(order_line.qty_delivered, 3) self.assertEqual(order_line.qty_delivered, 3)
@@ -995,7 +994,8 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Process all components except one of each # Process all components except one of each
for move in return_of_return_pick.move_ids: for move in return_of_return_pick.move_ids:
move.write({ move.write({
'quantity_done': expected_quantities[move.product_id] - 1, 'quantity': expected_quantities[move.product_id] - 1,
'picked': True,
'to_refund': True 'to_refund': True
}) })
@@ -1702,7 +1702,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Validate delivery # Validate delivery
pick = order.picking_ids pick = order.picking_ids
pick.move_ids.write({'quantity_done': 1}) pick.move_ids.write({'quantity': 1, 'picked': True})
pick.button_validate() pick.button_validate()
qty_del_validated = sum(sol.qty_delivered for sol in order.order_line) qty_del_validated = sum(sol.qty_delivered for sol in order.order_line)
self.assertEqual(qty_del_validated, 1.0, 'The order went from warehouse to client, so it has been delivered') self.assertEqual(qty_del_validated, 1.0, 'The order went from warehouse to client, so it has been delivered')
@@ -1718,7 +1718,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
}) })
res = return_wiz.create_returns() res = return_wiz.create_returns()
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
return_pick.move_line_ids.qty_done = 1 return_pick.move_line_ids.quantity = 1
return_pick.button_validate() # validate return return_pick.button_validate() # validate return
# Delivered quantities to the client should be 0 # Delivered quantities to the client should be 0
@@ -1766,9 +1766,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so = so_form.save() so = so_form.save()
so.action_confirm() so.action_confirm()
wiz_act = so.picking_ids.button_validate() so.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
p2_bom.type = "normal" p2_bom.type = "normal"
@@ -1902,8 +1900,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so.action_confirm() so.action_confirm()
# Deliver the products # Deliver the products
pick = so.picking_ids pick = so.picking_ids
wiz_act = pick.button_validate() pick.button_validate()
Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process()
# Create the invoice # Create the invoice
so._create_invoices() so._create_invoices()
# Validate the invoice # Validate the invoice
@@ -1946,9 +1943,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Check picking creation # Check picking creation
self.assertEqual(len(so.picking_ids), 1, "A picking should be created after the SO validation") self.assertEqual(len(so.picking_ids), 1, "A picking should be created after the SO validation")
wiz_act = so.picking_ids.button_validate() so.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
so._action_cancel() so._action_cancel()
so.action_draft() so.action_draft()
@@ -1989,9 +1984,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.assertEqual(price, 10) self.assertEqual(price, 10)
picking = so.picking_ids picking = so.picking_ids
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
ctx = {'active_ids':picking.ids, 'active_id': picking.ids[0], 'active_model': 'stock.picking'} ctx = {'active_ids':picking.ids, 'active_id': picking.ids[0], 'active_model': 'stock.picking'}
return_picking_wizard_form = Form(self.env['stock.return.picking'].with_context(ctx)) return_picking_wizard_form = Form(self.env['stock.return.picking'].with_context(ctx))
@@ -2049,8 +2042,6 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
{'product_id': self.component_f.id, 'product_uom_qty': 100}, {'product_id': self.component_f.id, 'product_uom_qty': 100},
{'product_id': self.component_g.id, 'product_uom_qty': 200}, {'product_id': self.component_g.id, 'product_uom_qty': 200},
]) ])
delivery.action_set_quantities_to_reservation()
delivery.button_validate() delivery.button_validate()
# Return 2 [uom_ten] x kit_3 # Return 2 [uom_ten] x kit_3
@@ -2060,7 +2051,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_wizard.product_return_moves[1].quantity = 40 return_wizard.product_return_moves[1].quantity = 40
action = return_wizard.create_returns() action = return_wizard.create_returns()
return_picking = self.env['stock.picking'].browse(action['res_id']) return_picking = self.env['stock.picking'].browse(action['res_id'])
return_picking.action_set_quantities_to_reservation() return_picking.move_ids.picked = True
return_picking.button_validate() return_picking.button_validate()
# Adapt the SOL qty according to the delivered one # Adapt the SOL qty according to the delivered one
@@ -2069,10 +2060,10 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
line.product_uom_qty = 8 line.product_uom_qty = 8
self.assertRecordValues(so.picking_ids.sorted('id').move_ids, [ self.assertRecordValues(so.picking_ids.sorted('id').move_ids, [
{'product_id': self.component_f.id, 'location_dest_id': custo_location.id, 'quantity_done': 100, 'state': 'done'}, {'product_id': self.component_f.id, 'location_dest_id': custo_location.id, 'quantity': 100, 'state': 'done'},
{'product_id': self.component_g.id, 'location_dest_id': custo_location.id, 'quantity_done': 200, 'state': 'done'}, {'product_id': self.component_g.id, 'location_dest_id': custo_location.id, 'quantity': 200, 'state': 'done'},
{'product_id': self.component_f.id, 'location_dest_id': stock_location.id, 'quantity_done': 20, 'state': 'done'}, {'product_id': self.component_f.id, 'location_dest_id': stock_location.id, 'quantity': 20, 'state': 'done'},
{'product_id': self.component_g.id, 'location_dest_id': stock_location.id, 'quantity_done': 40, 'state': 'done'}, {'product_id': self.component_g.id, 'location_dest_id': stock_location.id, 'quantity': 40, 'state': 'done'},
]) ])
def test_kit_decrease_sol_qty_to_zero(self): def test_kit_decrease_sol_qty_to_zero(self):
@@ -2139,7 +2130,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
delivery = so.picking_ids delivery = so.picking_ids
for m in delivery.move_ids: for m in delivery.move_ids:
m.quantity_done = m.product_uom_qty m.write({'quantity': m.product_uom_qty, 'picked': True})
delivery.button_validate() delivery.button_validate()
self.assertEqual(delivery.state, 'done') self.assertEqual(delivery.state, 'done')
@@ -2150,7 +2141,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_picking_id, dummy = return_wizard._create_returns() return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
for m in return_picking.move_ids: for m in return_picking.move_ids:
m.quantity_done = m.product_uom_qty m.write({'quantity': m.product_uom_qty, 'picked': True})
return_picking.button_validate() return_picking.button_validate()
self.assertEqual(return_picking.state, 'done') self.assertEqual(return_picking.state, 'done')
@@ -2197,7 +2188,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 50]]) } for p in [10, 50]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
so = self.env['sale.order'].create({ so = self.env['sale.order'].create({
@@ -2215,7 +2206,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so.action_confirm() so.action_confirm()
picking = so.picking_ids picking = so.picking_ids
picking.move_ids.quantity_done = 1.0 picking.move_ids.write({'quantity': 1.0, 'picked': True})
picking.button_validate() picking.button_validate()
invoice01 = so._create_invoices() invoice01 = so._create_invoices()
@@ -2279,7 +2270,8 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
})], })],
}) })
so.action_confirm() so.action_confirm()
so.picking_ids.move_line_ids.qty_done = 1 so.picking_ids.move_line_ids.quantity = 1
so.picking_ids.move_ids.picked = True
so.picking_ids.button_validate() so.picking_ids.button_validate()
invoice = so._create_invoices() invoice = so._create_invoices()
@@ -2310,7 +2302,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.bom_kit_1.unlink() self.bom_kit_1.unlink()
for move in so.order_line.move_ids: for move in so.order_line.move_ids:
move.quantity_done = move.product_uom_qty move.write({'quantity': move.product_uom_qty, 'picked': True})
so.picking_ids.button_validate() so.picking_ids.button_validate()
self.assertEqual(so.picking_ids.state, 'done') self.assertEqual(so.picking_ids.state, 'done')
@@ -184,7 +184,7 @@ class TestSaleMrpKitBom(TransactionCase):
self.assertEqual(so.order_line.qty_delivered, 0) self.assertEqual(so.order_line.qty_delivered, 0)
picking = so.picking_ids picking = so.picking_ids
picking.move_ids.quantity_done = 0.86000 picking.move_ids.write({'quantity': 0.86000, 'picked': True})
picking.button_validate() picking.button_validate()
# Checks the delivery amount (must be 10). # Checks the delivery amount (must be 10).
@@ -249,9 +249,7 @@ class TestSaleMrpKitBom(TransactionCase):
self.assertEqual(so.order_line.qty_delivered, 0) self.assertEqual(so.order_line.qty_delivered, 0)
picking = so.picking_ids picking = so.picking_ids
action = picking.button_validate() picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
# Checks the delivery amount (must be 1). # Checks the delivery amount (must be 1).
self.assertEqual(so.order_line.qty_delivered, 1) self.assertEqual(so.order_line.qty_delivered, 1)
@@ -386,7 +384,7 @@ class TestSaleMrpKitBom(TransactionCase):
ship = so.picking_ids[1] ship = so.picking_ids[1]
for move in pick.move_ids: for move in pick.move_ids:
move.quantity_done = 1 move.write({'quantity': 1, 'picked': True})
pick.action_put_in_pack() pick.action_put_in_pack()
pick.button_validate() pick.button_validate()
@@ -395,7 +393,7 @@ class TestSaleMrpKitBom(TransactionCase):
ship.package_level_ids._set_is_done() ship.package_level_ids._set_is_done()
for move_line in ship.move_line_ids: for move_line in ship.move_line_ids:
self.assertEqual(move_line.move_id.product_uom_qty, move_line.qty_done, "Quantity done should be equal to the quantity reserved in the move line") self.assertEqual(move_line.move_id.product_uom_qty, move_line.quantity, "Quantity done should be equal to the quantity reserved in the move line")
def test_kit_in_delivery_slip(self): def test_kit_in_delivery_slip(self):
""" """
@@ -511,7 +509,7 @@ class TestSaleMrpKitBom(TransactionCase):
so.action_confirm() so.action_confirm()
picking = so.picking_ids picking = so.picking_ids
self.assertEqual(len(so.picking_ids.move_ids_without_package), 7) self.assertEqual(len(so.picking_ids.move_ids_without_package), 7)
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
picking.button_validate() picking.button_validate()
self.assertEqual(picking.state, 'done') self.assertEqual(picking.state, 'done')
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import common, Form from odoo.tests import common
from odoo.tools import html2plaintext from odoo.tools import html2plaintext
@@ -57,9 +57,7 @@ class TestSaleMrpInvoices(common.TransactionCase):
}) })
so.action_confirm() so.action_confirm()
action = so.picking_ids.button_validate() so.picking_ids.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
invoice = so._create_invoices() invoice = so._create_invoices()
invoice.action_post() invoice.action_post()
@@ -71,7 +71,7 @@ class TestSalePurchaseStockFlow(TransactionCase):
'location_id': sm.location_id.id, 'location_id': sm.location_id.id,
'location_dest_id': sm.location_dest_id.id, 'location_dest_id': sm.location_dest_id.id,
'product_id': sm.product_id.id, 'product_id': sm.product_id.id,
'qty_done': 12, 'quantity': 12,
'company_id': sm.company_id.id, 'company_id': sm.company_id.id,
'product_uom_id': sm.product_uom.id, 'product_uom_id': sm.product_uom.id,
'picking_id': delivery.id, 'picking_id': delivery.id,
@@ -79,8 +79,8 @@ class TestSalePurchaseStockFlow(TransactionCase):
delivery.button_validate() delivery.button_validate()
self.assertEqual(delivery.state, 'done') self.assertEqual(delivery.state, 'done')
self.assertEqual(delivery.move_ids.move_line_ids.qty_done, 12) self.assertEqual(delivery.move_ids.move_line_ids.quantity, 12)
self.assertEqual(so.order_line.qty_delivered, 12) self.assertEqual(so.order_line.qty_delivered, 12)
sm.move_line_ids.qty_done = 10 sm.move_line_ids.quantity = 10
self.assertEqual(so.order_line.qty_delivered, 10) self.assertEqual(so.order_line.qty_delivered, 10)
+9 -9
View File
@@ -61,7 +61,7 @@ class AccountMove(models.Model):
continue continue
product = sml.product_id product = sml.product_id
product_uom = product.uom_id product_uom = product.uom_id
qty_done = sml.product_uom_id._compute_quantity(sml.qty_done, product_uom) quantity = sml.product_uom_id._compute_quantity(sml.quantity, product_uom)
# is it a stock return considering the document type (should it be it thought of as positively or negatively?) # is it a stock return considering the document type (should it be it thought of as positively or negatively?)
is_stock_return = ( is_stock_return = (
@@ -70,22 +70,22 @@ class AccountMove(models.Model):
self.move_type == 'out_refund' and (sml.location_id.usage, sml.location_dest_id.usage) == ('internal', 'customer') self.move_type == 'out_refund' and (sml.location_id.usage, sml.location_dest_id.usage) == ('internal', 'customer')
) )
if is_stock_return: if is_stock_return:
returned_qty = min(qties_per_lot[sml.lot_id], qty_done) returned_qty = min(qties_per_lot[sml.lot_id], quantity)
qties_per_lot[sml.lot_id] -= returned_qty qties_per_lot[sml.lot_id] -= returned_qty
qty_done = returned_qty - qty_done quantity = returned_qty - quantity
previous_qty_invoiced = previous_qties_invoiced[product] previous_qty_invoiced = previous_qties_invoiced[product]
previous_qty_delivered = previous_qties_delivered[product] previous_qty_delivered = previous_qties_delivered[product]
# If we return more than currently delivered (i.e., qty_done < 0), we remove the surplus # If we return more than currently delivered (i.e., quantity < 0), we remove the surplus
# from the previously delivered (and qty_done becomes zero). If it's a delivery, we first # from the previously delivered (and quantity becomes zero). If it's a delivery, we first
# try to reach the previous_qty_invoiced # try to reach the previous_qty_invoiced
if float_compare(qty_done, 0, precision_rounding=product_uom.rounding) < 0 or \ if float_compare(quantity, 0, precision_rounding=product_uom.rounding) < 0 or \
float_compare(previous_qty_delivered, previous_qty_invoiced, precision_rounding=product_uom.rounding) < 0: float_compare(previous_qty_delivered, previous_qty_invoiced, precision_rounding=product_uom.rounding) < 0:
previously_done = qty_done if is_stock_return else min(previous_qty_invoiced - previous_qty_delivered, qty_done) previously_done = quantity if is_stock_return else min(previous_qty_invoiced - previous_qty_delivered, quantity)
previous_qties_delivered[product] += previously_done previous_qties_delivered[product] += previously_done
qty_done -= previously_done quantity -= previously_done
qties_per_lot[sml.lot_id] += qty_done qties_per_lot[sml.lot_id] += quantity
for lot, qty in qties_per_lot.items(): for lot, qty in qties_per_lot.items():
# access the lot as a superuser in order to avoid an error # access the lot as a superuser in order to avoid an error
+6 -6
View File
@@ -62,7 +62,7 @@ class SaleOrderLine(models.Model):
line.qty_available_today = 0 line.qty_available_today = 0
line.free_qty_today = 0 line.free_qty_today = 0
for move in moves: for move in moves:
line.qty_available_today += move.product_uom._compute_quantity(move.reserved_availability, line.product_uom) line.qty_available_today += move.product_uom._compute_quantity(move.quantity, line.product_uom)
line.free_qty_today += move.product_id.uom_id._compute_quantity(move.forecast_availability, line.product_uom) line.free_qty_today += move.product_id.uom_id._compute_quantity(move.forecast_availability, line.product_uom)
line.scheduled_date = line.order_id.commitment_date or line._expected_date() line.scheduled_date = line.order_id.commitment_date or line._expected_date()
line.virtual_available_at_date = False line.virtual_available_at_date = False
@@ -148,7 +148,7 @@ class SaleOrderLine(models.Model):
if not line.is_expense and line.product_id.type in ['consu', 'product']: if not line.is_expense and line.product_id.type in ['consu', 'product']:
line.qty_delivered_method = 'stock_move' line.qty_delivered_method = 'stock_move'
@api.depends('move_ids.state', 'move_ids.scrapped', 'move_ids.quantity_done', 'move_ids.product_uom') @api.depends('move_ids.state', 'move_ids.scrapped', 'move_ids.quantity', 'move_ids.product_uom')
def _compute_qty_delivered(self): def _compute_qty_delivered(self):
super(SaleOrderLine, self)._compute_qty_delivered() super(SaleOrderLine, self)._compute_qty_delivered()
@@ -159,11 +159,11 @@ class SaleOrderLine(models.Model):
for move in outgoing_moves: for move in outgoing_moves:
if move.state != 'done': if move.state != 'done':
continue continue
qty += move.product_uom._compute_quantity(move.quantity_done, line.product_uom, rounding_method='HALF-UP') qty += move.product_uom._compute_quantity(move.quantity, line.product_uom, rounding_method='HALF-UP')
for move in incoming_moves: for move in incoming_moves:
if move.state != 'done': if move.state != 'done':
continue continue
qty -= move.product_uom._compute_quantity(move.quantity_done, line.product_uom, rounding_method='HALF-UP') qty -= move.product_uom._compute_quantity(move.quantity, line.product_uom, rounding_method='HALF-UP')
line.qty_delivered = qty line.qty_delivered = qty
@api.model_create_multi @api.model_create_multi
@@ -237,10 +237,10 @@ class SaleOrderLine(models.Model):
qty = 0.0 qty = 0.0
outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves() outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves()
for move in outgoing_moves: for move in outgoing_moves:
qty_to_compute = move.quantity_done if move.state == 'done' else move.product_uom_qty qty_to_compute = move.quantity if move.state == 'done' else move.product_uom_qty
qty += move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP') qty += move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP')
for move in incoming_moves: for move in incoming_moves:
qty_to_compute = move.quantity_done if move.state == 'done' else move.product_uom_qty qty_to_compute = move.quantity if move.state == 'done' else move.product_uom_qty
qty -= move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP') qty -= move.product_uom._compute_quantity(qty_to_compute, self.product_uom, rounding_method='HALF-UP')
return qty return qty
+2 -2
View File
@@ -106,7 +106,7 @@ class StockPicking(models.Model):
sale_order = move.picking_id.sale_id sale_order = move.picking_id.sale_id
# Creates new SO line only when pickings linked to a sale order and # Creates new SO line only when pickings linked to a sale order and
# for moves with qty. done and not already linked to a SO line. # for moves with qty. done and not already linked to a SO line.
if not sale_order or move.location_dest_id.usage != 'customer' or move.sale_line_id or not move.quantity_done: if not sale_order or move.location_dest_id.usage != 'customer' or move.sale_line_id or not move.picked:
continue continue
product = move.product_id product = move.product_id
so_line_vals = { so_line_vals = {
@@ -115,7 +115,7 @@ class StockPicking(models.Model):
'order_id': sale_order.id, 'order_id': sale_order.id,
'product_id': product.id, 'product_id': product.id,
'product_uom_qty': 0, 'product_uom_qty': 0,
'qty_delivered': move.quantity_done, 'qty_delivered': move.quantity,
'product_uom': move.product_uom.id, 'product_uom': move.product_uom.id,
} }
if product.invoice_policy == 'delivery': if product.invoice_policy == 'delivery':
@@ -57,7 +57,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 8, 'price_unit': 8,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = 1 in_move_1.write({'quantity': 1, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
in_move_2 = self.env['stock.move'].create({ in_move_2 = self.env['stock.move'].create({
'name': 'a', 'name': 'a',
@@ -69,7 +69,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10, 'price_unit': 10,
}) })
in_move_2._action_confirm() in_move_2._action_confirm()
in_move_2.quantity_done = 1 in_move_2.write({'quantity': 1, 'picked': True})
in_move_2._action_done() in_move_2._action_done()
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@@ -127,7 +127,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -160,7 +160,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0 self.product.standard_price = 14.0
# deliver the backorder # deliver the backorder
sale_order.picking_ids[0].move_ids.quantity_done = 1 sale_order.picking_ids[0].move_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids[0].button_validate() sale_order.picking_ids[0].button_validate()
# change the standard price to 16 # change the standard price to 16
@@ -199,7 +199,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -208,7 +208,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0 self.product.standard_price = 14.0
# deliver the backorder # deliver the backorder
sale_order.picking_ids.filtered('backorder_id').move_ids.quantity_done = 1 sale_order.picking_ids.filtered('backorder_id').move_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids.filtered('backorder_id').button_validate() sale_order.picking_ids.filtered('backorder_id').button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -266,7 +266,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -299,7 +299,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0 self.product.standard_price = 14.0
# deliver the backorder # deliver the backorder
sale_order.picking_ids[0].move_ids.quantity_done = 1 sale_order.picking_ids[0].move_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids[0].button_validate() sale_order.picking_ids[0].button_validate()
# change the standard price to 16 # change the standard price to 16
@@ -338,7 +338,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -347,7 +347,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0 self.product.standard_price = 14.0
# deliver the backorder # deliver the backorder
sale_order.picking_ids.filtered('backorder_id').move_ids.quantity_done = 1 sale_order.picking_ids.filtered('backorder_id').move_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids.filtered('backorder_id').button_validate() sale_order.picking_ids.filtered('backorder_id').button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -418,7 +418,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -456,7 +456,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 2 sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -503,7 +503,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
so.action_confirm() so.action_confirm()
pick = so.picking_ids pick = so.picking_ids
pick.move_ids.write({'quantity_done': 5}) pick.move_ids.write({'quantity': 5, 'picked': True})
pick.button_validate() pick.button_validate()
product.standard_price = 40 product.standard_price = 40
@@ -516,7 +516,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
res = return_wiz.create_returns() res = return_wiz.create_returns()
return_pick = self.env['stock.picking'].browse(res['res_id']) return_pick = self.env['stock.picking'].browse(res['res_id'])
return_pick.move_ids.write({'quantity_done': 1}) return_pick.move_ids.write({'quantity': 1, 'picked': True})
return_pick.button_validate() return_pick.button_validate()
picking = self.env['stock.picking'].create({ picking = self.env['stock.picking'].create({
@@ -532,7 +532,8 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'picking_id': picking.id, 'picking_id': picking.id,
'product_id': product.id, 'product_id': product.id,
'product_uom': product.uom_id.id, 'product_uom': product.uom_id.id,
'quantity_done': 1, 'quantity': 1,
'picked': True,
}) })
picking.button_validate() picking.button_validate()
@@ -573,7 +574,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -610,7 +611,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
# Create and confirm a sale order for 2@12 # Create and confirm a sale order for 2@12
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 2 sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -648,7 +649,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
# Create and confirm a sale order for 2@12 # Create and confirm a sale order for 2@12
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver both products (there should be two SML) # Deliver both products (there should be two SML)
sale_order.picking_ids.move_line_ids.qty_done = 1 sale_order.picking_ids.move_line_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice one by one # Invoice one by one
@@ -687,7 +688,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.env['stock.quant']._update_available_quantity(self.product, self.company_data['default_warehouse'].lot_stock_id, 2, owner_id=self.partner_b) self.env['stock.quant']._update_available_quantity(self.product, self.company_data['default_warehouse'].lot_stock_id, 2, owner_id=self.partner_b)
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
sale_order.picking_ids.move_line_ids.qty_done = 2 sale_order.picking_ids.move_line_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
invoice = sale_order._create_invoices() invoice = sale_order._create_invoices()
@@ -747,7 +748,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -790,7 +791,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 2 sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -843,7 +844,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
wiz = sale_order.picking_ids.button_validate() wiz = sale_order.picking_ids.button_validate()
wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process() wiz.process()
@@ -887,7 +888,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units() sale_order = self._so_and_confirm_two_units()
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 2 sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -927,7 +928,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10, 'price_unit': 10,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = 8 in_move_1.write({'quantity': 8, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
# Create and confirm a sale order for 2@12 # Create and confirm a sale order for 2@12
@@ -946,7 +947,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order.action_confirm() sale_order.action_confirm()
# Deliver 10 # Deliver 10
sale_order.picking_ids.move_ids.quantity_done = 10 sale_order.picking_ids.move_ids.write({'quantity': 10, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Make the second receipt # Make the second receipt
@@ -960,7 +961,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12, 'price_unit': 12,
}) })
in_move_2._action_confirm() in_move_2._action_confirm()
in_move_2.quantity_done = 2 in_move_2.write({'quantity': 2, 'picked': True})
in_move_2._action_done() in_move_2._action_done()
self.assertEqual(self.product.stock_valuation_layer_ids[-1].value, -4) # we sent two at 10 but they should have been sent at 12 self.assertEqual(self.product.stock_valuation_layer_ids[-1].value, -4) # we sent two at 10 but they should have been sent at 12
self.assertEqual(self.product.stock_valuation_layer_ids[-1].quantity, 0) self.assertEqual(self.product.stock_valuation_layer_ids[-1].quantity, 0)
@@ -1003,7 +1004,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 8, 'price_unit': 8,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = 5 in_move_1.write({'quantity': 5, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
# +8@12 # +8@12
@@ -1017,7 +1018,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12, 'price_unit': 12,
}) })
in_move_2._action_confirm() in_move_2._action_confirm()
in_move_2.quantity_done = 8 in_move_2.write({'quantity': 8, 'picked': True})
in_move_2._action_done() in_move_2._action_done()
# sale 1@20, deliver, invoice # sale 1@20, deliver, invoice
@@ -1034,7 +1035,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
sale_order.action_confirm() sale_order.action_confirm()
sale_order.picking_ids.move_ids.quantity_done = 1 sale_order.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
invoice = sale_order._create_invoices() invoice = sale_order._create_invoices()
invoice.action_post() invoice.action_post()
@@ -1053,7 +1054,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
sale_order.action_confirm() sale_order.action_confirm()
sale_order.picking_ids.move_ids.quantity_done = 6 sale_order.picking_ids.move_ids.write({'quantity': 6, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
invoice = sale_order._create_invoices() invoice = sale_order._create_invoices()
invoice.action_post() invoice.action_post()
@@ -1081,7 +1082,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10, 'price_unit': 10,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = 8 in_move_1.write({'quantity': 8, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
# Create and confirm a sale order for 10@12 # Create and confirm a sale order for 10@12
@@ -1100,7 +1101,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order.action_confirm() sale_order.action_confirm()
# Deliver 10 # Deliver 10
sale_order.picking_ids.move_ids.quantity_done = 10 sale_order.picking_ids.move_ids.write({'quantity': 10, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -1118,7 +1119,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12, 'price_unit': 12,
}) })
in_move_2._action_confirm() in_move_2._action_confirm()
in_move_2.quantity_done = 2 in_move_2.write({'quantity': 2, 'picked': True})
in_move_2._action_done() in_move_2._action_done()
# check the last anglo saxon move line # check the last anglo saxon move line
@@ -1143,12 +1144,12 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10, 'price_unit': 10,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = 2 in_move_1.write({'quantity': 2, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
# Create, confirm and deliver a sale order for 2@12 (SO1) # Create, confirm and deliver a sale order for 2@12 (SO1)
so_1 = self._so_and_confirm_two_units() so_1 = self._so_and_confirm_two_units()
so_1.picking_ids.move_ids.quantity_done = 2 so_1.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
so_1.picking_ids.button_validate() so_1.picking_ids.button_validate()
# Return 1 from SO1 # Return 1 from SO1
@@ -1161,7 +1162,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
stock_return_picking_action = stock_return_picking.create_returns() stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id']) return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign() return_pick.action_assign()
return_pick.move_ids.quantity_done = 1 return_pick.move_ids.write({'quantity': 1, 'picked': True})
return_pick._action_done() return_pick._action_done()
# Create, confirm and deliver a sale order for 1@12 (SO2) # Create, confirm and deliver a sale order for 1@12 (SO2)
@@ -1178,7 +1179,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so_2.action_confirm() so_2.action_confirm()
so_2.picking_ids.move_ids.quantity_done = 1 so_2.picking_ids.move_ids.write({'quantity': 1, 'picked': True})
so_2.picking_ids.button_validate() so_2.picking_ids.button_validate()
# Receive 1@20 # Receive 1@20
@@ -1192,7 +1193,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 20, 'price_unit': 20,
}) })
in_move_2._action_confirm() in_move_2._action_confirm()
in_move_2.quantity_done = 1 in_move_2.write({'quantity': 1, 'picked': True})
in_move_2._action_done() in_move_2._action_done()
# Re-deliver returned 1 from SO1 # Re-deliver returned 1 from SO1
@@ -1205,7 +1206,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
stock_redeliver_picking_action = stock_redeliver_picking.create_returns() stock_redeliver_picking_action = stock_redeliver_picking.create_returns()
redeliver_pick = self.env['stock.picking'].browse(stock_redeliver_picking_action['res_id']) redeliver_pick = self.env['stock.picking'].browse(stock_redeliver_picking_action['res_id'])
redeliver_pick.action_assign() redeliver_pick.action_assign()
redeliver_pick.move_ids.quantity_done = 1 redeliver_pick.move_ids.write({'quantity': 1, 'picked': True})
redeliver_pick._action_done() redeliver_pick._action_done()
# Invoice the sale orders # Invoice the sale orders
@@ -1274,7 +1275,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so_1.action_confirm() so_1.action_confirm()
so_1.picking_ids.move_ids.quantity_done = 12 so_1.picking_ids.move_ids.write({'quantity': 12, 'picked': True})
so_1.picking_ids.button_validate() so_1.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -1317,7 +1318,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 1.0, 'price_unit': 1.0,
}) })
in_move_1._action_confirm() in_move_1._action_confirm()
in_move_1.quantity_done = quantity in_move_1.write({'quantity': quantity, 'picked': True})
in_move_1._action_done() in_move_1._action_done()
# Create, confirm and deliver a sale order for 12@1.5 with reception (50 * 1.0, 50 * 0.0)(SO2) # Create, confirm and deliver a sale order for 12@1.5 with reception (50 * 1.0, 50 * 0.0)(SO2)
@@ -1334,7 +1335,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})], })],
}) })
so_2.action_confirm() so_2.action_confirm()
so_2.picking_ids.move_ids.quantity_done = 12 so_2.picking_ids.move_ids.write({'quantity': 12, 'picked': True})
so_2.picking_ids.button_validate() so_2.picking_ids.button_validate()
# Invoice the sale order. # Invoice the sale order.
@@ -1384,7 +1385,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 20, 60]]) } for p in [10, 20, 60]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Sell 3 units # Sell 3 units
@@ -1407,7 +1408,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids picking = so.picking_ids
while picking: while picking:
pickings.append(picking) pickings.append(picking)
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1428,7 +1429,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100, 'price_unit': 100,
}) })
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Return the second picking (i.e. 1@20) # Return the second picking (i.e. 1@20)
@@ -1436,7 +1437,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save() return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
return_picking_id, dummy = return_wizard._create_returns() return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.write({'quantity': 1, 'picked': True})
return_picking.button_validate() return_picking.button_validate()
# Add a credit note for the returned product # Add a credit note for the returned product
@@ -1478,7 +1479,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 20, 60]]) } for p in [10, 20, 60]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Sell 3 units # Sell 3 units
@@ -1501,7 +1502,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids picking = so.picking_ids
while picking: while picking:
pickings.append(picking) pickings.append(picking)
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1522,7 +1523,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100, 'price_unit': 100,
}) })
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
# Return the second picking (i.e. 1@20) # Return the second picking (i.e. 1@20)
@@ -1530,7 +1531,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save() return_wizard = Form(self.env['stock.return.picking'].with_context(ctx)).save()
return_picking_id, dummy = return_wizard._create_returns() return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id) return_picking = self.env['stock.picking'].browse(return_picking_id)
return_picking.move_ids.quantity_done = 1 return_picking.move_ids.write({'quantity': 1, 'picked': True})
return_picking.button_validate() return_picking.button_validate()
# Create a new invoice for the returned product # Create a new invoice for the returned product
@@ -1567,7 +1568,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in svl_values]) } for p in svl_values])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
so = self.env['sale.order'].create({ so = self.env['sale.order'].create({
@@ -1589,7 +1590,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
invoices = self.env['account.move'] invoices = self.env['account.move']
picking = so.picking_ids picking = so.picking_ids
while picking: while picking:
picking.move_ids.quantity_done = 1 picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate() action = picking.button_validate()
if isinstance(action, dict): if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save() wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1657,7 +1658,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p, 'price_unit': p,
} for p in [10, 50]]) } for p in [10, 50]])
in_moves._action_confirm() in_moves._action_confirm()
in_moves.quantity_done = 1 in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done() in_moves._action_done()
so = self.env['sale.order'].create({ so = self.env['sale.order'].create({
@@ -1675,7 +1676,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
so.action_confirm() so.action_confirm()
picking = so.picking_ids picking = so.picking_ids
picking.move_ids.quantity_done = 1.0 picking.move_ids.write({'quantity': 1.0, 'picked': True})
picking.button_validate() picking.button_validate()
invoice01 = so._create_invoices() invoice01 = so._create_invoices()
@@ -1714,7 +1715,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.assertEqual(sale_order.order_line.product_uom_qty, 2.0) self.assertEqual(sale_order.order_line.product_uom_qty, 2.0)
# Deliver one. # Deliver one.
sale_order.picking_ids.move_ids.quantity_done = 2 sale_order.picking_ids.move_ids.write({'quantity': 2, 'picked': True})
sale_order.picking_ids.button_validate() sale_order.picking_ids.button_validate()
svl_am = sale_order.order_line.move_ids.stock_valuation_layer_ids.account_move_id svl_am = sale_order.order_line.move_ids.stock_valuation_layer_ids.account_move_id
svl_am.button_draft() svl_am.button_draft()

Some files were not shown because too many files have changed in this diff Show More