[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>
<t t-set="total_value" t-value="0"/>
<t t-foreach="o.move_ids" t-as="move">
<t t-if="move.quantity_done">
<t t-if="move.quantity">
<tr>
<td>
<span t-field="move.product_id"/>
</td>
<td>
<span t-field="move.quantity_done"/>
<span t-field="move.quantity"/>
<span t-field="move.product_uom" groups="uom.group_uom"/>
</td>
<td>
<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-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>
<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"/>
+5 -5
View File
@@ -72,7 +72,7 @@ class TestDDT(TestSaleCommon):
# deliver partially
pick = self.so.picking_ids
pick.move_ids.write({'quantity_done': 1})
pick.move_ids.write({'quantity': 1, 'picked': True})
wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
@@ -84,14 +84,14 @@ class TestDDT(TestSaleCommon):
# deliver partially
pickx1 = self.so.picking_ids.filtered(lambda p: p.state != 'done')
pickx1.move_ids.write({'quantity_done': 1})
pickx1.move_ids.write({'quantity': 1, 'picked': True})
wiz_act = pickx1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
# and again
pickx2 = self.so.picking_ids.filtered(lambda p: p.state != 'done')
pickx2.move_ids.write({'quantity_done': 2})
pickx2.move_ids.write({'quantity': 2, 'picked': True})
wiz_act = pickx2.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
@@ -129,7 +129,7 @@ class TestDDT(TestSaleCommon):
# deliver partially
picking_1 = so.picking_ids
picking_1.move_ids.write({'quantity_done': 1})
picking_1.move_ids.write({'quantity': 1, 'picked': True})
wiz_act = picking_1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
@@ -139,7 +139,7 @@ class TestDDT(TestSaleCommon):
invoice_1.action_post()
picking_2 = so.picking_ids.filtered(lambda p: p.state != 'done')
picking_2.move_ids.write({'quantity_done': 2})
picking_2.move_ids.write({'quantity': 2, 'picked': True})
picking_2.button_validate()
invoice_2 = so._create_invoices()
+1 -1
View File
@@ -143,7 +143,7 @@ class TestItEdiDDT(TestItEdi):
def _create_delivery(self, sale_order, qty=1):
""" Create a picking of a limited quantity and create a backorder """
pickings = sale_order.picking_ids.filtered(lambda picking: picking.state != 'done')
pickings.move_ids.write({'quantity_done': qty})
pickings.move_ids.write({'quantity': qty})
wizard_action = pickings.button_validate()
context = wizard_action['context']
wizard = Form(self.env['stock.backorder.confirmation'].with_context(context))
+1 -1
View File
@@ -718,7 +718,7 @@
<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 eval="{'quantity_done': 5}"/>
<value eval="{'quantity': 5, 'picked': True}"/>
</function>
<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')
@api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
'workorder_ids.state', 'product_qty', 'qty_producing')
'move_raw_ids.state', 'move_raw_ids.quantity', 'move_finished_ids.state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'move_raw_ids.picked')
def _compute_state(self):
""" Compute the production state. This uses a similar process to stock
picking, but has been adapted to support having no moves. This adaption
@@ -564,7 +564,7 @@ class MrpProduction(models.Model):
production.state = 'progress'
elif production.product_uom_id and not float_is_zero(production.qty_producing, precision_rounding=production.product_uom_id.rounding):
production.state = 'progress'
elif any(not float_is_zero(move.quantity_done, precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding) for move in production.move_raw_ids if move.product_id):
elif any(production.move_raw_ids.mapped('picked')):
production.state = 'progress'
@api.depends('bom_id', 'product_id', 'product_qty', 'product_uom_id')
@@ -633,16 +633,16 @@ class MrpProduction(models.Model):
def _compute_unreserve_visible(self):
for order in self:
already_reserved = order.state not in ('done', 'cancel') and order.mapped('move_raw_ids.move_line_ids')
any_quantity_done = any(m.quantity_done > 0 for m in order.move_raw_ids)
any_quantity_done = any(order.move_raw_ids.mapped('picked'))
order.unreserve_visible = not any_quantity_done and already_reserved
order.reserve_visible = order.state in ('confirmed', 'progress', 'to_close') and any(move.product_uom_qty and move.state in ['confirmed', 'partially_available'] for move in order.move_raw_ids)
@api.depends('workorder_ids.state', 'move_finished_ids', 'move_finished_ids.quantity_done')
@api.depends('workorder_ids.state', 'move_finished_ids', 'move_finished_ids.quantity')
def _get_produced_qty(self):
for production in self:
done_moves = production.move_finished_ids.filtered(lambda x: x.state != 'cancel' and x.product_id.id == production.product_id.id)
qty_produced = sum(done_moves.mapped('quantity_done'))
qty_produced = sum(done_moves.filtered(lambda m: m.picked).mapped('quantity'))
production.qty_produced = qty_produced
return True
@@ -890,7 +890,7 @@ class MrpProduction(models.Model):
if 'lot_producing_id' in vals:
finished_move_lines.write({'lot_id': vals.get('lot_producing_id')})
if 'qty_producing' in vals:
finished_move_lines.write({'qty_done': vals.get('qty_producing')})
finished_move_lines.write({'quantity': vals.get('qty_producing')})
if self._has_workorders() and not production.workorder_ids.operation_id and vals.get('date_start') and not vals.get('date_finished'):
new_date_start = fields.Datetime.to_datetime(vals.get('date_start'))
if not production.date_finished or new_date_start >= production.date_finished:
@@ -1030,7 +1030,7 @@ class MrpProduction(models.Model):
target_uom = (move.bom_line_id or move.byproduct_id).product_uom_id or move.product_uom
# In order to be able to multiply the move quantity by the ratio, we
# have to be sure they both express in the same UoM.
qty = move.quantity_done or move.product_uom_qty
qty = move.quantity or move.product_uom_qty
qty = move.product_uom._compute_quantity(qty * ratio, target_uom)
return (target_uom, qty)
@@ -1195,20 +1195,18 @@ class MrpProduction(models.Model):
qty_producing_uom = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP')
if qty_producing_uom != 1:
self.qty_producing = self.product_id.uom_id._compute_quantity(1, self.product_uom_id, rounding_method='HALF-UP')
for move in (self.move_raw_ids | self.move_finished_ids.filtered(lambda m: m.product_id != self.product_id)):
if move._should_bypass_set_qty_producing() or not move.product_uom:
# picked + manual means the user set the quantity manually
if move.manual_consumption and move.picked:
continue
if move._should_bypass_set_qty_producing():
continue
new_qty = float_round((self.qty_producing - self.qty_produced) * move.unit_factor, precision_rounding=move.product_uom.rounding)
if self.use_auto_consume_components_lots and move.has_tracking in ('lot', 'serial'):
if float_compare(move.reserved_availability, 0, precision_rounding=move.product_uom.rounding) <= 0:
continue
else:
new_qty = min(new_qty, move.reserved_availability)
move.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0
move._set_quantity_done(new_qty)
if (not move.manual_consumption and not move._origin.manual_consumption):
move.picked = True
def _update_raw_moves(self, factor):
self.ensure_one()
@@ -1518,17 +1516,17 @@ class MrpProduction(models.Model):
done_qty_by_product = defaultdict(float)
for move in order.move_raw_ids:
qty_done = move.product_uom._compute_quantity(move.quantity_done, move.product_id.uom_id)
quantity = move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id)
rounding = move.product_id.uom_id.rounding
if not (move.product_id in expected_qty_by_product or float_is_zero(qty_done, precision_rounding=rounding)):
issues.append((order, move.product_id, qty_done, 0.0))
if not (move.product_id in expected_qty_by_product or float_is_zero(quantity, precision_rounding=rounding)) or not move.picked:
issues.append((order, move.product_id, quantity, 0.0))
continue
done_qty_by_product[move.product_id] += qty_done
done_qty_by_product[move.product_id] += quantity
for product, qty_to_consume in expected_qty_by_product.items():
qty_done = done_qty_by_product.get(product, 0.0)
if float_compare(qty_to_consume, qty_done, precision_rounding=product.uom_id.rounding) != 0:
issues.append((order, product, qty_done, qty_to_consume))
quantity = done_qty_by_product.get(product, 0.0)
if float_compare(qty_to_consume, quantity, precision_rounding=product.uom_id.rounding) != 0:
issues.append((order, product, quantity, qty_to_consume))
return issues
@@ -1632,15 +1630,17 @@ class MrpProduction(models.Model):
return True
def _post_inventory(self, cancel_backorder=False):
moves_to_do, moves_not_to_do = set(), set()
moves_to_do, moves_not_to_do, moves_to_cancel = set(), set(), set()
for move in self.move_raw_ids:
if move.state == 'done':
moves_not_to_do.add(move.id)
elif not move.picked:
moves_to_cancel.add(move.id)
elif move.state != 'cancel':
moves_to_do.add(move.id)
if move.product_qty == 0.0 and move.quantity_done > 0:
move.product_uom_qty = move.quantity_done
self.with_context(skip_mo_check=True).env['stock.move'].browse(moves_to_do)._action_done(cancel_backorder=cancel_backorder)
self.with_context(skip_mo_check=True).env['stock.move'].browse(moves_to_cancel)._action_cancel()
moves_to_do = self.move_raw_ids.filtered(lambda x: x.state == 'done') - self.env['stock.move'].browse(moves_not_to_do)
# Create a dict to avoid calling filtered inside for loops.
moves_to_do_by_order = defaultdict(lambda: self.env['stock.move'], [
@@ -1651,8 +1651,7 @@ class MrpProduction(models.Model):
finish_moves = order.move_finished_ids.filtered(lambda m: m.product_id == order.product_id and m.state not in ('done', 'cancel'))
# the finish move can already be completed by the workorder.
for move in finish_moves:
if not move.quantity_done:
move._set_quantity_done(float_round(order.qty_producing - order.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP'))
move.quantity = float_round(order.qty_producing - order.qty_produced, precision_rounding=order.product_uom_id.rounding, rounding_method='HALF-UP')
if move.has_tracking != 'none' and order.lot_producing_id:
move.move_line_ids.lot_id = order.lot_producing_id
# workorder duration need to be set to calculate the price of the product
@@ -1660,12 +1659,12 @@ class MrpProduction(models.Model):
if workorder.state not in ('done', 'cancel'):
workorder.duration_expected = workorder._get_duration_expected()
if workorder.duration == 0.0:
workorder.duration = workorder.duration_expected * order.qty_produced/order.product_qty
workorder.duration = workorder.duration_expected * order.qty_producing / order.product_qty
workorder.duration_unit = round(workorder.duration / max(workorder.qty_produced, 1), 2)
order._cal_price(moves_to_do_by_order[order.id])
moves_to_finish = self.move_finished_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
moves_to_finish.picked = True
moves_to_finish = moves_to_finish._action_done(cancel_backorder=cancel_backorder)
self.action_assign()
for order in self:
consume_move_lines = moves_to_do_by_order[order.id].mapped('move_line_ids')
order.move_finished_ids.move_line_ids.consume_line_ids = [(6, 0, consume_move_lines.ids)]
@@ -1707,7 +1706,7 @@ class MrpProduction(models.Model):
and a new backorder with product_qty=2.
:param bool cancel_remaining_qty: whether to cancel remaining quantities or generate
an additional backorder, e.g. having product_qty=5 if mrp.production(1,) product_qty was 10.
:param bool set_consumed_qty: whether to set qty_done on move lines to the reserved quantity
:param bool set_consumed_qty: whether to set quantity on move lines to the reserved quantity
or the initial demand if no reservation, except for the remaining backorder.
:return: mrp.production records in order of [orig_prod_1, backorder_prod_1,
backorder_prod_2, orig_prod_2, backorder_prod_2, etc.]
@@ -1796,6 +1795,7 @@ class MrpProduction(models.Model):
moves.append(move)
backorder_moves = self.env['stock.move'].create(new_moves_vals)
move_to_assign = backorder_moves
# Split `stock.move.line`s. 2 options for this:
# - do_unreserve -> action_assign
# - Split the reserved amounts manually
@@ -1810,7 +1810,6 @@ class MrpProduction(models.Model):
assigned_moves = set()
partially_assigned_moves = set()
move_lines_to_unlink = set()
for initial_move, backorder_moves in move_to_backorder_moves.items():
# Create `stock.move.line` for consumed but non-reserved components
if initial_move.raw_material_production_id and not initial_move.move_line_ids and set_consumed_qty:
@@ -1819,7 +1818,7 @@ class MrpProduction(models.Model):
for move in list(initial_move + backorder_moves - backorder_move_to_ignore):
new_ml_vals = dict(
ml_vals,
qty_done=move.product_uom_qty,
quantity=move.product_uom_qty,
move_id=move.id
)
move_lines_vals.append(new_ml_vals)
@@ -1828,23 +1827,27 @@ class MrpProduction(models.Model):
ml_by_move = []
product_uom = initial_move.product_id.uom_id
for move_line in initial_move.move_line_ids:
available_qty = move_line.product_uom_id._compute_quantity(move_line.reserved_uom_qty, product_uom)
if initial_move.picked:
continue
available_qty = move_line.product_uom_id._compute_quantity(move_line.quantity, product_uom)
if float_compare(available_qty, 0, precision_rounding=move_line.product_uom_id.rounding) <= 0:
continue
ml_by_move.append((available_qty, move_line, move_line.copy_data()[0]))
initial_move.move_line_ids.with_context(bypass_reservation_update=True).write({'reserved_uom_qty': 0})
moves = list(initial_move | backorder_moves)
move = moves and moves.pop(0)
move_qty_to_reserve = move.product_qty
for index, (quantity, move_line, ml_vals) in enumerate(ml_by_move):
taken_qty = min(quantity, move_qty_to_reserve, move_line.product_uom_id._compute_quantity(move_line.qty_done, product_uom))
taken_qty = min(quantity, move_qty_to_reserve)
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id)
if float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
continue
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty = taken_qty_uom
move_line.write({
'quantity': taken_qty_uom,
'move_id': move.id,
})
move_qty_to_reserve -= taken_qty
ml_by_move[index] = (quantity - taken_qty, move_line, ml_vals)
@@ -1859,17 +1862,13 @@ class MrpProduction(models.Model):
taken_qty = min(move_qty_to_reserve, quantity)
taken_qty_uom = product_uom._compute_quantity(taken_qty, move_line.product_uom_id)
if move == initial_move:
move_line.with_context(bypass_reservation_update=True).reserved_uom_qty += taken_qty_uom
if set_consumed_qty:
move_line.qty_done += taken_qty_uom
move_line.quantity += taken_qty_uom
elif not float_is_zero(taken_qty_uom, precision_rounding=move_line.product_uom_id.rounding):
new_ml_vals = dict(
ml_vals,
reserved_uom_qty=taken_qty_uom,
quantity=taken_qty_uom,
move_id=move.id
)
if set_consumed_qty:
new_ml_vals['qty_done'] = taken_qty_uom
move_lines_vals.append(new_ml_vals)
quantity -= taken_qty
move_qty_to_reserve -= taken_qty
@@ -1891,15 +1890,22 @@ class MrpProduction(models.Model):
if move and move_qty_to_reserve != move.product_qty:
partially_assigned_moves.add(move.id)
move_lines_to_unlink.update(initial_move.move_line_ids.filtered(
lambda ml: not ml.reserved_uom_qty and not ml.qty_done).ids)
move_lines_to_unlink.update(initial_move.move_line_ids.filtered(lambda ml: not ml.quantity).ids)
# reserve new backorder moves depending on the picking type
self.env['stock.move'].browse(assigned_moves).write({'state': 'assigned'})
self.env['stock.move'].browse(partially_assigned_moves).write({'state': 'partially_available'})
move_to_assign = move_to_assign.filtered(
lambda move: move.state in ('confirmed', 'partially_available')
and (move._should_bypass_reservation()
or move.picking_type_id.reservation_method == 'at_confirm'
or (move.reservation_date and move.reservation_date <= fields.Date.today())))
move_to_assign._action_assign()
# Avoid triggering a useless _recompute_state
self.env['stock.move.line'].browse(move_lines_to_unlink).write({'move_id': False})
self.env['stock.move.line'].browse(move_lines_to_unlink).unlink()
self.env['stock.move.line'].with_context(bypass_reservation_update=True).create(move_lines_vals)
self.env['stock.move.line'].create(move_lines_vals)
workorders_to_cancel = self.env['mrp.workorder']
for production in self:
@@ -1993,7 +1999,7 @@ class MrpProduction(models.Model):
}
elif self.user_has_groups('mrp.group_mrp_reception_report'):
mos_to_show = self.filtered(lambda mo: mo.picking_type_id.auto_show_reception_report)
lines = mos_to_show.move_finished_ids.filtered(lambda m: m.product_id.type == 'product' and m.state != 'cancel' and m.quantity_done and not m.move_dest_ids)
lines = mos_to_show.move_finished_ids.filtered(lambda m: m.product_id.type == 'product' and m.state != 'cancel' and m.picked and not m.move_dest_ids)
if lines:
if any(mo.show_allocation for mo in mos_to_show):
another_action = mos_to_show.action_view_reception_report()
@@ -2187,7 +2193,7 @@ class MrpProduction(models.Model):
if self.product_id.tracking != 'serial':
return
if self.state == 'confirmed' and self.reservation_state != 'assigned':
missing_components = {move.product_id for move in self.move_raw_ids if float_compare(move.reserved_availability, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0}
missing_components = {move.product_id for move in self.move_raw_ids if float_compare(move.quantity, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0}
message = _("Make sure enough quantities of these components are reserved to do the production:\n")
message += "\n".join(component.name for component in missing_components)
raise UserError(message)
@@ -2474,17 +2480,17 @@ class MrpProduction(models.Model):
if move.has_tracking != 'serial' or move.product_id == self.product_id:
continue
for move_line in move.move_line_ids:
if float_is_zero(move_line.qty_done, precision_rounding=move_line.product_uom_id.rounding):
if float_is_zero(move_line.quantity, precision_rounding=move_line.product_uom_id.rounding):
continue
if self._is_finished_sn_already_produced(move_line.lot_id, excluded_sml=move_line):
raise UserError(_('The serial number %(number)s used for byproduct %(product_name)s has already been produced',
number=move_line.lot_id.name, product_name=move_line.product_id.name))
for move in self.move_raw_ids:
if move.has_tracking != 'serial':
if move.has_tracking != 'serial' or not move.picked:
continue
for move_line in move.move_line_ids:
if float_is_zero(move_line.qty_done, precision_rounding=move_line.product_uom_id.rounding):
if float_is_zero(move_line.quantity, precision_rounding=move_line.product_uom_id.rounding):
continue
message = _('The serial number %(number)s used for component %(component)s has already been consumed',
number=move_line.lot_id.name,
@@ -2494,7 +2500,7 @@ class MrpProduction(models.Model):
# Check presence of same sn in previous productions
duplicates = self.env['stock.move.line'].search_count([
('lot_id', '=', move_line.lot_id.id),
('qty_done', '=', 1),
('quantity', '=', 1),
('state', '=', 'done'),
('location_dest_id.usage', '=', 'production'),
('production_id', '!=', False),
@@ -2517,7 +2523,7 @@ class MrpProduction(models.Model):
if not ((duplicates_returned or removed) and duplicates - duplicates_returned - removed + unremoved == 0):
raise UserError(message)
# Check presence of same sn in current production
duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == move_line.lot_id) - move_line
duplicates = co_prod_move_lines.filtered(lambda ml: ml.quantity and ml.lot_id == move_line.lot_id) - move_line
if duplicates:
raise UserError(message)
@@ -2527,7 +2533,7 @@ class MrpProduction(models.Model):
excluded_sml = excluded_sml or self.env['stock.move.line']
domain = [
('lot_id', '=', lot.id),
('qty_done', '=', 1),
('quantity', '=', 1),
('state', '=', 'done')
]
co_prod_move_lines = self.move_finished_ids.move_line_ids - excluded_sml
@@ -2554,7 +2560,7 @@ class MrpProduction(models.Model):
if not ((duplicates_unbuild or removed) and duplicates - duplicates_unbuild - removed == 0):
return True
# Check presence of same sn in current production
duplicates = co_prod_move_lines.filtered(lambda ml: ml.qty_done and ml.lot_id == lot)
duplicates = co_prod_move_lines.filtered(lambda ml: ml.quantity and ml.lot_id == lot)
return bool(duplicates)
def _pre_action_split_merge_hook(self, merge=False, split=False):
@@ -2612,10 +2618,10 @@ class MrpProduction(models.Model):
continue
rounding = move.product_uom.rounding
if move.manual_consumption:
if move.has_tracking in ('serial', 'lot') and float_is_zero(move.quantity_done, precision_rounding=rounding):
if move.has_tracking in ('serial', 'lot') and (any(not line.lot_id for line in move.move_line_ids if line.quantity) or not move.picked):
missing_lot_id_products += "\n - %s" % move.product_id.display_name
if missing_lot_id_products:
error_msg = _('You need to supply Lot/Serial Number for products:') + missing_lot_id_products
error_msg = _("You need to supply Lot/Serial Number for products and 'picked' them:") + missing_lot_id_products
raise UserError(error_msg)
def _get_autoprint_done_report_actions(self):
+26 -26
View File
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from collections import defaultdict
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.tools import float_compare, float_round
from odoo.osv import expression
from odoo.tools.misc import clean_context
from collections import defaultdict
class MrpUnbuild(models.Model):
_name = "mrp.unbuild"
@@ -131,17 +131,19 @@ class MrpUnbuild(models.Model):
def action_unbuild(self):
self.ensure_one()
self._check_company()
# remove the default_* keys that was only needed in the unbuild wizard
self.env.context = dict(clean_context(self.env.context))
if self.product_id.tracking != 'none' and not self.lot_id.id:
raise UserError(_('You should provide a lot number for the final product.'))
if self.mo_id:
if self.mo_id.state != 'done':
raise UserError(_('You cannot unbuild a undone manufacturing order.'))
if self.mo_id and self.mo_id.state != 'done':
raise UserError(_('You cannot unbuild a undone manufacturing order.'))
consume_moves = self._generate_consume_moves()
consume_moves._action_confirm()
produce_moves = self._generate_produce_moves()
produce_moves.with_context(default_lot_id=False)._action_confirm()
produce_moves._action_confirm()
produce_moves.quantity = 0
finished_moves = consume_moves.filtered(lambda m: m.product_id == self.product_id)
consume_moves -= finished_moves
@@ -153,18 +155,15 @@ class MrpUnbuild(models.Model):
raise UserError(_('Some of your byproducts are tracked, you have to specify a manufacturing order in order to retrieve the correct byproducts.'))
for finished_move in finished_moves:
if finished_move.has_tracking != 'none':
self.env['stock.move.line'].create({
'move_id': finished_move.id,
'lot_id': self.lot_id.id,
'qty_done': finished_move.product_uom_qty,
'product_id': finished_move.product_id.id,
'product_uom_id': finished_move.product_uom.id,
'location_id': finished_move.location_id.id,
'location_dest_id': finished_move.location_dest_id.id,
})
else:
finished_move.quantity_done = finished_move.product_uom_qty
self.env['stock.move.line'].create({
'move_id': finished_move.id,
'lot_id': self.lot_id.id,
'quantity': finished_move.product_uom_qty,
'product_id': finished_move.product_id.id,
'product_uom_id': finished_move.product_uom.id,
'location_id': finished_move.location_id.id,
'location_dest_id': finished_move.location_dest_id.id,
})
# TODO: Will fail if user do more than one unbuild with lot on the same MO. Need to check what other unbuild has aready took
qty_already_used = defaultdict(float)
@@ -178,12 +177,12 @@ class MrpUnbuild(models.Model):
moves_lines = moves_lines.filtered(lambda ml: self.lot_id in ml.produce_line_ids.lot_id) # FIXME sle: double check with arm
for move_line in moves_lines:
# Iterate over all move_lines until we unbuilded the correct quantity.
taken_quantity = min(needed_quantity, move_line.qty_done - qty_already_used[move_line])
taken_quantity = min(needed_quantity, move_line.quantity - qty_already_used[move_line])
if taken_quantity:
self.env['stock.move.line'].create({
'move_id': move.id,
'lot_id': move_line.lot_id.id,
'qty_done': taken_quantity,
'quantity': taken_quantity,
'product_id': move.product_id.id,
'product_uom_id': move_line.product_uom_id.id,
'location_id': move.location_id.id,
@@ -192,12 +191,13 @@ class MrpUnbuild(models.Model):
needed_quantity -= taken_quantity
qty_already_used[move_line] += taken_quantity
else:
move.quantity_done = float_round(move.product_uom_qty, precision_rounding=move.product_uom.rounding)
move.quantity = float_round(move.product_uom_qty, precision_rounding=move.product_uom.rounding)
(finished_moves | consume_moves | produce_moves).picked = True
finished_moves._action_done()
consume_moves._action_done()
produce_moves._action_done()
produced_move_line_ids = produce_moves.mapped('move_line_ids').filtered(lambda ml: ml.qty_done > 0)
produced_move_line_ids = produce_moves.mapped('move_line_ids').filtered(lambda ml: ml.quantity > 0)
consume_moves.mapped('move_line_ids').write({'produce_line_ids': [(6, 0, produced_move_line_ids.ids)]})
if self.mo_id:
unbuild_msg = _("%(qty)s %(measure)s unbuilt in %(order)s",
@@ -300,7 +300,7 @@ class MrpUnbuild(models.Model):
'default_location_id': self.location_id.id,
'default_unbuild_id': self.id,
'default_quantity': unbuild_qty,
'default_product_uom_name': self.product_id.uom_name
'default_product_uom_name': self.product_id.uom_name,
},
'target': 'new'
'target': 'new',
}
+4 -7
View File
@@ -830,8 +830,8 @@ class MrpWorkorder(models.Model):
if move_line:
if self.product_id.tracking == 'serial':
raise UserError(_('You cannot produce the same serial number twice.'))
move_line.reserved_uom_qty += self.qty_producing
move_line.qty_done += self.qty_producing
move_line.picked = True
move_line.quantity += self.qty_producing
else:
quantity = self.product_uom_id._compute_quantity(self.qty_producing, self.product_id.uom_id, rounding_method='HALF-UP')
putaway_location = production_move.location_dest_id._get_putaway_strategy(self.product_id, quantity)
@@ -839,17 +839,14 @@ class MrpWorkorder(models.Model):
'move_id': production_move.id,
'product_id': production_move.product_id.id,
'lot_id': self.finished_lot_id.id,
'reserved_uom_qty': self.qty_producing,
'product_uom_id': self.product_uom_id.id,
'qty_done': self.qty_producing,
'quantity': self.qty_producing,
'location_id': production_move.location_id.id,
'location_dest_id': putaway_location.id,
})
else:
rounding = production_move.product_uom.rounding
production_move._set_quantity_done(
float_round(self.qty_producing, precision_rounding=rounding)
)
production_move.quantity = float_round(self.qty_producing, precision_rounding=rounding)
def _should_start_timer(self):
return True
+1 -1
View File
@@ -314,7 +314,7 @@ class ProductProduct(models.Model):
def _count_returned_sn_products(self, sn_lot):
res = self.env['stock.move.line'].search_count([
('lot_id', '=', sn_lot.id),
('qty_done', '=', 1),
('quantity', '=', 1),
('state', '=', 'done'),
('production_id', '=', False),
('location_id.usage', '=', 'production'),
+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))
return lines
def _reservation_is_updatable(self, quantity, reserved_quant):
self.ensure_one()
if self.produce_line_ids.lot_id:
ml_remaining_qty = self.qty_done - self.reserved_uom_qty
ml_remaining_qty = self.product_uom_id._compute_quantity(ml_remaining_qty, self.product_id.uom_id, rounding_method="HALF-UP")
if float_compare(ml_remaining_qty, quantity, precision_rounding=self.product_id.uom_id.rounding) < 0:
return False
return super(StockMoveLine, self)._reservation_is_updatable(quantity, reserved_quant)
def write(self, vals):
for move_line in self:
production = move_line.move_id.production_id or move_line.move_id.raw_material_production_id
if production and move_line.state == 'done' and any(field in vals for field in ('lot_id', 'location_id', 'qty_done')):
if production and move_line.state == 'done' and any(field in vals for field in ('lot_id', 'location_id', 'quantity')):
move_line._log_message(production, move_line, 'mrp.track_production_move_template', vals)
return super(StockMoveLine, self).write(vals)
@@ -229,7 +220,8 @@ class StockMove(models.Model):
def _onchange_product_uom_qty(self):
if self.raw_material_production_id and self.has_tracking == 'none':
mo = self.raw_material_production_id
self._update_quantity_done(mo)
new_qty = float_round((mo.qty_producing - mo.qty_produced) * self.unit_factor, precision_rounding=self.product_uom.rounding)
self.quantity = new_qty
@api.model
def default_get(self, fields_list):
@@ -362,12 +354,12 @@ class StockMove(models.Model):
moves_ids_to_return.add(move.id)
continue
if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding):
factor = move.product_uom._compute_quantity(move.quantity_done, bom.product_uom_id) / bom.product_qty
factor = move.product_uom._compute_quantity(move.quantity, bom.product_uom_id) / bom.product_qty
else:
factor = move.product_uom._compute_quantity(move.product_uom_qty, bom.product_uom_id) / bom.product_qty
boms, lines = bom.sudo().explode(move.product_id, factor, picking_type=bom.picking_type_id)
for bom_line, line_data in lines:
if move.picking_id.immediate_transfer or float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding) or self.env.context.get('is_scrap'):
if float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding) or self.env.context.get('is_scrap'):
phantom_moves_vals_list += move._generate_move_phantom(bom_line, 0, line_data['qty'])
else:
phantom_moves_vals_list += move._generate_move_phantom(bom_line, line_data['qty'], 0)
@@ -375,7 +367,7 @@ class StockMove(models.Model):
moves_ids_to_unlink.add(move.id)
move_to_unlink = self.env['stock.move'].browse(moves_ids_to_unlink).sudo()
move_to_unlink.quantity_done = 0
move_to_unlink.quantity = 0
move_to_unlink._action_cancel()
move_to_unlink.unlink()
if phantom_moves_vals_list:
@@ -423,9 +415,9 @@ class StockMove(models.Model):
'product_id': bom_line.product_id.id,
'product_uom': bom_line.product_uom_id.id,
'product_uom_qty': product_qty,
'quantity_done': quantity_done,
'state': 'draft', # will be confirmed below
'quantity': quantity_done,
'name': self.name,
'picked': self.picked,
'bom_line_id': bom_line.id,
}
@@ -478,8 +470,6 @@ class StockMove(models.Model):
# Do not update extra product quantities
if float_is_zero(self.product_uom_qty, precision_rounding=self.product_uom.rounding):
return True
if (not self.raw_material_production_id.use_auto_consume_components_lots and self.has_tracking != 'none') or self.manual_consumption or self._origin.manual_consumption:
return True
return False
def _key_assign_picking(self):
@@ -542,15 +532,6 @@ class StockMove(models.Model):
else:
return 0.0
def _update_quantity_done(self, mo):
self.ensure_one()
new_qty = float_round((mo.qty_producing - mo.qty_produced) * self.unit_factor, precision_rounding=self.product_uom.rounding)
if not self.is_quantity_done_editable:
self.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0
self.move_line_ids = self._set_quantity_done_prepare_vals(new_qty)
else:
self.quantity_done = new_qty
def _update_candidate_moves_list(self, candidate_moves_set):
super()._update_candidate_moves_list(candidate_moves_set)
for production in self.mapped('raw_material_production_id'):
@@ -586,3 +567,11 @@ class StockMove(models.Model):
def _determine_is_manual_consumption(self, product, production, bom_line):
return (product.product_tmpl_id.tracking != 'none' and not production.use_auto_consume_components_lots) or \
(product.product_tmpl_id.tracking == 'none' and bom_line and bom_line.manual_consumption)
def _get_relevant_state_among_moves(self):
res = super()._get_relevant_state_among_moves()
if res == 'partially_available'\
and self.raw_material_production_id\
and all(float_compare(move.quantity, move.should_consume_qty, precision_rounding=move.product_uom.rounding) == 0 for move in self):
res = 'assigned'
return res
+2 -1
View File
@@ -3,6 +3,7 @@
from odoo import _, api, fields, models
class StockScrap(models.Model):
_inherit = 'stock.scrap'
@@ -55,7 +56,7 @@ class StockScrap(models.Model):
else:
return super()._onchange_serial_number()
@api.depends('move_ids', 'move_ids.move_line_ids.qty_done', 'product_id')
@api.depends('move_ids', 'move_ids.move_line_ids.quantity', 'product_id')
def _compute_scrap_qty(self):
self.scrap_qty = 1
for scrap in self:
@@ -115,7 +115,7 @@
</td>
<td t-attf-class="{{ 'text-end' if not has_product_barcode else '' }}" t-if="o.state in ('progress', 'to_close','done')">
<div>
<span t-field="raw_line.quantity_done"/>
<span t-field="raw_line.quantity"/>
</div>
</td>
<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='nCols' t-value='4'/>
<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-foreach="move_lines" t-as="move_line">
<t t-if="move_line.product_uom_id.category_id == uom_categ_unit">
<!-- 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-else="">
<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><span>Quantity: </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"/>
</div>
</div>
+5 -5
View File
@@ -199,8 +199,8 @@ class ReportMoOverview(models.AbstractModel):
for move in record.move_raw_ids:
if move.product_id.detailed_type != 'product':
continue
components_qty_to_produce[move.product_id] += move.product_uom._compute_quantity(move.product_uom_qty, move.product_id.uom_id)
components_qty_reserved[move.product_id] += move.product_uom._compute_quantity(move.reserved_availability, move.product_id.uom_id)
components_qty_to_produce[move.product_id] += move.product_qty
components_qty_reserved[move.product_id] += move.product_uom._compute_quantity(move.quantity, move.product_id.uom_id)
producible_qty = record.product_qty
for product_id, comp_qty_to_produce in components_qty_to_produce.items():
if float_is_zero(comp_qty_to_produce, precision_rounding=product_id.uom_id.rounding):
@@ -419,7 +419,7 @@ class ReportMoOverview(models.AbstractModel):
def _format_component_move(self, production, move_raw, replenishments, replenish_data, level, index):
currency = (production.company_id or self.env.company).currency_id
product = move_raw.product_id
quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity_done
quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity
replenish_mo_cost, dummy_real_cost = self._compute_cost_sums(replenishments)
replenish_quantity = sum(rep.get('summary', {}).get('quantity', 0.0) for rep in replenishments)
missing_quantity = quantity - replenish_quantity
@@ -495,7 +495,7 @@ class ReportMoOverview(models.AbstractModel):
def _get_replenishment_lines(self, production, move_raw, replenish_data, level, current_index):
product = move_raw.product_id
quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity_done
quantity = move_raw.product_uom_qty if move_raw.state != 'done' else move_raw.quantity
currency = (production.company_id or self.env.company).currency_id
forecast = replenish_data['products'][product.id].get('forecast', [])
current_lines = filter(lambda line: line.get('document_in', False) and line.get('document_out', False)
@@ -848,7 +848,7 @@ class ReportMoOverview(models.AbstractModel):
if move.state not in ('partially_available', 'assigned'):
continue
# count reserved stock in move_raw's uom
reserved = move.product_uom._compute_quantity(move.reserved_availability, move_raw.product_uom)
reserved = move.product_uom._compute_quantity(move.quantity, move_raw.product_uom)
# check if the move reserved qty was counted before (happens if multiple outs share pick/pack)
reserved = min(reserved - move.product_uom._compute_quantity(replenish_data['qty_already_reserved'][move], move_raw.product_uom), move_raw.product_uom_qty)
total_reserved += reserved
@@ -7,7 +7,7 @@
<t t-foreach="production.move_finished_ids" t-as="move">
<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-set="qty" t-value="int(move_line.qty_done)"/>
<t t-set="qty" t-value="int(move_line.quantity)"/>
</t>
<t t-else="">
<t t-set="qty" t-value="1"/>
@@ -7,7 +7,7 @@ import { StockMoveX2ManyField, MovesListRenderer } from "@stock/views/picking_fo
export class MrpProductionComponentsListRenderer extends MovesListRenderer {
getCellClass(column, record) {
let classNames = super.getCellClass(...arguments);
if (column.name == "quantity_done" && !record.data.manual_consumption) {
if (column.name == "quantity" && !record.data.manual_consumption) {
classNames += ' o_non_manual_consumption';
}
return classNames;
@@ -7,15 +7,15 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
test: true,
steps: () => [
{
trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("5.00")',
trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("5.00")',
run: () => {},
},
{
trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last',
trigger: 'div[name=move_raw_ids] td[name="quantity"]:last',
run: 'click',
},
{
trigger: 'div[name="quantity_done"] input',
trigger: 'div[name="quantity"] input',
run: 'text 6.0'
},
{
@@ -31,7 +31,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
trigger: ".o_pager_value:first()",
},
{
trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("6.00")',
trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("6.00")',
run: () => {},
},
{
@@ -56,7 +56,7 @@ registry.category("web_tour.tours").add('test_mrp_manual_consumption', {
trigger: ".o_pager_value:first()",
},
{
trigger: 'div[name=move_raw_ids] td[name="quantity_done"]:last:contains("2.00")',
trigger: 'div[name=move_raw_ids] td[name="quantity"]:last:contains("2.00")',
run: () => {},
},
{
+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_qty, 3)
self.assertEqual(sum(mo_backorder.move_raw_ids.filtered(lambda m: m.product_id.id == product_to_use_1.id).mapped("product_uom_qty")), 9)
self.assertEqual(mo_backorder.reserve_visible, False) # the reservation of the first MO should've been moved here
self.assertEqual(mo_backorder.reserve_visible, False) # the reservation is retrigger depending on the picking type
def test_no_tracking_pbm_1(self):
"""Create a MO for 4 product. Produce 1. The backorder button should
@@ -195,18 +195,18 @@ class TestMrpProductionBackorder(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, nb_product_todo*4, lot_id=lot_1)
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, nb_product_todo, lot_id=lot_2)
production.action_assign()
active_production = production
for i in range(nb_product_todo):
active_production.action_assign()
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4
ml.quantity = 4
ml.lot_id = lot_1
details_operation_form.save()
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
ml.lot_id = lot_2
details_operation_form.save()
@@ -215,6 +215,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
production_form.lot_producing_id = lot_final
active_production = production_form.save()
active_production.move_raw_ids.picked = True
active_production.button_mark_done()
if i + 1 != nb_product_todo: # If last MO, don't make a backorder
action = active_production.button_mark_done()
@@ -249,7 +250,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4 * 3
ml.quantity = 4 * 3
details_operation_form.save()
# Consume 1 Product from lot1 and 2 from lot 2
@@ -257,10 +258,10 @@ class TestMrpProductionBackorder(TestMrpCommon):
self.assertEqual(len(p2_smls), 2, 'One for each lot')
details_operation_form = Form(production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
ml.lot_id = lot1
with details_operation_form.move_line_ids.edit(1) as ml:
ml.qty_done = 2
ml.quantity = 2
ml.lot_id = lot2
details_operation_form.save()
@@ -272,7 +273,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
p2_bo_mls = production.procurement_group_id.mrp_production_ids[-1].move_raw_ids.filtered(lambda m: m.product_id == p2).move_line_ids
self.assertEqual(len(p2_bo_mls), 1)
self.assertEqual(p2_bo_mls.lot_id, lot1)
self.assertEqual(p2_bo_mls.reserved_qty, 2)
self.assertEqual(p2_bo_mls.quantity, 2)
def test_uom_backorder(self):
"""
@@ -352,22 +353,20 @@ class TestMrpProductionBackorder(TestMrpCommon):
production.action_assign()
active_production = production
for i in range(nb_product_todo):
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.lot_id = serials_p1[i]
details_operation_form.save()
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.lot_id = serials_p2[i]
details_operation_form.save()
production_form = Form(active_production)
production_form.qty_producing = 1
production_form.lot_producing_id = serials_final[i]
active_production = production_form.save()
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.quantity = 1
ml.lot_id = serials_p1[i]
details_operation_form.save()
details_operation_form = Form(active_production.move_raw_ids.filtered(lambda m: m.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.quantity = 1
ml.lot_id = serials_p2[i]
details_operation_form.save()
active_production.button_mark_done()
if i + 1 != nb_product_todo: # If last MO, don't make a backorder
action = active_production.button_mark_done()
@@ -556,6 +555,7 @@ class TestMrpProductionBackorder(TestMrpCommon):
self.assertEqual(production.reserve_visible, False)
backorder = produce_one(production)
self.assertEqual(backorder.state, 'confirmed')
# The backorder is re reserved depending on the picking type
self.assertEqual(backorder.reserve_visible, False)
+1 -1
View File
@@ -1264,7 +1264,7 @@ class TestBoM(TestMrpCommon):
operation.duration_expected = 480
with mo_form.move_byproduct_ids.new() as by_product_line:
by_product_line.product_id = by_product
by_product_line.quantity_done = 6
by_product_line.quantity = 6
mo_3 = mo_form.save()
bom_from_mo_3 = generate_bom_from_mo(mo_3)
# Checks the MO's data.
+1 -6
View File
@@ -105,7 +105,7 @@ class TestMrpByProduct(common.TransactionCase):
mnf_product_a.action_confirm()
self.assertEqual(mnf_product_a.state, "confirmed")
mnf_product_a.move_raw_ids._action_assign()
mnf_product_a.move_raw_ids.quantity_done = mnf_product_a.move_raw_ids.product_uom_qty
mnf_product_a.move_raw_ids.picked = True
mnf_product_a.move_raw_ids._action_done()
self.assertEqual(mnf_product_a.state, "progress")
mnf_product_a.qty_producing = 2
@@ -357,8 +357,6 @@ class TestMrpByProduct(common.TransactionCase):
'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0
})
byproduct_2 = self.env['stock.move'].create({
'name': 'By Product 2',
@@ -367,8 +365,6 @@ class TestMrpByProduct(common.TransactionCase):
'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0
})
# Update byproduct has cost share > 100%
@@ -404,6 +400,5 @@ class TestMrpByProduct(common.TransactionCase):
mo_form.qty_producing = 1
mo = mo_form.save()
self.assertEqual(mo.state, 'to_close')
mo.move_byproduct_ids[0].quantity_done = 1
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
+16 -15
View File
@@ -1,7 +1,7 @@
import copy
from odoo.exceptions import UserError
from odoo.tests import common, tagged
from odoo.tests import common, tagged, Form
class TestConsumeComponentCommon(common.TransactionCase):
@@ -261,7 +261,7 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are fully reserved (stock.move state is available)
mo_all.action_assign()
for mov in mo_all.move_raw_ids:
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
self.assertEqual(mov.product_qty, mov.quantity, "Reserved quantity shall be equal to To Consume quantity.")
# Test for Serial Product
self.executeConsumptionTriggers(mo_serial)
@@ -269,9 +269,9 @@ class TestConsumeComponent(TestConsumeComponentCommon):
self.executeConsumptionTriggers(mo_lot)
for mov in mo_all.move_raw_ids:
if mov.has_tracking == 'none' or mov.raw_material_production_id.state == 'done':
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
self.assertTrue(mov.picked, "non tracked components should be picked")
else:
self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to 0.")
self.assertFalse(mov.picked, "tracked components should be picked")
def test_option_enabled_and_qty_available(self):
"""Option enabled, qty available
@@ -296,13 +296,13 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are fully reserved (stock.move state is available)
mo_all.action_assign()
for mov in mo_all.move_raw_ids:
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
self.assertEqual(mov.product_qty, mov.quantity, "Reserved quantity shall be equal to To Consume quantity.")
self.executeConsumptionTriggers(mo_serial)
self.executeConsumptionTriggers(mo_none)
self.executeConsumptionTriggers(mo_lot)
for mov in mo_all.move_raw_ids:
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
self.assertTrue(mov.picked, "All components should be picked")
def test_option_enabled_and_qty_not_available(self):
"""Option enabled, qty not available
@@ -320,7 +320,7 @@ class TestConsumeComponent(TestConsumeComponentCommon):
# Quantities are not reserved at all (stock.move state is confirmed)
mo_all.action_assign()
for mov in mo_all.move_raw_ids:
self.assertEqual(0, mov.reserved_availability, "Reserved quantity shall be equal to 0.")
self.assertEqual(0, mov.quantity, "Reserved quantity shall be equal to 0.")
self.executeConsumptionTriggers(mo_serial)
self.executeConsumptionTriggers(mo_none)
@@ -328,9 +328,9 @@ class TestConsumeComponent(TestConsumeComponentCommon):
for mov in mo_all.move_raw_ids:
if mov.has_tracking == 'none':
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
self.assertTrue(mov.picked, "components should be picked even without no quantity reserved")
else:
self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
self.assertEqual(mov.product_qty, mov.quantity, "Done quantity shall be equal to To Consume quantity.")
def test_option_enabled_and_qty_partially_available(self):
"""Option enabled, qty partially available
@@ -360,23 +360,24 @@ class TestConsumeComponent(TestConsumeComponentCommon):
mo.action_assign()
for mov in mo.move_raw_ids:
if mov.has_tracking == "none":
self.assertEqual(raw_none_qty, mov.reserved_availability, "Reserved quantity shall be equal to " + str(raw_none_qty)+ ".")
self.assertEqual(raw_none_qty, mov.quantity, "Reserved quantity shall be equal to " + str(raw_none_qty) + ".")
else:
self.assertEqual(raw_tracked_qty, mov.reserved_availability, "Reserved quantity shall be equal to " + str(raw_tracked_qty)+ ".")
self.assertEqual(raw_tracked_qty, mov.quantity, "Reserved quantity shall be equal to " + str(raw_tracked_qty) + ".")
if serialTrigger is None:
self.executeConsumptionTriggers(mo)
elif serialTrigger == 1:
mo.qty_producing = mo.product_qty
mo._onchange_producing()
mo_form = Form(mo)
mo_form.qty_producing = mo_form.product_qty
mo = mo_form.save()
elif serialTrigger == 2:
mo.action_generate_serial()
for mov in mo.move_raw_ids:
if mov.has_tracking == "none":
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
self.assertTrue(mov.picked, "non tracked components should be picked")
else:
self.assertEqual(raw_tracked_qty, mov.quantity_done, "Done quantity shall be equal to " + str(raw_tracked_qty)+ ".")
self.assertEqual(mov.product_qty, mov.quantity, "Done quantity shall be equal to To Consume quantity.")
mo.action_cancel()
testUnit(self.mo_none_tmpl)
+18 -14
View File
@@ -9,9 +9,9 @@ from odoo.tests import tagged, Form, HttpCase
class TestTourManualConsumption(HttpCase):
def test_mrp_manual_consumption(self):
"""Test manual consumption mechanism. Test when manual consumption is
True, quantity_done won't be updated automatically. Bom line with tracked
True, quantity won't be updated automatically. Bom line with tracked
products or operations should be set to manual consumption automatically.
Also test that when manually change quantity_done, manual consumption
Also test that when manually change quantity, manual consumption
will be set to True. Also test when create backorder, the manual consumption
should be set according to the bom.
"""
@@ -58,22 +58,25 @@ class TestTourManualConsumption(HttpCase):
mo = mo_form.save()
move_nt, move_sn, move_lot = mo.move_raw_ids
self.assertEqual(move_nt.manual_consumption, False)
self.assertEqual(move_nt.quantity_done, 5)
self.assertEqual(move_nt.quantity, 5)
self.assertTrue(move_nt.picked)
self.assertEqual(move_sn.manual_consumption, True)
self.assertEqual(move_sn.quantity_done, 0)
self.assertEqual(move_sn.quantity, 5)
self.assertFalse(move_sn.picked)
self.assertEqual(move_lot.manual_consumption, True)
self.assertEqual(move_lot.quantity_done, 0)
self.assertEqual(move_lot.quantity, 5)
self.assertFalse(move_lot.picked)
action_id = self.env.ref('mrp.menu_mrp_production_action').action
url = "/web#model=mrp.production&view_type=form&action=%s&id=%s" % (str(action_id.id), str(mo.id))
self.start_tour(url, "test_mrp_manual_consumption", login="admin", timeout=200)
self.start_tour(url, "test_mrp_manual_consumption", login="admin", timeout=100)
self.assertEqual(move_nt.manual_consumption, True)
self.assertEqual(move_nt.quantity_done, 6.0)
self.assertEqual(move_nt.quantity, 6.0)
self.assertEqual(move_sn.manual_consumption, True)
self.assertEqual(move_sn.quantity_done, 0)
self.assertEqual(move_sn.quantity, 0)
self.assertEqual(move_lot.manual_consumption, True)
self.assertEqual(move_lot.quantity_done, 0)
self.assertEqual(move_lot.quantity, 0)
backorder = mo.procurement_group_id.mrp_production_ids - mo
move_nt = backorder.move_raw_ids.filtered(lambda m: m.product_id == product_nt)
@@ -111,12 +114,13 @@ class TestManualConsumption(TestMrpCommon):
mo_form = Form(mo)
mo_form.qty_producing = 1
mo_form.save()
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity_done")), 0)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity_done")), 1)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity")), 4)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity")), 1)
self.assertEqual(sorted(mo.move_raw_ids.mapped('picked')), sorted([False, True]))
details_operation_form = Form(mo.move_raw_ids.filtered(lambda m: m.product_id == c1), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 4
ml.quantity = 4
ml.lot_id = lot
details_operation_form.save()
@@ -152,8 +156,8 @@ class TestManualConsumption(TestMrpCommon):
mo_form = Form(mo)
mo_form.qty_producing = 1
mo_form.save()
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity_done")), 4)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity_done")), 1)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c1.id).mapped("quantity")), 4)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id.id == c2.id).mapped("quantity")), 1)
action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
+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'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = lot_b
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
mo.move_raw_ids.picked = True
with self.assertRaises(UserError):
mo.button_mark_done()
+140 -167
View File
@@ -182,24 +182,24 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 2
ml.quantity = 2
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 11
ml.quantity = 11
details_operation_form.save()
self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2)
self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('qty_done'), [2])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [11])
self.assertEqual(mo.move_raw_ids[0].quantity_done, 2)
self.assertEqual(mo.move_raw_ids[1].quantity_done, 11)
self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('quantity'), [2])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [11])
self.assertEqual(mo.move_raw_ids[0].quantity, 2)
self.assertEqual(mo.move_raw_ids[1].quantity, 11)
mo.button_mark_done()
self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2)
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [2, 11])
self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.qty_done'), [2, 11])
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [2, 11])
self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.quantity'), [2, 11])
def test_under_consumption(self):
""" Consume less component quantity than the initial demand.
@@ -218,26 +218,26 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 0
ml.quantity = 0
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 5
ml.quantity = 5
details_operation_form.save()
self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 2)
self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('qty_done'), [0])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('qty_done'), [5])
self.assertEqual(mo.move_raw_ids[0].quantity_done, 0)
self.assertEqual(mo.move_raw_ids[1].quantity_done, 5)
self.assertEqual(mo.move_raw_ids[0].move_line_ids.mapped('quantity'), [0])
self.assertEqual(mo.move_raw_ids[1].move_line_ids.mapped('quantity'), [5])
self.assertEqual(mo.move_raw_ids[0].quantity, 0)
self.assertEqual(mo.move_raw_ids[1].quantity, 5)
mo.button_mark_done()
self.assertEqual(len(mo.move_raw_ids), 2)
self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 1)
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [0, 5])
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [0, 5])
self.assertEqual(mo.move_raw_ids.mapped('product_uom_qty'), [1, 10])
self.assertEqual(mo.move_raw_ids.mapped('state'), ['cancel', 'done'])
self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.qty_done'), [5])
self.assertEqual(mo.move_raw_ids.mapped('move_line_ids.quantity'), [5])
def test_update_quantity_1(self):
""" Build 5 final products with different consumed lots,
@@ -271,17 +271,18 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = lot_1
ml.qty_done = 20
ml.quantity = 20
details_operation_form.save()
mo.move_raw_ids[1].picked = True
update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id,
'product_qty': 4,
})
update_quantity_wizard.change_prod_qty()
self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done, 20, 'Update the produce quantity should not impact already produced quantity.')
self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity, 20, 'Update the produce quantity should not impact already produced quantity.')
self.assertEqual(mo.move_finished_ids.product_uom_qty, 4)
mo.button_mark_done()
@@ -303,8 +304,6 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 2
mo = mo_form.save()
mo._post_inventory()
update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id,
'product_qty': 5,
@@ -315,8 +314,8 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
mo.button_mark_done()
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity_done')), 20)
self.assertEqual(sum(mo.move_finished_ids.mapped('quantity_done')), 5)
self.assertEqual(sum(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).mapped('quantity')), 20)
self.assertEqual(sum(mo.move_finished_ids.mapped('quantity')), 5)
def test_update_quantity_3(self):
bom = self.env['mrp.bom'].create({
@@ -536,8 +535,8 @@ class TestMrpOrder(TestMrpCommon):
mo_form = Form(production)
mo_form.qty_producing = 8
production = mo_form.save()
self.assertEqual(production.move_raw_ids[0].quantity_done, 16, 'Should use half-up rounding when producing')
self.assertEqual(production.move_raw_ids[1].quantity_done, 34, 'Should use half-up rounding when producing')
self.assertEqual(production.move_raw_ids[0].quantity, 16, 'Should use half-up rounding when producing')
self.assertEqual(production.move_raw_ids[1].quantity, 34, 'Should use half-up rounding when producing')
def test_product_produce_1(self):
""" Checks the production wizard contains lines even for untracked products. """
@@ -553,7 +552,7 @@ class TestMrpOrder(TestMrpCommon):
# change the quantity done in one line
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
# change the quantity producing
@@ -562,9 +561,9 @@ class TestMrpOrder(TestMrpCommon):
# check than all quantities are update correctly
self.assertEqual(mo_form.move_raw_ids._records[0]['product_uom_qty'], 5, "Wrong quantity to consume")
self.assertEqual(mo_form.move_raw_ids._records[0]['quantity_done'], 3, "Wrong quantity done")
self.assertEqual(mo_form.move_raw_ids._records[0]['quantity'], 3, "Wrong quantity done")
self.assertEqual(mo_form.move_raw_ids._records[1]['product_uom_qty'], 20, "Wrong quantity to consume")
self.assertEqual(mo_form.move_raw_ids._records[1]['quantity_done'], 12, "Wrong quantity done")
self.assertEqual(mo_form.move_raw_ids._records[1]['quantity'], 12, "Wrong quantity done")
def test_product_produce_2(self):
""" Checks that, for a BOM where one of the components is tracked by serial number and the
@@ -599,10 +598,10 @@ class TestMrpOrder(TestMrpCommon):
# get the proposed lot
details_operation_form = Form(mo.move_raw_ids.filtered(lambda move: move.product_id == p1), view=self.env.ref('stock.view_stock_move_operations'))
self.assertEqual(len(details_operation_form.move_line_ids), 2)
self.assertEqual(len(details_operation_form.move_line_ids), 1)
with details_operation_form.move_line_ids.edit(0) as ml:
consumed_lots = ml.lot_id
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
remaining_lot = (lot_p1_1 | lot_p1_2) - consumed_lots
@@ -668,28 +667,22 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
# p2
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as line:
line.qty_done = line.reserved_uom_qty
with details_operation_form.move_line_ids.new() as line:
line.qty_done = 1
line.quantity = 1
details_operation_form.save()
# p1
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
for i in range(len(details_operation_form.move_line_ids)):
# reservation in shelf1: 3 lot1, shelf2: 3 lot1, stock: 4 lot2
with details_operation_form.move_line_ids.edit(i) as line:
line.qty_done = line.reserved_uom_qty
with details_operation_form.move_line_ids.new() as line:
line.qty_done = 2
line.quantity = 2
line.lot_id = first_lot_for_p1
with details_operation_form.move_line_ids.new() as line:
line.qty_done = 1
line.quantity = 1
line.lot_id = second_lot_for_p1
details_operation_form.save()
move_1 = mo.move_raw_ids.filtered(lambda m: m.product_id == p1)
# qty_done/reserved_uom_qty lot
# quantity/reserved_uom_qty lot
# 3/3 lot 1 shelf 1
# 1/1 lot 1 shelf 2
# 2/2 lot 1 shelf 2
@@ -698,9 +691,9 @@ class TestMrpOrder(TestMrpCommon):
ml_to_shelf_1 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_1)
ml_to_shelf_2 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_2)
self.assertEqual(sum(ml_to_shelf_1.mapped('qty_done')), 3.0, '3 units should be took from shelf1 as reserved.')
self.assertEqual(sum(ml_to_shelf_2.mapped('qty_done')), 3.0, '3 units should be took from shelf2 as reserved.')
self.assertEqual(move_1.quantity_done, 13, 'You should have used the tem units.')
self.assertEqual(sum(ml_to_shelf_1.mapped('quantity')), 3.0, '3 units should be took from shelf1 as reserved.')
self.assertEqual(sum(ml_to_shelf_2.mapped('quantity')), 3.0, '3 units should be took from shelf2 as reserved.')
self.assertEqual(move_1.quantity, 13, 'You should have used the tem units.')
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -723,9 +716,6 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(len(ml_p1), 2)
self.assertEqual(len(ml_p2), 1)
# Add some quantity already done to force an extra move line to be created
ml_p1[0].qty_done = 1.0
# Produce baby!
mo_form = Form(mo)
mo_form.qty_producing = 1
@@ -734,9 +724,8 @@ class TestMrpOrder(TestMrpCommon):
m_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1)
ml_p1 = m_p1.mapped('move_line_ids')
self.assertEqual(len(ml_p1), 2)
self.assertEqual(sorted(ml_p1.mapped('qty_done')), [2.0, 3.0], 'Quantity done should be 1.0, 2.0 or 3.0')
self.assertEqual(m_p1.quantity_done, 5.0, 'Total qty done should be 6.0')
self.assertEqual(sum(ml_p1.mapped('reserved_uom_qty')), 5.0, 'Total qty reserved should be 5.0')
self.assertEqual(sorted(ml_p1.mapped('quantity')), [2.0, 3.0], 'Quantity should be 2.0 and 3.0')
self.assertEqual(m_p1.quantity, 5.0, 'Total qty done should be 5.0')
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -763,21 +752,21 @@ class TestMrpOrder(TestMrpCommon):
mo._post_inventory()
self.assertEqual(len(mo.move_raw_ids), 4)
mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity_done = 3
mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity = 3
update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id,
'product_qty': 3,
})
mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity_done = 0
mo.move_raw_ids.filtered(lambda m: m.state != 'done')[0].quantity = 0
update_quantity_wizard.change_prod_qty()
self.assertEqual(len(mo.move_raw_ids), 4)
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertTrue(all(s in ['done', 'cancel'] for s in mo.move_raw_ids.mapped('state')))
self.assertEqual(sum(mo.move_raw_ids.mapped('move_line_ids.reserved_uom_qty')), 0)
def test_consumption_strict_1(self):
""" Checks the constraints of a strict BOM without tracking when playing around
@@ -800,7 +789,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
# Won't accept to be done, instead return a wizard
mo.button_mark_done()
@@ -838,7 +827,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
# Won't accept to be done, instead return a wizard
@@ -878,7 +867,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
# Won't accept to be done, instead return a wizard
@@ -910,7 +899,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
# Won't accept to be done, instead return a wizard
@@ -937,7 +926,7 @@ class TestMrpOrder(TestMrpCommon):
with self.assertRaises(AssertionError):
with mo_form.move_raw_ids.new() as move:
move.product_id = serial
move.quantity_done = 2
move.quantity = 2
mo_form.save()
def test_product_produce_10(self):
@@ -1011,18 +1000,18 @@ class TestMrpOrder(TestMrpCommon):
move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 2.0)
self.assertEqual(move_byproduct_1.quantity_done, 0)
self.assertEqual(move_byproduct_1.quantity, 2)
self.assertEqual(len(move_byproduct_1.move_line_ids), 2)
move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 4.0)
self.assertEqual(move_byproduct_2.quantity_done, 0)
self.assertEqual(move_byproduct_2.quantity, 4)
self.assertEqual(len(move_byproduct_2.move_line_ids), 1)
move_byproduct_3 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3)
self.assertEqual(move_byproduct_3.product_uom_qty, 4.0)
self.assertEqual(move_byproduct_3.quantity_done, 0)
self.assertEqual(move_byproduct_3.quantity, 4)
self.assertEqual(move_byproduct_3.product_uom, dozen)
self.assertEqual(len(move_byproduct_3.move_line_ids), 1)
@@ -1032,27 +1021,28 @@ class TestMrpOrder(TestMrpCommon):
move_byproduct_1 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 2.0)
self.assertEqual(move_byproduct_1.quantity_done, 0)
self.assertEqual(move_byproduct_1.quantity, 1)
self.assertFalse(move_byproduct_1.picked)
move_byproduct_2 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 4.0)
self.assertEqual(move_byproduct_2.quantity_done, 0)
self.assertEqual(move_byproduct_2.quantity, 2)
self.assertFalse(move_byproduct_2.picked)
move_byproduct_3 = mo.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3)
self.assertEqual(move_byproduct_3.product_uom_qty, 4.0)
self.assertEqual(move_byproduct_3.quantity_done, 2.0)
self.assertEqual(move_byproduct_3.quantity, 2.0)
self.assertTrue(move_byproduct_3.picked)
self.assertEqual(move_byproduct_3.product_uom, dozen)
details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.serial_1
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.lot_1
ml.qty_done = 2
details_operation_form.save()
action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
@@ -1066,42 +1056,44 @@ class TestMrpOrder(TestMrpCommon):
move_byproduct_1 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1)
self.assertEqual(len(move_byproduct_1), 1)
self.assertEqual(move_byproduct_1.product_uom_qty, 1.0)
self.assertEqual(move_byproduct_1.quantity_done, 0)
self.assertEqual(move_byproduct_1.quantity, 1)
self.assertFalse(move_byproduct_1.picked)
move_byproduct_2 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2)
self.assertEqual(len(move_byproduct_2), 1)
self.assertEqual(move_byproduct_2.product_uom_qty, 2.0)
self.assertEqual(move_byproduct_2.quantity_done, 0)
self.assertEqual(move_byproduct_2.quantity, 2)
self.assertFalse(move_byproduct_2.picked)
move_byproduct_3 = mo2.move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3)
self.assertEqual(move_byproduct_3.product_uom_qty, 2.0)
self.assertEqual(move_byproduct_3.quantity_done, 2.0)
self.assertEqual(move_byproduct_3.quantity, 2.0)
self.assertTrue(move_byproduct_3.picked)
self.assertEqual(move_byproduct_3.product_uom, dozen)
details_operation_form = Form(move_byproduct_1, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.serial_2
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
details_operation_form = Form(move_byproduct_2, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.lot_2
ml.qty_done = 2
details_operation_form.save()
details_operation_form = Form(move_byproduct_3, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 3
ml.quantity = 3
details_operation_form.save()
mo2.button_mark_done()
move_lines_byproduct_1 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct1).mapped('move_line_ids')
move_lines_byproduct_2 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct2).mapped('move_line_ids')
move_lines_byproduct_3 = (mo | mo2).move_finished_ids.filtered(lambda l: l.product_id == self.byproduct3).mapped('move_line_ids')
self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_1).qty_done, 1.0)
self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_2).qty_done, 1.0)
self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_1).qty_done, 2.0)
self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_2).qty_done, 2.0)
self.assertEqual(sum(move_lines_byproduct_3.mapped('qty_done')), 5.0)
self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_1).quantity, 1.0)
self.assertEqual(move_lines_byproduct_1.filtered(lambda ml: ml.lot_id == self.serial_2).quantity, 1.0)
self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_1).quantity, 2.0)
self.assertEqual(move_lines_byproduct_2.filtered(lambda ml: ml.lot_id == self.lot_2).quantity, 2.0)
self.assertEqual(sum(move_lines_byproduct_3.mapped('quantity')), 5.0)
self.assertEqual(move_lines_byproduct_3.mapped('product_uom_id'), dozen)
def test_product_produce_11(self):
@@ -1122,33 +1114,30 @@ class TestMrpOrder(TestMrpCommon):
mo_form = Form(mo)
mo_form.qty_producing = 3
self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 15, 'Update the produce quantity should change the components quantity.')
self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 15, 'Update the produce quantity should change the components quantity.')
mo = mo_form.save()
self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.')
mo_form = Form(mo)
mo_form.qty_producing = 4
self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 20, 'Update the produce quantity should change the components quantity.')
self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 20, 'Update the produce quantity should change the components quantity.')
mo = mo_form.save()
self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.')
mo_form = Form(mo)
mo_form.qty_producing = 1
self.assertEqual(sum([x['quantity_done'] for x in mo_form.move_raw_ids._records]), 5, 'Update the produce quantity should change the components quantity.')
self.assertEqual(sum([x['quantity'] for x in mo_form.move_raw_ids._records]), 5, 'Update the produce quantity should change the components quantity.')
mo = mo_form.save()
self.assertEqual(sum(mo.move_raw_ids.mapped('reserved_availability')), 5, 'Update the produce quantity should not change the components reserved quantity.')
# try adding another product that doesn't belong to the BoM
with mo_form.move_raw_ids.new() as move:
move.product_id = self.product_4
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[-1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 10
ml.quantity = 10
details_operation_form.save()
# Check that this new product is not updated by qty_producing
mo_form = Form(mo)
mo_form.qty_producing = 2
for move in mo_form.move_raw_ids._records:
if move['product_id'] == self.product_4.id:
self.assertEqual(move['quantity_done'], 10)
self.assertEqual(move['quantity'], 10)
break
mo = mo_form.save()
mo.button_mark_done()
@@ -1192,9 +1181,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1
mo1 = mo_form.save()
details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = sn
details_operation_form.save()
mo1.move_raw_ids.picked = True
mo1.button_mark_done()
mo_form = Form(self.env['mrp.production'])
@@ -1208,9 +1198,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1
mo2 = mo_form.save()
details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = sn
details_operation_form.save()
mo2.move_raw_ids.picked = True
with self.assertRaises(UserError):
mo2.button_mark_done()
@@ -1250,7 +1241,7 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
move_byproduct = mo.move_finished_ids.filtered(lambda m: m.product_id != mo.product_id)
details_operation_form = Form(move_byproduct, view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = sn
details_operation_form.save()
mo.button_mark_done()
@@ -1286,9 +1277,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1
mo1 = mo_form.save()
details_operation_form = Form(mo1.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = sn
details_operation_form.save()
mo1.move_raw_ids.picked = True
mo1.button_mark_done()
unbuild_form = Form(self.env['mrp.unbuild'])
@@ -1310,9 +1302,10 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1
mo2 = mo_form.save()
details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = sn
details_operation_form.save()
mo2.move_raw_ids.picked = True
mo2.button_mark_done()
def test_product_produce_duplicate_5(self):
@@ -1373,9 +1366,10 @@ class TestMrpOrder(TestMrpCommon):
view=self.env.ref("stock.view_stock_move_operations"),
)
with finished_good_detailed_operations_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
ml.lot_id = subassembly_sn
finished_good_detailed_operations_form.save()
finished_good_mo.move_raw_ids.picked = True
finished_good_mo.button_mark_done()
finished_good_ub_form = Form(self.env["mrp.unbuild"])
@@ -1425,8 +1419,6 @@ class TestMrpOrder(TestMrpCommon):
'product_uom': self.ref('uom.product_uom_unit'),
'production_id': mo.id,
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0,
})
self.assertEqual(move.name, mo.name)
@@ -1464,8 +1456,6 @@ class TestMrpOrder(TestMrpCommon):
'production_id': mo.id,
'location_id': self.ref('stock.stock_location_stock'),
'location_dest_id': self.ref('stock.stock_location_output'),
'product_uom_qty': 0,
'quantity_done': 0,
})
mo.move_raw_ids |= move
mo.action_confirm()
@@ -1525,13 +1515,13 @@ class TestMrpOrder(TestMrpCommon):
mo_form.lot_producing_id = final_product_lot
mo = mo_form.save()
move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done)
self.assertEqual(move_line_raw.qty_done, 1)
move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.quantity)
self.assertEqual(move_line_raw.quantity, 1)
self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
mo._post_inventory()
move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done)
self.assertEqual(move_line_finished.qty_done, 1)
move_line_finished = mo.move_finished_ids.move_line_ids.filtered(lambda m: m.state == 'done' and m.quantity)
self.assertEqual(move_line_finished.quantity, 1)
self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
def test_product_type_service_1(self):
@@ -1577,27 +1567,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign()
res_dict = mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', 'done'])
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1])
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1])
self.assertEqual(mo.move_finished_ids.state, 'done')
self.assertEqual(mo.move_finished_ids.quantity_done, 1)
def test_immediate_validate_2(self):
""" In a production non-tracked components, clicking on mark as done after filling quantity for a move be
processed as immediate production. The manual filling quantity will be kept."""
mo, bom, p_final, p1, p2 = self.generate_mo(qty_final=1, qty_base_1=1, qty_base_2=1)
self.env['stock.quant']._update_available_quantity(p1, self.stock_location_components, 5.0)
self.env['stock.quant']._update_available_quantity(p2, self.stock_location_components, 5.0)
mo.action_assign()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 2
details_operation_form.save()
mo.move_raw_ids.filtered('quantity_done').manual_consumption = True # this should be set automatically in front end
mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.mapped('state'), ['done', 'done'])
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [2, 1])
self.assertEqual(mo.move_finished_ids.state, 'done')
self.assertEqual(mo.move_finished_ids.quantity_done, 1)
self.assertEqual(mo.move_finished_ids.quantity, 1)
def test_immediate_validate_3(self):
""" In a production with a serial number tracked product. Check that the immediate production only creates
@@ -1611,7 +1583,7 @@ class TestMrpOrder(TestMrpCommon):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
action = wizard.action_backorder()
self.assertEqual(mo.qty_producing, 1)
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1])
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1])
self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2)
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
self.assertEqual(mo_backorder.product_qty, 1)
@@ -1628,7 +1600,7 @@ class TestMrpOrder(TestMrpCommon):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
action = wizard.action_backorder()
self.assertEqual(mo.qty_producing, 1)
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1, 1])
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1, 1])
self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), 2)
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
self.assertEqual(mo_backorder.product_qty, 1)
@@ -1657,9 +1629,9 @@ class TestMrpOrder(TestMrpCommon):
mos = mo1 | mo2 | mo3
mos.button_mark_done()
self.assertEqual(mos.move_raw_ids.mapped('state'), ['done'] * 6)
self.assertEqual(mos.move_raw_ids.mapped('quantity_done'), [1] * 6)
self.assertEqual(mos.move_raw_ids.mapped('quantity'), [1] * 6)
self.assertEqual(mos.move_finished_ids.mapped('state'), ['done'] * 3)
self.assertEqual(mos.move_finished_ids.mapped('quantity_done'), [1] * 3)
self.assertEqual(mos.move_finished_ids.mapped('quantity'), [1] * 3)
def test_components_availability(self):
self.bom_2.unlink() # remove the kit bom of product_5
@@ -1698,7 +1670,7 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(mo.components_availability, f'Exp {format_date(self.env, tommorrow)}')
self.assertEqual(mo.components_availability_state, 'expected')
(move1 | move2 | move3)._set_quantities_to_reservation()
(move1 | move2 | move3).picked = True
(move1 | move2 | move3)._action_done()
mo.invalidate_recordset(['components_availability', 'components_availability_state'])
@@ -1721,9 +1693,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign()
mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.mapped('state'), ['done'] * 2)
self.assertEqual(mo.move_raw_ids.mapped('quantity_done'), [1] * 2)
self.assertEqual(mo.move_raw_ids.mapped('quantity'), [1] * 2)
self.assertEqual(mo.move_finished_ids.state, 'done')
self.assertEqual(mo.move_finished_ids.quantity_done, 1)
self.assertEqual(mo.move_finished_ids.quantity, 1)
self.assertTrue(mo.move_finished_ids.move_line_ids.lot_id != False)
def test_immediate_validate_uom(self):
@@ -1756,9 +1728,9 @@ class TestMrpOrder(TestMrpCommon):
mo.action_assign()
mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.state, 'done')
self.assertEqual(mo.move_raw_ids.quantity_done, 12)
self.assertEqual(mo.move_raw_ids.quantity, 12)
self.assertEqual(mo.move_finished_ids.state, 'done')
self.assertEqual(mo.move_finished_ids.quantity_done, 1)
self.assertEqual(mo.move_finished_ids.quantity, 1)
self.assertEqual(component.qty_available, 13)
def test_immediate_validate_uom_2(self):
@@ -1815,7 +1787,7 @@ class TestMrpOrder(TestMrpCommon):
mo.button_mark_done()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 0.2)
self.assertEqual(mo.move_raw_ids.quantity_done, 0.2)
self.assertEqual(mo.move_raw_ids.quantity, 0.2)
def test_copy(self):
""" Check that copying a done production, create all the stock moves"""
@@ -2072,9 +2044,11 @@ class TestMrpOrder(TestMrpCommon):
"""
mo, _, _, _, _ = self.generate_mo(qty_final=30, qty_base_1=2, qty_base_2=3)
mo.action_confirm()
mo.qty_producing = 10
mo.move_raw_ids[0].quantity_done = 90
mo.move_raw_ids[1].quantity_done = 70
mo_form = Form(mo)
mo_form.qty_producing = 10
mo = mo_form.save()
mo.move_raw_ids[0].quantity = 90
mo.move_raw_ids[1].quantity = 70
action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder()
@@ -2089,9 +2063,9 @@ class TestMrpOrder(TestMrpCommon):
move_prod_2 = self.env['stock.move'].search([
('product_id', '=', mo.bom_id.bom_line_ids[1].product_id.id),
('raw_material_production_id', '=', mo.id)])
self.assertEqual(sum(move_prod_1.mapped('quantity_done')), 90.0)
self.assertEqual(sum(move_prod_1.mapped('quantity')), 90.0)
self.assertEqual(sum(move_prod_1.mapped('product_uom_qty')), 30.0)
self.assertEqual(sum(move_prod_2.mapped('quantity_done')), 70.0)
self.assertEqual(sum(move_prod_2.mapped('quantity')), 70.0)
self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 20.0)
# Check quantities of the backorder MO
@@ -2111,9 +2085,11 @@ class TestMrpOrder(TestMrpCommon):
"""
mo, _, _, p1, p2 = self.generate_mo(qty_final=20, qty_base_1=1, qty_base_2=1)
mo.action_confirm()
mo.qty_producing = 10
mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done = 5
mo.move_raw_ids.filtered(lambda m: m.product_id == p2).quantity_done = 10
mo_form = Form(mo)
mo_form.qty_producing = 10
mo = mo_form.save()
mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity = 5
mo.move_raw_ids.filtered(lambda m: m.product_id == p2).quantity = 10
action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder()
@@ -2123,10 +2099,10 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(mo.product_uom_qty, 10.0)
self.assertEqual(mo.qty_produced, 10.0)
move_prod_1_done = mo.move_raw_ids.filtered(lambda m: m.product_id == p1)
self.assertEqual(sum(move_prod_1_done.mapped('quantity_done')), 5)
self.assertEqual(sum(move_prod_1_done.mapped('quantity')), 5)
self.assertEqual(sum(move_prod_1_done.mapped('product_uom_qty')), 10)
move_prod_2 = mo.move_raw_ids.filtered(lambda m: m.product_id == p2)
self.assertEqual(sum(move_prod_2.mapped('quantity_done')), 10)
self.assertEqual(sum(move_prod_2.mapped('quantity')), 10)
self.assertEqual(sum(move_prod_2.mapped('product_uom_qty')), 10)
# Check quantities of the backorder MO
@@ -3193,7 +3169,7 @@ class TestMrpOrder(TestMrpCommon):
mo.action_confirm()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 1)
self.assertEqual(mo.move_raw_ids.move_line_ids.reserved_uom_qty, mo.move_raw_ids.product_uom_qty)
self.assertEqual(mo.move_raw_ids.move_line_ids.quantity, mo.move_raw_ids.product_uom_qty)
self.assertEqual(mo.move_raw_ids.availability, 250)
update_quantity_wizard = self.env['change.production.qty'].create({
'mo_id': mo.id,
@@ -3202,7 +3178,7 @@ class TestMrpOrder(TestMrpCommon):
update_quantity_wizard.change_prod_qty()
self.assertEqual(mo.move_raw_ids.product_uom_qty, 2)
self.assertEqual(mo.move_raw_ids.move_line_ids.reserved_uom_qty, mo.move_raw_ids.product_uom_qty)
self.assertEqual(mo.move_raw_ids.move_line_ids.quantity, mo.move_raw_ids.product_uom_qty)
self.assertEqual(mo.move_raw_ids.availability, 0)
def test_update_qty_to_consume_of_component(self):
@@ -3228,7 +3204,7 @@ class TestMrpOrder(TestMrpCommon):
with mo_form.move_raw_ids.edit(0) as raw:
raw.product_uom_qty = 1.25
self.assertEqual(mo.move_raw_ids.quantity_done, 1.25)
self.assertEqual(mo.move_raw_ids.quantity, 1.25)
def test_clear_finished_move(self):
""" Test that the finished moves created by the compute are correctly
@@ -3395,7 +3371,7 @@ class TestMrpOrder(TestMrpCommon):
- a component's UoM is changed after MO is created => action_set_qty = match BoM qty, but leave UoM unchanged (i.e. correctly convert)
scenario 3:
- a component has 2 moves in a MO => action_set_qty = set the 1st move to the correct qty, set 2nd move to 0
(i.e. no way to know how to distribute qty_done across these moves since warning aggregates qty by product)
(i.e. no way to know how to distribute quantity across these moves since warning aggregates qty by product)
"""
mo, bom, p_final, p1, p2 = self.generate_mo(consumption='warning', qty_final=10, qty_base_1=12, qty_base_2=20)
@@ -3405,13 +3381,13 @@ class TestMrpOrder(TestMrpCommon):
mo = mo_form.save()
# mo.move_raw_ids[0] = p2 => 20 qty_base, mo.move_raw_ids[1] = p1 => 12 qty_base
self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 200, "current MO To Consume qty should match expected qty to produce")
self.assertEqual(mo.move_raw_ids[0].quantity_done, 80, "current MO Consumed qty should match expected qty to produce")
self.assertEqual(mo.move_raw_ids[0].quantity, 80, "current MO Consumed qty should match expected qty to produce")
self.assertEqual(mo.move_raw_ids[1].product_uom_qty, 120, "current MO To Consume qty should match expected qty produced")
self.assertEqual(mo.move_raw_ids[1].quantity_done, 48, "current MO Consumed qty should match expected qty produced")
self.assertEqual(mo.move_raw_ids[1].quantity, 48, "current MO Consumed qty should match expected qty produced")
# bom changes won't auto-update MO, it will only show diff in consumption warning
bom.bom_line_ids[0].product_qty = 10
self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 200)
self.assertEqual(mo.move_raw_ids[0].quantity_done, 80)
self.assertEqual(mo.move_raw_ids[0].quantity, 80)
action = mo.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context']))
consumption = warning.save()
@@ -3421,9 +3397,9 @@ class TestMrpOrder(TestMrpCommon):
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder()
self.assertEqual(mo.move_raw_ids[0].product_uom_qty, 80, "current bom expected qty should remain unchanged")
self.assertEqual(mo.move_raw_ids[0].quantity_done, 40, "current bom expected qty was not applied as qty to be done")
self.assertEqual(mo.move_raw_ids[0].quantity, 40, "current bom expected qty was not applied as qty to be done")
self.assertEqual(mo.move_raw_ids[1].product_uom_qty, 48, "line without consumption issue was incorrectly changed")
self.assertEqual(mo.move_raw_ids[1].quantity_done, 48, "line without consumption issue was incorrectly changed")
self.assertEqual(mo.move_raw_ids[1].quantity, 48, "line without consumption issue was incorrectly changed")
self.assertEqual(mo.state, 'done')
# double check that backorder qtys are also correct
mo_backorder = mo.procurement_group_id.mrp_production_ids[-1]
@@ -3448,7 +3424,7 @@ class TestMrpOrder(TestMrpCommon):
mo2 = mo2_form.save()
self.assertEqual(len(mo2.move_raw_ids), 1, "current MO should still have 1 component from its BoM deleted")
self.assertEqual(mo2.move_raw_ids[0].product_uom_qty, 60, "current MO To Consume qty should match manually set expected qty produced")
self.assertEqual(mo2.move_raw_ids[0].quantity_done, 48, "current MO Consumed qty should match expected qty to produce based on manually set value")
self.assertEqual(mo2.move_raw_ids[0].quantity, 48, "current MO Consumed qty should match expected qty to produce based on manually set value")
action = mo2.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context']))
@@ -3468,10 +3444,10 @@ class TestMrpOrder(TestMrpCommon):
for move in mo2.move_raw_ids:
if move.product_id == p2:
self.assertEqual(move.product_uom_qty, 40, "missing line values were not correctly added")
self.assertEqual(move.quantity_done, 40, "missing line values were not correctly added")
self.assertEqual(move.quantity, 40, "missing line values were not correctly added")
else:
self.assertEqual(move.product_uom_qty, 48, "expected qty should be unchanged")
self.assertEqual(move.quantity_done, 4, "expected qty was not applied as qty to be done (UoM was possibly not correctly converted)")
self.assertEqual(move.quantity, 4, "expected qty was not applied as qty to be done (UoM was possibly not correctly converted)")
self.assertEqual(mo2.state, 'done')
# double check that backorder qtys are also correct
mo2_backorder = mo2.procurement_group_id.mrp_production_ids[-1]
@@ -3499,9 +3475,9 @@ class TestMrpOrder(TestMrpCommon):
mo3_form.qty_producing = 1
mo3 = mo3_form.save()
self.assertEqual(mo3.move_raw_ids[0].product_uom_qty, 12, "BoM created comp move does not match expected To Consume qty")
self.assertEqual(mo3.move_raw_ids[0].quantity_done, 12, "BoM created comp move does not match expected Consumed qty")
self.assertEqual(mo3.move_raw_ids[0].quantity, 12, "BoM created comp move does not match expected Consumed qty")
self.assertEqual(mo3.move_raw_ids[1].product_uom_qty, 5, "Manually added comp move does not match original To Consume qty")
self.assertEqual(mo3.move_raw_ids[1].quantity_done, 5, "Manually added comp move was not Consumed")
self.assertEqual(mo3.move_raw_ids[1].quantity, 5, "Manually added comp move was not Consumed")
action = mo3.button_mark_done()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context']))
consumption = warning.save()
@@ -3510,9 +3486,9 @@ class TestMrpOrder(TestMrpCommon):
self.assertEqual(consumption.mrp_consumption_warning_line_ids[0].product_consumed_qty_uom, 17, "total Consumed qty not correctly passed to wizard")
action = consumption.action_set_qty()
self.assertEqual(mo3.move_raw_ids[0].product_uom_qty, 12, "BoM created comp move does not match expected To Consume qty")
self.assertEqual(mo3.move_raw_ids[0].quantity_done, 12, "BoM created comp move does not match expected Consumed qty")
self.assertEqual(mo3.move_raw_ids[0].quantity, 12, "BoM created comp move does not match expected Consumed qty")
self.assertEqual(mo3.move_raw_ids[1].product_uom_qty, 5, "Manually added comp move To Consume qty should be unchanged")
self.assertEqual(mo3.move_raw_ids[1].quantity_done, 0, "Extra line Consumed qty not correctly zero-ed")
self.assertEqual(mo3.move_raw_ids[1].quantity, 0, "Extra line Consumed qty not correctly zero-ed")
self.assertEqual(mo3.state, 'done')
def test_exceeded_consumed_qty_and_duplicated_lines(self):
@@ -3549,15 +3525,15 @@ class TestMrpOrder(TestMrpCommon):
mo_form.qty_producing = 1.0
mo = mo_form.save()
mo.move_raw_ids[0].move_line_ids.qty_done = 1.5
mo.move_raw_ids[0].move_line_ids.quantity = 1.5
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
p02_raws = mo.move_raw_ids.filtered(lambda m: m.product_id == product02)
p03_raws = mo.move_raw_ids.filtered(lambda m: m.product_id == product03)
self.assertEqual(sum(p02_raws.mapped('quantity_done')), 1.5)
self.assertEqual(sum(p03_raws.mapped('quantity_done')), 2)
self.assertEqual(sum(p02_raws.mapped('quantity')), 1.5)
self.assertEqual(sum(p03_raws.mapped('quantity')), 2)
def test_validation_mo_with_tracked_component(self):
"""
@@ -3584,18 +3560,15 @@ class TestMrpOrder(TestMrpCommon):
mo.action_confirm()
self.assertEqual(mo.state, 'confirmed')
# set the qty to consume of the tracked product to 0
mo.move_raw_ids[0].write({
'product_uom_qty': 0,
'quantity_done': 0,
})
mo.action_assign()
mo.move_raw_ids[0].product_uom_qty = 0
mo.move_raw_ids[0].quantity = 0
# Set MO Done and create backorder
action = mo.button_mark_done()
consumption_warning = Form(self.env['mrp.consumption.warning'].with_context(**action['context'])).save()
self.assertEqual(len(consumption_warning.mrp_consumption_warning_line_ids), 1)
self.assertEqual(len(consumption_warning.mrp_consumption_warning_line_ids), 2)
self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_consumed_qty_uom, 0)
self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_expected_qty_uom, 1)
self.assertEqual(consumption_warning.mrp_consumption_warning_line_ids[0].product_expected_qty_uom, 0)
# Force the warning
consumption_warning.action_confirm()
self.assertEqual(mo.state, 'done')
+12 -14
View File
@@ -174,22 +174,21 @@ class TestProcurement(TestMrpCommon):
self.assertTrue(picking_qc_to_stock)
picking_input_to_qc.action_assign()
self.assertEqual(picking_input_to_qc.state, 'assigned')
picking_input_to_qc.move_line_ids.write({'qty_done': 5.0})
picking_input_to_qc.move_ids.write({'quantity': 5.0, 'picked': True})
picking_input_to_qc._action_done()
picking_qc_to_stock.action_assign()
self.assertEqual(picking_qc_to_stock.state, 'assigned')
picking_qc_to_stock.move_line_ids.write({'qty_done': 3.0})
picking_qc_to_stock.move_ids.write({'quantity': 3.0, 'picked': True})
picking_qc_to_stock.with_context(skip_backorder=True, picking_ids_not_to_backorder=picking_qc_to_stock.ids).button_validate()
self.assertEqual(picking_qc_to_stock.state, 'done')
mo.action_assign()
self.assertEqual(mo.move_raw_ids.reserved_availability, 3.0)
self.assertEqual(mo.move_raw_ids.quantity, 3.0)
produce_form = Form(mo)
produce_form.qty_producing = 3.0
mo = produce_form.save()
self.assertEqual(mo.move_raw_ids.quantity_done, 3.0)
picking_qc_to_stock.move_line_ids.qty_done = 5.0
self.assertEqual(mo.move_raw_ids.reserved_availability, 5.0)
self.assertEqual(mo.move_raw_ids.quantity_done, 3.0)
self.assertEqual(mo.move_raw_ids.quantity, 3.0)
picking_qc_to_stock.move_line_ids.quantity = 5.0
self.assertEqual(mo.move_raw_ids.quantity, 3.0)
def test_link_date_mo_moves(self):
""" Check link of shedule date for manufaturing with date stock move."""
@@ -343,7 +342,7 @@ class TestProcurement(TestMrpCommon):
production.button_mark_done()
move_dest._action_assign()
self.assertEqual(move_dest.reserved_availability, 10.0)
self.assertEqual(move_dest.quantity, 10.0)
def test_auto_assign(self):
""" When auto reordering rule exists, check for when:
@@ -455,7 +454,6 @@ class TestProcurement(TestMrpCommon):
'location_dest_id': self.ref('stock.stock_location_customers'),
})],
})
pick_output.action_reset_draft()
pick_output.action_confirm() # should trigger orderpoint to create and confirm 1st MO
pick_output.action_assign()
@@ -467,7 +465,7 @@ class TestProcurement(TestMrpCommon):
self.assertEqual(len(mo), 1, "Manufacture order was not automatically created")
mo.action_assign()
mo.is_locked = False
self.assertEqual(mo.move_raw_ids.reserved_availability, 0, "No components should be reserved yet")
self.assertEqual(mo.move_raw_ids.quantity, 0, "No components should be reserved yet")
self.assertEqual(mo.product_qty, 15, "Quantity to produce should be picking demand + reordering rule max qty")
# 2nd MO for product_2 should have been created and confirmed when 1st MO for product_1 was confirmed
@@ -483,7 +481,7 @@ class TestProcurement(TestMrpCommon):
mo2 = mo2_form.save()
mo2.button_mark_done()
self.assertEqual(mo.move_raw_ids.reserved_availability, 15, "Components should have been auto-reserved")
self.assertEqual(mo.move_raw_ids.quantity, 15, "Components should have been auto-reserved")
# add new component to 1st MO
mo_form = Form(mo)
@@ -501,12 +499,12 @@ class TestProcurement(TestMrpCommon):
self.assertEqual(mo3.product_qty, 6, "Quantity to produce should be 1 + reordering rule max qty")
mo_form = Form(mo)
mo.move_raw_ids.quantity_done = 15
mo.move_raw_ids.quantity = 15
mo_form.qty_producing = 15
mo = mo_form.save()
mo.button_mark_done()
self.assertEqual(pick_output.move_ids_without_package.reserved_availability, 10, "Completed products should have been auto-reserved in picking")
self.assertEqual(pick_output.move_ids_without_package.quantity, 10, "Completed products should have been auto-reserved in picking")
# make sure next MO auto-reserves components now that they are in stock since
# default reservation_method = 'at_confirm'
@@ -518,7 +516,7 @@ class TestProcurement(TestMrpCommon):
mo_assign_at_confirm = mo_form.save()
mo_assign_at_confirm.action_confirm()
self.assertEqual(mo_assign_at_confirm.move_raw_ids.reserved_availability, 5, "Components should have been auto-reserved")
self.assertEqual(mo_assign_at_confirm.move_raw_ids.quantity, 5, "Components should have been auto-reserved")
def test_check_update_qty_mto_chain(self):
""" Simulate a mto chain with a manufacturing order. Updating the
+4 -4
View File
@@ -117,11 +117,11 @@ class TestMrpSerialMassProduce(TestMrpCommon):
# Reload the wizard to apply generated serial numbers
wizard = Form(self.env['stock.assign.serial'].browse(action['res_id']))
wizard.save().apply()
# 1st & 3rd MO in sequence should have only 1 move lines (1 lot) for product_to_use_1 (2nd in bom)
# 1st & 2nd MO in sequence should have only 1 move lines (1 lot) for product_to_use_1 (2nd in bom)
self.assertEqual(mo.procurement_group_id.mrp_production_ids[0].move_raw_ids[1].move_lines_count, 1)
self.assertEqual(mo.procurement_group_id.mrp_production_ids[2].move_raw_ids[1].move_lines_count, 1)
# 2nd MO should have 2 move lines (2 different lots) for product_to_use_1
self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 2)
self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 1)
# 3rd MO should have 2 move lines (2 different lots) for product_to_use_1
self.assertEqual(mo.procurement_group_id.mrp_production_ids[2].move_raw_ids[1].move_lines_count, 2)
# Verify if lot IDs were correctly assigned to move lines
mo.procurement_group_id.mrp_production_ids.button_mark_done()
+11 -14
View File
@@ -296,7 +296,7 @@ class TestKitPicking(common.TestMrpCommon):
}
def test_kit_immediate_transfer(self):
""" Make sure a kit is split in the corrects quantity_done by components in case of an
""" Make sure a kit is split in the corrects quantity by components in case of an
immediate transfer.
"""
picking = self.env['stock.picking'].create({
@@ -308,7 +308,8 @@ class TestKitPicking(common.TestMrpCommon):
self.env['stock.move'].create({
'name': self.kit_parent.name,
'product_id': self.kit_parent.id,
'product_uom_qty': 3,
'quantity': 3,
'picked': True,
'product_uom': self.kit_parent.uom_id.id,
'picking_id': picking.id,
'picking_type_id': self.env.ref('stock.picking_type_in').id,
@@ -316,14 +317,12 @@ class TestKitPicking(common.TestMrpCommon):
'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id,
})
picking.button_validate()
backorder_wizard_dict = picking.button_validate()
backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context']))
backorder_wizard_form.save().process()
# We check that the picking has the correct quantities after its move were splitted.
self.assertEqual(len(picking.move_ids), 7)
for move_line in picking.move_ids:
self.assertEqual(move_line.quantity_done, self.expected_quantities[move_line.product_id])
for move in picking.move_ids:
self.assertEqual(move.quantity, self.expected_quantities[move.product_id])
self.assertEqual(move.state, 'done')
def test_kit_planned_transfer(self):
""" Make sure a kit is split in the corrects product_qty by components in case of a
@@ -334,7 +333,6 @@ class TestKitPicking(common.TestMrpCommon):
'location_dest_id': self.warehouse_1.wh_input_stock_loc_id.id,
'partner_id': self.test_partner.id,
'picking_type_id': self.env.ref('stock.picking_type_in').id,
'immediate_transfer': False,
})
move_receipt_1 = self.env['stock.move'].create({
'name': self.kit_parent.name,
@@ -368,8 +366,6 @@ class TestKitPicking(common.TestMrpCommon):
'picking_type_id': in_type.id,
'location_id': customer_location.id,
'location_dest_id': stock_location.id,
'state': 'draft',
'immediate_transfer': False,
'move_ids': [(0, 0, {
'name': product.name,
'product_id': product.id,
@@ -381,19 +377,20 @@ class TestKitPicking(common.TestMrpCommon):
})
receipt.action_confirm()
receipt.move_line_ids.qty_done = 1
receipt.move_line_ids.quantity = 1
receipt.move_line_ids = [(0, 0, {
'product_id': kit.id,
'qty_done': 1,
'quantity': 1,
'product_uom_id': kit.uom_id.id,
'location_id': customer_location.id,
'location_dest_id': stock_location.id,
})]
receipt.move_ids.picked = True
receipt.button_validate()
self.assertEqual(receipt.state, 'done')
self.assertRecordValues(receipt.move_ids, [
{'product_id': product.id, 'quantity_done': 1, 'state': 'done'},
{'product_id': compo.id, 'quantity_done': 1, 'state': 'done'},
{'product_id': product.id, 'quantity': 1, 'state': 'done'},
{'product_id': compo.id, 'quantity': 1, 'state': 'done'},
])
+4 -2
View File
@@ -123,7 +123,7 @@ class TestMrpStockReports(TestReportsCommon):
pick = mo_1.move_raw_ids.move_orig_ids.picking_id
pick_form = Form(pick)
with pick_form.move_line_ids_without_package.edit(0) as move_line:
move_line.qty_done = 20
move_line.quantity = 20
pick = pick_form.save()
pick.button_validate()
# Produces 3 products then creates a backorder for the remaining product.
@@ -236,7 +236,7 @@ class TestMrpStockReports(TestReportsCommon):
picking = picking_form.save()
picking.action_confirm()
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
move = picking.move_ids.filtered(lambda m: m.name == "Super Kit" and m.product_id == compo03)
move.move_line_ids.result_package_id = self.env['stock.quant.package'].create({'name': 'Package0001'})
picking.button_validate()
@@ -254,4 +254,6 @@ class TestMrpStockReports(TestReportsCommon):
break
if keys[0] in line:
keys = keys[1:]
self.assertFalse(keys, "All keys should be in the report with the defined order")
+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'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[2], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
@@ -192,48 +193,45 @@ class TestTraceability(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': product_1.id,
'name': 'Raw_1_lot_1',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': product_2.id,
'name': 'Raw_2_lot_1',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(
mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_1.id,
'name': 'Byproduct_1_lot_1',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(
mo.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_2.id,
'name': 'Byproduct_2_lot_1',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
mo.move_raw_ids.picked = True
action = mo.button_mark_done()
backorder = Form(self.env['mrp.production.backorder'].with_context(**action['context']))
backorder.save().action_backorder()
@@ -251,56 +249,50 @@ class TestTraceability(TestMrpCommon):
mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_1),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': product_1.id,
'name': 'Raw_1_lot_2',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(
mo_backorder.move_raw_ids.filtered(lambda m: m.product_id == product_2),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': product_2.id,
'name': 'Raw_2_lot_2',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(
mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_1),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_1.id,
'name': 'Byproduct_1_lot_2',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
details_operation_form = Form(
mo_backorder.move_finished_ids.filtered(lambda m: m.product_id == byproduct_2),
view=self.env.ref('stock.view_stock_move_operations')
)
with details_operation_form.move_line_ids.new() as ml:
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = self.env['stock.lot'].create({
'product_id': byproduct_2.id,
'name': 'Byproduct_2_lot_2',
'company_id': self.env.company.id,
})
ml.qty_done = 1
details_operation_form.save()
mo_backorder.move_raw_ids.picked = True
mo_backorder.button_mark_done()
# self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 4)
# self.assertEqual(len(mo.move_finished_ids.mapped('move_line_ids')), 6)
mo = mo | mo_backorder
raw_move_lines = mo.move_raw_ids.mapped('move_line_ids')
raw_line_raw_1_lot_1 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_1')
@@ -310,19 +302,19 @@ class TestTraceability(TestMrpCommon):
finished_move_lines = mo.move_finished_ids.mapped('move_line_ids')
finished_move_line_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_1')
self.assertEqual(finished_move_line_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
self.assertEqual(finished_move_line_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
finished_move_line_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_2')
raw_line_raw_1_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_2')
raw_line_raw_2_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_2_lot_2')
self.assertEqual(finished_move_line_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
byproduct_move_line_1_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_1')
self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
byproduct_move_line_1_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_2')
self.assertEqual(byproduct_move_line_1_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
byproduct_move_line_2_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_1')
self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids.filtered(lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids.filtered(lambda l: l.quantity), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1)
byproduct_move_line_2_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_2')
self.assertEqual(byproduct_move_line_2_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
@@ -348,6 +340,7 @@ class TestTraceability(TestMrpCommon):
mo_form.qty_producing = 1.0
mo_form.lot_producing_id = lot
mo = mo_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
unbuild_form = Form(self.env['mrp.unbuild'])
@@ -367,6 +360,7 @@ class TestTraceability(TestMrpCommon):
_logger.warning('Dummy')
self.assertEqual(len(log_catcher.output), 1, "Useless warnings: \n%s" % "\n".join(log_catcher.output[:-1]))
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
@@ -483,7 +477,7 @@ class TestTraceability(TestMrpCommon):
mo_form.qty_producing = 1
mo_form.lot_producing_id = lot_componentA
mo = mo_form.save()
mo.move_raw_ids[0].quantity_done = 1.0
mo.move_raw_ids.picked = True
mo.button_mark_done()
# Produce 1 endProduct A
@@ -496,7 +490,7 @@ class TestTraceability(TestMrpCommon):
mo_form.qty_producing = 1
mo_form.lot_producing_id = lot_endProductA
mo = mo_form.save()
mo.move_raw_ids[0].quantity_done = 1.0
mo.move_raw_ids[0].write({'quantity': 1.0, 'picked': True})
mo.button_mark_done()
# Create out picking for EndProduct A
@@ -508,7 +502,7 @@ class TestTraceability(TestMrpCommon):
moveA = self.env['stock.move'].create({
'name': 'Picking A move',
'product_id': endproductA.id,
'quantity_done': 1,
'quantity': 1,
'product_uom': endproductA.uom_id.id,
'picking_id': pickingA_out.id,
'location_id': stock_location.id,
@@ -516,10 +510,11 @@ class TestTraceability(TestMrpCommon):
# Set move_line lot_id to the mrp.production lot_producing_id
moveA.move_line_ids[0].write({
'qty_done': 1.0,
'quantity': 1.0,
'lot_id': lot_endProductA.id,
})
# Transfer picking
moveA.picked = True
pickingA_out._action_done()
# Use concat so that delivery_ids is computed in batch.
@@ -557,7 +552,7 @@ class TestTraceability(TestMrpCommon):
with Form(mo) as mo_form:
mo_form.qty_producing = 1
mo.move_raw_ids.move_line_ids.qty_done = 1
mo.move_raw_ids.move_line_ids.quantity = 1
mo.button_mark_done()
# unbuild
@@ -583,12 +578,13 @@ class TestTraceability(TestMrpCommon):
'product_id': component.id,
'product_uom': component.uom_id.id,
'product_uom_qty': 1.0,
'picked': True,
'move_line_ids': [(0, 0, {
'product_id': component.id,
'location_id': scrap_location.id,
'location_dest_id': stock_location.id,
'product_uom_id': component.uom_id.id,
'qty_done': 1.0,
'quantity': 1.0,
'lot_id': serial_number.id,
})],
})
@@ -605,12 +601,13 @@ class TestTraceability(TestMrpCommon):
with Form(mo) as mo_form:
mo_form.qty_producing = 1
mo.move_raw_ids.move_line_ids.qty_done = 1
mo.move_raw_ids.move_line_ids.quantity = 1
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertRecordValues((mo.move_finished_ids + mo.move_raw_ids).move_line_ids, [
{'product_id': self.bom_4.product_id.id, 'lot_id': False, 'qty_done': 1},
{'product_id': component.id, 'lot_id': serial_number.id, 'qty_done': 1},
{'product_id': self.bom_4.product_id.id, 'lot_id': False, 'quantity': 1},
{'product_id': component.id, 'lot_id': serial_number.id, 'quantity': 1},
])
def test_generate_serial_button(self):
+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'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.lot_id = lot
ml.qty_done = 20
ml.quantity = 20
details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild.
@@ -272,13 +273,14 @@ class TestUnbuild(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 5
ml.quantity = 5
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 20
ml.quantity = 20
details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild.
@@ -371,15 +373,8 @@ class TestUnbuild(TestMrpCommon):
mo_form = Form(mo)
mo_form.qty_producing = 5.0
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids.filtered(lambda ml: ml.product_id == p2), view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = ml.reserved_uom_qty
with details_operation_form.move_line_ids.edit(1) as ml:
ml.qty_done = ml.reserved_uom_qty
with details_operation_form.move_line_ids.edit(2) as ml:
ml.qty_done = ml.reserved_uom_qty
details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# Check quantity in stock before unbuild.
@@ -427,12 +422,13 @@ class TestUnbuild(TestMrpCommon):
mo_form.qty_producing = 3.0
mo_form.lot_producing_id = lot_finished_1
mo = mo_form.save()
self.assertEqual(mo.move_raw_ids[1].quantity_done, 12)
self.assertEqual(mo.move_raw_ids[1].quantity, 12)
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 3
with details_operation_form.move_line_ids.edit(0) as ml:
ml.quantity = 3
ml.lot_id = lot_1
details_operation_form.save()
mo.move_raw_ids.picked = True
action = mo.button_mark_done()
backorder = Form(self.env[action['res_model']].with_context(**action['context']))
backorder.save().action_backorder()
@@ -460,16 +456,16 @@ class TestUnbuild(TestMrpCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 2
ml.quantity = 2
ml.lot_id = lot_2
details_operation_form.save()
action = mo.button_mark_done()
mo1 = mo.procurement_group_id.mrp_production_ids[0]
ml = mo1.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and lot_finished_1 in m.produce_line_ids.lot_id)
self.assertEqual(sum(ml.mapped('qty_done')), 12.0, 'Should have consumed 12 for the first lot')
self.assertEqual(sum(ml.mapped('quantity')), 12.0, 'Should have consumed 12 for the first lot')
ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and lot_finished_2 in m.produce_line_ids.lot_id)
self.assertEqual(sum(ml.mapped('qty_done')), 8.0, 'Should have consumed 8 for the second lot')
self.assertEqual(sum(ml.mapped('quantity')), 8.0, 'Should have consumed 8 for the second lot')
def test_unbuild_with_routes(self):
""" This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock
@@ -581,7 +577,7 @@ class TestUnbuild(TestMrpCommon):
# Transfer it
for ml in picking.move_ids_without_package:
ml.quantity_done = 1
ml.write({'quantity': 1, 'picked': True})
picking._action_done()
# Check the available quantity of components and final product in stock
@@ -662,12 +658,13 @@ class TestUnbuild(TestMrpCommon):
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
details_operation_form = Form(mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.edit(0) as ml:
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
mo.move_raw_ids.picked = True
mo.button_mark_done()
uo_form = Form(self.env['mrp.unbuild'])
@@ -708,7 +705,8 @@ class TestUnbuild(TestMrpCommon):
internal_form.location_dest_id = subloc01
with internal_form.move_ids_without_package.new() as move:
move.product_id = p_final
move.quantity_done = 1.0
move.quantity = 1.0
move.picked = True
internal_transfer = internal_form.save()
internal_transfer.button_validate()
@@ -811,11 +809,12 @@ class TestUnbuild(TestMrpCommon):
details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.lot_id = product_1_sn
ml.qty_done = 1
ml.quantity = 1
details_operation_form.save()
mo_form = Form(mo2)
mo_form.qty_producing = 1
mo2 = mo_form.save()
mo2.move_raw_ids.picked = True
mo2.button_mark_done()
self.assertEqual(mo2.state, 'done', "Production order should be in done state.")
@@ -857,7 +856,7 @@ class TestUnbuild(TestMrpCommon):
mo.action_confirm()
mo.qty_producing = 1.0
mo.lot_producing_id = finished_product_sn
mo.move_raw_ids.quantity_done = 1
mo.move_raw_ids.write({'quantity': 1, 'picked': True})
mo.button_mark_done()
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
# unbuild order mo_1
@@ -886,7 +885,7 @@ class TestUnbuild(TestMrpCommon):
mo_2.action_confirm()
mo_2.qty_producing = 1.0
mo_2.lot_producing_id = finished_product_sn
mo_2.move_raw_ids.quantity_done = 1
mo_2.move_raw_ids.write({'quantity': 1, 'picked': True})
mo_2.button_mark_done()
self.assertEqual(mo_2.state, 'done', "Production order should be in done state.")
# unbuild mo_2
@@ -147,7 +147,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'),
'picking_type_id': self.warehouse.out_type_id.id,
'state': 'draft',
'immediate_transfer': False,
})
self.env['stock.move'].create({
@@ -189,7 +188,7 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
self.assertEqual(picking_stock_postprod.origin, 'SOURCEDOCUMENT', 'The post-prod origin should be the SO name')
picking_stock_preprod.action_assign()
picking_stock_preprod.move_line_ids.qty_done = 4
picking_stock_preprod.move_ids.write({'quantity': 4, 'picked': True})
picking_stock_preprod._action_done()
self.assertFalse(sum(self.env['stock.quant']._gather(self.raw_product, self.warehouse.lot_stock_id).mapped('quantity')))
@@ -230,7 +229,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
'partner_id': self.env['ir.model.data']._xmlid_to_res_id('base.res_partner_4'),
'picking_type_id': self.warehouse.out_type_id.id,
'state': 'draft',
'immediate_transfer': False,
})
self.env['stock.move'].create({
'name': self.finished_product.name,
+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,
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 -->
<field name="cost_share"/>
</tree>
@@ -391,22 +392,23 @@
<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" 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_qty" readonly="1" column_invisible="True"/>
<field name="reserved_availability" column_invisible="True"/>
<field name="show_reserved" 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 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"/>
<field name="forecast_availability" string="Reserved" column_invisible="parent.state in ('draft', 'done')" widget="forecast_widget"/>
<field name="quantity_done" string="Consumed"
decoration-success="product_uom_qty - quantity_done &gt; 0.0001"
decoration-warning="quantity_done - product_uom_qty &gt; 0.0001"
<!-- TODO put forecast back -->
<field name="forecast_availability" string="Forecast" widget="forecast_widget"/>
<field name="quantity" string="Reserved"
decoration-success="product_uom_qty - quantity &gt; 0.0001"
decoration-warning="quantity - product_uom_qty &gt; 0.0001"
column_invisible="parent.state == 'draft'"
readonly="has_tracking != 'none'"
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="show_details_visible" column_invisible="True"/>
<field name="lot_ids" widget="many2many_tags"
@@ -453,7 +455,7 @@
<field name="move_lines_count" column_invisible="True"/>
<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="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="cost_share" optional="hide"/>
<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_id" readonly="state == 'done'"/>
<field name="product_qty" string="To Consume"/>
<field name="reserved_availability" string ="Reserved"/>
<field name="quantity_done" string="Consumed"/>
<field name="quantity" string="Quantity"/>
<field name="picked" string="Consumed"/>
<field name="product_qty_available" string="On Hand" invisible="product_type != 'product'"/>
<field name="product_virtual_available" string="Forecasted" invisible="product_type != 'product'"/>
</tree>
+2 -8
View File
@@ -15,10 +15,7 @@
<field name="inherit_id" ref="stock.view_stock_move_operations" />
<field name="arch" type="xml">
<xpath expr="//label[@for='product_uom_qty']" position="attributes">
<attribute name="string">Total To Consume</attribute>
</xpath>
<xpath expr="//label[@for='quantity_done']" position="attributes">
<attribute name="string">Consumed</attribute>
<attribute name="string">Total To Consume</attribute>
</xpath>
</field>
</record>
@@ -31,10 +28,7 @@
<field name="inherit_id" ref="stock.view_stock_move_operations" />
<field name="arch" type="xml">
<xpath expr="//label[@for='product_uom_qty']" position="attributes">
<attribute name="string">To Produce</attribute>
</xpath>
<xpath expr="//label[@for='quantity_done']" position="attributes">
<attribute name="string">Produced</attribute>
<attribute name="string">To Produce</attribute>
</xpath>
</field>
</record>
+5 -9
View File
@@ -58,20 +58,16 @@ class ChangeProductionQty(models.TransientModel):
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
for wizard in self:
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 < produced:
if wizard.product_qty < production.qty_produced:
format_qty = '%.{precision}f'.format(precision=precision)
raise UserError(_(
"You have already processed %(quantity)s. Please input a quantity higher than %(minimum)s ",
quantity=format_qty % produced,
minimum=format_qty % produced
"You have already processed %(quantity)s. Please input a quantity higher than %(quantity)s ",
quantity=format_qty % production.qty_produced,
))
old_production_qty = production.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)
documents = {}
for move, old_qty, new_qty in update_info:
@@ -84,7 +80,7 @@ class ChangeProductionQty(models.TransientModel):
else:
documents[key] = [value]
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})
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:
continue
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 (move.has_tracking in ('lot', 'serial')
and not production.use_auto_consume_components_lots
and qty_compare_result > 0):
problem_tracked_products |= line.product_id
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
line.product_expected_qty_uom = 0
# 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_uom': line.product_uom_id.id,
'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,
'additional': True,
'picked': True,
})
if problem_tracked_products:
raise UserError(
@@ -78,10 +78,6 @@ class StockAssignSerialNumbers(models.TransientModel):
for workorder in production.workorder_ids:
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:
productions.button_mark_done()
+2 -2
View File
@@ -44,11 +44,11 @@
<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 eval="{'quantity_done': 5}"/>
<value eval="{'quantity': 5}"/>
</function>
<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 eval="{'quantity_done': 4}"/>
<value eval="{'quantity': 4}"/>
</function>
<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)
work_center_cost = 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_done > 0)
lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity > 0)
if finished_move:
finished_move.ensure_one()
for work_order in self.workorder_ids:
work_center_cost += work_order._cal_cost()
qty_done = finished_move.product_uom._compute_quantity(
finished_move.quantity_done, finished_move.product_id.uom_id)
extra_cost = self.extra_cost * qty_done
quantity = finished_move.product_uom._compute_quantity(
finished_move.quantity, finished_move.product_id.uom_id)
extra_cost = self.extra_cost * quantity
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
for byproduct in byproduct_moves:
if byproduct.cost_share == 0:
continue
byproduct_cost_share += byproduct.cost_share
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'):
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
def _get_backorder_mo_vals(self):
+1 -9
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, models
from odoo import models
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)
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):
distribution = self.raw_material_production_id.analytic_distribution
if distribution:
@@ -296,6 +296,6 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
mo.button_mark_done()
self.assertEqual(mo.state, 'done')
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.assertFalse(analytic_account.line_ids)
@@ -370,20 +370,20 @@ class TestMrpValuationStandard(TestMrpValuationCommon):
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_in.id,
'state': 'draft',
'immediate_transfer': False,
'move_line_ids': [(0, 0, {
'product_id': self.product1.id,
'qty_done': 1,
'quantity': 1,
'product_uom_id': self.product1.uom_id.id,
'location_id': self.customer_location.id,
'location_dest_id': self.stock_location.id,
})]
})
receipt.move_ids.picked = True
receipt.button_validate()
self.assertEqual(receipt.state, 'done')
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.value_svl, 1424)
@@ -434,7 +434,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0,
})
production_in.action_confirm()
move.quantity_done = 10
move.quantity = 10
move.picked = True
production_in.button_validate()
in_aml = self._get_production_cost_move_lines()
@@ -457,7 +458,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0,
})
production_out.action_confirm()
move.quantity_done = 10
move.quantity = 10
move.picked = True
production_out.button_validate()
out_aml = self._get_production_cost_move_lines() - in_aml
@@ -509,7 +511,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0,
})
production_in.action_confirm()
move.quantity_done = 10
move.quantity = 10
move.picked = True
production_in.button_validate()
in_aml = self.env['account.move.line'].search([
@@ -534,7 +537,8 @@ class TestMrpStockValuation(TestStockValuation):
'product_uom_qty': 10.0,
})
production_out.action_confirm()
move.quantity_done = 10
move.quantity = 10
move.picked = True
production_out.button_validate()
out_aml = self.env['account.move.line'].search([
@@ -82,7 +82,7 @@ class TestStockLot(TestStockCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 3
ml.quantity = 3
ml.lot_id = self.lot_good_apple
details_operation_form.save()
res = mo.button_mark_done()
@@ -105,7 +105,7 @@ class TestStockLot(TestStockCommon):
mo = mo_form.save()
details_operation_form = Form(mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations'))
with details_operation_form.move_line_ids.new() as ml:
ml.qty_done = 3
ml.quantity = 3
ml.lot_id = self.lot_expired_apple
details_operation_form.save()
res = mo.button_mark_done()
+5 -1
View File
@@ -44,6 +44,7 @@ class TestRepairTraceability(TestMrpCommon):
mo.lot_producing_id = ptrepair_lot
# Set component serial to B2
mo.move_raw_ids.move_line_ids.lot_id = ptremove_lot
mo.move_raw_ids.picked = True
mo.button_mark_done()
with Form(self.env['repair.order']) as ro_form:
@@ -56,6 +57,7 @@ class TestRepairTraceability(TestMrpCommon):
ro.action_validate()
ro.move_ids[0].lot_ids = ptremove_lot # Remove product Serial B2 from the product.
ro.action_repair_start()
ro.move_ids.picked = True
ro.action_repair_end()
# 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
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
mo2.button_mark_done()
@@ -92,6 +95,7 @@ class TestRepairTraceability(TestMrpCommon):
mo = mo_form.save()
mo.action_confirm()
mo.action_assign()
mo.move_raw_ids.picked = True
mo.button_mark_done()
return mo
@@ -128,8 +132,8 @@ class TestRepairTraceability(TestMrpCommon):
ro.action_validate()
ro.move_ids[0].lot_ids = sn_lot
ro.action_repair_start()
ro.move_ids.picked = True
ro.action_repair_end()
mo = produce_one(finished, component)
self.assertEqual(mo.state, 'done')
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:
move.move_line_ids = line_by_product.pop(move.product_id, self.env['stock.move.line'])
for product_id, lines in line_by_product.items():
qty = sum(line.product_uom_id._compute_quantity(line.qty_done, product_id.uom_id) for line in lines)
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['additional'] = True
production.move_raw_ids = [(0, 0, move)]
@@ -58,6 +58,7 @@ class MrpProduction(models.Model):
def subcontracting_record_component(self):
self.ensure_one()
self.move_raw_ids.picked = True
if not self._get_subcontract_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):
@@ -67,13 +68,12 @@ class MrpProduction(models.Model):
for sml in self.move_raw_ids.move_line_ids:
if sml.tracking != 'none' and not sml.lot_id:
raise UserError(_('You must enter a serial number for each line of %s', sml.product_id.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"))
consumption_issues = self._get_consumption_issues()
if 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
quantity_issues = self._get_quantity_produced_issues()
@@ -104,37 +104,30 @@ class MrpProduction(models.Model):
if subcontract_move_id:
quantity = self.qty_producing
if self.lot_producing_id:
move_lines = subcontract_move_id.move_line_ids.filtered(lambda ml: ml.lot_id == self.lot_producing_id or not ml.lot_id)
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:
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
for ml in move_lines:
rounding = ml.product_uom_id.rounding
if float_compare(quantity, 0, precision_rounding=rounding) <= 0:
break
quantity_to_process = min(quantity, ml.reserved_uom_qty - ml.qty_done)
quantity_to_process = min(quantity, ml.quantity)
quantity -= quantity_to_process
new_quantity_done = (ml.qty_done + quantity_to_process)
# on which lot of finished product
if float_compare(new_quantity_done, ml.reserved_uom_qty, precision_rounding=rounding) >= 0:
if float_compare(quantity_to_process, ml.quantity, precision_rounding=rounding) >= 0:
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,
})
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({
'reserved_uom_qty': new_qty_reserved,
'qty_done': 0
'quantity': quantity_to_process,
'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:
self.env['stock.move.line'].create({
@@ -143,18 +136,13 @@ class MrpProduction(models.Model):
'product_id': self.product_id.id,
'location_id': subcontract_move_id.location_id.id,
'location_dest_id': subcontract_move_id.location_dest_id.id,
'reserved_uom_qty': 0,
'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,
})
if not self._get_quantity_to_backorder():
ml_reserved = subcontract_move_id.move_line_ids.filtered(lambda ml:
float_is_zero(ml.qty_done, precision_rounding=ml.product_uom_id.rounding) and
not float_is_zero(ml.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.move_line_ids.filtered(lambda ml: not ml.picked).unlink()
subcontract_move_id._recompute_state()
def _subcontracting_filter_to_done(self):
+3 -15
View File
@@ -38,7 +38,7 @@ class StockMove(models.Model):
for move in self:
if not move.is_subcontract:
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
productions = move._get_subcontract_production()
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,
})
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')]
else:
action['views'] = [(self.env.ref('mrp_subcontracting.mrp_subcontracting_view_stock_move_nosuggest_operations').id, 'form')]
action['views'] = [(self.env.ref('mrp_subcontracting.mrp_subcontracting_view_stock_move_operations').id, 'form')]
return action
def action_show_subcontract_details(self):
@@ -135,10 +132,6 @@ class StockMove(models.Model):
'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):
for move in self:
if move.is_subcontract:
@@ -225,11 +218,6 @@ class StockMove(models.Model):
vals['location_id'] = self.location_id.id
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):
""" If the move is subcontracted then ignore the reservation. """
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):
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
if float_is_zero(quantity_to_remove, precision_rounding=move.product_uom.rounding):
continue
@@ -13,10 +13,6 @@ class StockPicking(models.Model):
_name = '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(
domain=['&', '|', ('location_dest_id.usage', '!=', 'production'), ('move_id.picking_code', '!=', 'outgoing'),
'|', ('package_level_id', '=', False), ('picking_type_entire_packs', '=', False)])
@@ -49,7 +45,7 @@ class StockPicking(models.Model):
productions = move._get_subcontract_production()
recorded_productions = productions.filtered(lambda p: p._has_been_recorded())
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')
if float_compare(recorded_qty, sm_done_qty, precision_digits=rounding) >= 0:
continue
@@ -59,7 +55,7 @@ class StockPicking(models.Model):
if len(production) > 1:
raise UserError(_("There shouldn't be multiple productions to record for the same subcontracted move."))
# 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:
change_qty = self.env['change.production.qty'].create({
'mo_id': production.id,
@@ -67,14 +63,15 @@ class StockPicking(models.Model):
})
change_qty.with_context(skip_activity=True).change_prod_qty()
# 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)
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):
if move_line.lot_id:
production.lot_producing_id = move_line.lot_id
production.qty_producing = production.product_qty
production._set_qty_producing()
productions[:len_amounts].subcontracting_has_been_recorded = True
for picking in self:
@@ -138,7 +135,7 @@ class StockPicking(models.Model):
'bom_id': bom.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,
'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,
'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
if move.move_orig_ids.production_id:
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 a subcontracted amount is decreased, don't create a MO that would be for a negative value.
continue
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.action_confirm()
@@ -40,11 +40,10 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# 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.display_action_record_components, 'hide')
@@ -72,7 +71,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)])
self.assertEqual(len(picking), 1)
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()
self.assertEqual(mo.state, 'done')
@@ -122,7 +122,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt = picking_form.save()
# Nothing should be tracked
@@ -149,7 +150,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)])
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()
self.assertEqual(mo.state, 'done')
@@ -189,7 +191,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt = picking_form.save()
# Nothing should be tracked
@@ -215,7 +218,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
# As well as a manufacturing order for `self.comp2`
comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)])
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()
self.assertEqual(mo.state, 'done')
@@ -254,7 +258,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt = picking_form.save()
warehouse = picking_receipt.picking_type_id.warehouse_id
@@ -322,7 +327,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt1 = picking_form.save()
# Create a receipt picking from the subcontractor2
@@ -331,7 +337,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_form.partner_id = subcontractor_partner2
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt2 = picking_form.save()
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_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
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()
mo = picking_receipt._get_subcontract_production()
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)
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_comp1.mapped('quantity_done')), 3.0)
self.assertEqual(sum(move_comp3.mapped('quantity_done')), 6.0)
self.assertEqual(sum(move_comp1.mapped('quantity')), 3.0)
self.assertEqual(sum(move_comp3.mapped('quantity')), 6.0)
move_finished = mo.move_finished_ids
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):
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_uom_qty = 5
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
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 = Form(self.env[backorder_wiz['res_model']].with_context(backorder_wiz['context'])).save()
backorder_wiz.process()
@@ -430,7 +437,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertTrue(backorder_mo)
self.assertEqual(backorder_mo.product_uom_qty, 2)
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()
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
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = 5
move.quantity = 5
move.picked = True
picking_receipt = picking_form.save()
picking_delivery = self.env['stock.move'].search([
@@ -484,7 +493,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# Check that a manufacturing order is created
@@ -502,7 +510,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
self.assertEqual(picking_receipt.display_action_record_components, 'facultative')
action = picking_receipt.action_record_components()
@@ -511,11 +518,11 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
mo_form.qty_producing = 1
with mo_form.move_line_raw_ids.edit(0) as ml:
self.assertEqual(ml.product_id, self.comp1)
self.assertEqual(ml.qty_done, 1)
ml.qty_done = 2
self.assertEqual(ml.quantity, 1)
ml.quantity = 2
mo = mo_form.save()
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
self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -536,7 +543,6 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
self.assertEqual(picking_receipt.display_action_record_components, 'facultative')
@@ -546,8 +552,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
mo_form.qty_producing = 1
with mo_form.move_line_raw_ids.edit(0) as ml:
self.assertEqual(ml.product_id, self.comp1)
self.assertEqual(ml.qty_done, 1)
ml.qty_done = 2
self.assertEqual(ml.quantity, 1)
ml.quantity = 2
mo = mo_form.save()
action_warning = mo.subcontracting_record_component()
warning = Form(self.env['mrp.consumption.warning'].with_context(**action_warning['context']))
@@ -559,7 +565,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
warning = warning.save()
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
self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -614,7 +620,8 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
def test_several_backorders(self):
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()
if isinstance(action, dict):
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_uom_qty = 5
picking = picking_form.save()
picking.action_reset_draft()
picking.action_confirm()
supply_picking = self.env['mrp.production'].search([('bom_id', '=', bom.id)]).picking_ids
@@ -657,8 +663,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
process_picking(backorder01, 1)
backorder02 = backorder01.backorder_ids
process_picking(backorder02, 0)
self.assertEqual(backorder02.move_ids.quantity_done, 2.75)
self.assertEqual(backorder02.move_ids.quantity, 2.75)
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 Form(picking) as picking_form:
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()
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')
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):
""" Test activate/archive subcontracting location rules."""
@@ -802,9 +808,9 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product
move.quantity_done = 1
move.quantity = 1
move.picked = True
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# We should be able to call the 'record_components' button
@@ -881,10 +887,10 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product
move.quantity_done = nb_finished_product
move.quantity = nb_finished_product
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# We shouldn't be able to call the 'record_components' button
self.assertEqual(picking_receipt.display_action_record_components, 'hide')
@@ -892,13 +898,13 @@ class TestSubcontractingTracking(TransactionCase):
wh = picking_receipt.picking_type_id.warehouse_id
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:
with move_details.move_line_nosuggest_ids.new() as ml:
ml.qty_done = 1
with move_details.move_line_ids.new() as ml:
ml.quantity = 1
ml.lot_name = lot_name
move_details.save()
picking_receipt.move_ids.picked = True
picking_receipt.button_validate()
# Check the created manufacturing order
# Should have one mo by serial number
@@ -929,9 +935,8 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product
move.quantity_done = todo_nb
move.quantity = todo_nb
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# 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
self.assertEqual(picking_receipt.display_action_record_components, 'hide')
picking_receipt.move_ids.picked = True
picking_receipt.button_validate()
self.assertEqual(mo.state, 'done')
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_uom_qty = todo_nb
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
@@ -1043,9 +1048,7 @@ class TestSubcontractingTracking(TransactionCase):
# Process the delivery of the components
compo_picking = mo.picking_ids
compo_picking.action_assign()
wizard_data = compo_picking.button_validate()
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
wizard.process()
compo_picking.button_validate()
for qty in [3, 1]:
# Record the receiption of <qty> finished products
@@ -1108,9 +1111,9 @@ class TestSubcontractingTracking(TransactionCase):
picking_form.partner_id = self.subcontractor_partner1
with picking_form.move_ids_without_package.new() as move:
move.product_id = finished_product
move.quantity_done = todo_nb
move.quantity = todo_nb
move.picked = True
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
@@ -1118,16 +1121,13 @@ class TestSubcontractingTracking(TransactionCase):
# Process the delivery of the components
compo_picking = mo.picking_ids
compo_picking.action_assign()
wizard_data = compo_picking.button_validate()
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
wizard.process()
compo_picking.button_validate()
picking_receipt = self.env['stock.picking'].search([('partner_id', '=', self.subcontractor_partner1.id), ('state', '!=', 'done')])
for sn in finished_serials:
# Record the production of each serial number separately
action = picking_receipt.action_record_components()
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.qty_producing = 1
mo_form.lot_producing_id = sn
@@ -1210,7 +1210,6 @@ class TestSubcontractingPortal(TransactionCase):
move.product_id = self.finished_product
move.product_uom_qty = 2
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# Using the subcontractor (portal user)
@@ -1259,7 +1258,7 @@ class TestSubcontractingPortal(TransactionCase):
ml.product_id = self.comp1_sn
ml.lot_id = serial3
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
with self.assertRaises(AccessError):
with mo_form.move_line_raw_ids.new() as ml:
@@ -1277,11 +1276,11 @@ class TestSubcontractingPortal(TransactionCase):
# Validation from the backend user
picking_receipt.button_validate()
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[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[2].qty_done, 2)
self.assertEqual(mo.move_line_raw_ids[2].quantity, 2)
class TestSubcontractingSerialMassReceipt(TransactionCase):
def setUp(self):
@@ -1324,23 +1323,21 @@ class TestSubcontractingSerialMassReceipt(TransactionCase):
move.product_id = self.finished
move.product_uom_qty = sum(quantities)
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
# Process the delivery of the components
picking_deliver = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]).picking_ids
picking_deliver.action_assign()
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
for quantity in quantities:
# Receive <quantity> finished products
picking_receipt.action_clear_quantities_to_zero()
Form(self.env['stock.assign.serial'].with_context(
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_count=quantity,
)).save().generate_serial_numbers()
picking_receipt.move_ids.picked = True
wizard_data = picking_receipt.button_validate()
if wizard_data is not True:
# Create backorder
@@ -1361,14 +1358,17 @@ class TestSubcontractingSerialMassReceipt(TransactionCase):
picking_form.partner_id = self.subcontractor
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = quantity
move.product_uom_qty = quantity
picking_receipt = picking_form.save()
picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# Receive finished products
Form(self.env['stock.assign.serial'].with_context(
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_count=quantity,
)).save().generate_serial_numbers()
picking_receipt.move_ids.picked = True
picking_receipt.button_validate()
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)
@@ -54,9 +54,9 @@ class TestSubcontractingPortalUi(HttpCase):
picking_form.partner_id = self.partner_portal
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished_product
move.quantity_done = 2
move.quantity = 2
move.picked = True
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
picking_receipt.action_confirm()
self.start_tour("/my/productions", 'subcontracting_portal_tour', login="georges1")
@@ -23,10 +23,7 @@
invisible="tracking not in ('serial', 'lot')"
required="tracking in ('serial', 'lot')"
context="{'default_product_id': product_id, 'default_company_id': company_id}"/>
<field name="reserved_uom_qty" readonly="1" force_save="1"/>
<field name="qty_done"
decoration-warning="reserved_uom_qty &lt; qty_done"
decoration-success="reserved_uom_qty == qty_done"/>
<field name="quantity"/>
<field name="product_uom_id" groups="uom.group_uom"/>
</tree>
</field>
@@ -70,18 +67,7 @@
</xpath>
</field>
</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">
<field name="name">mrp.subcontracting.move.form.view</field>
<field name="model">stock.move</field>
@@ -105,7 +91,7 @@
<group>
<field name="order_finished_lot_id"/>
<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>
<field name="move_line_ids"
readonly="state in ['done', 'cancel']"
@@ -121,7 +107,7 @@
<field name="location_id" column_invisible="True"/>
<field name="location_dest_id" column_invisible="True"/>
<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"/>
</tree>
</field>
@@ -152,7 +138,7 @@
<field name="model">stock.move</field>
<field name="priority">1000</field>
<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="sequence" 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="product_id" required="1" readonly="state == 'done'"/>
<field name="order_finished_lot_id"/>
<field name="reserved_availability" invisible="is_done" string="Reserved"/>
<field name="quantity_done" string="Consumed" readonly="1"/>
<field name="quantity" string="Consumed" readonly="1"/>
<field name="product_uom" groups="uom.group_uom"/>
</tree>
</field>
@@ -32,7 +32,7 @@
<field name="product_packaging_id" groups="product.group_stock_packaging"/>
<field name="product_uom_qty" string="Demand" readonly="1"/>
<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"/>
<button name="action_show_details" type="object" icon="fa-list" title="Details"
invisible="not show_details_visible" context="{'is_subcontracting_portal': 1}"/>
@@ -8,7 +8,7 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production'
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
last_done_receipt = finished_move.move_dest_ids.filtered(lambda m: m.state == 'done')[-1:]
if last_done_receipt.is_subcontract:
@@ -36,7 +36,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
})
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 10.0
move1.move_line_ids.quantity = 10.0
move1.picked = True
move1._action_done()
move2 = self.env['stock.move'].create({
'name': 'IN 10 units @ 20.00 per unit',
@@ -49,7 +50,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
})
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 10.0
move2.move_line_ids.quantity = 10.0
move2.picked = True
move2._action_done()
all_amls_ids = self.env['account.move.line'].search([]).ids
@@ -66,7 +68,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.action_confirm()
# Suppose the additional cost changes:
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()
mo = picking_receipt._get_subcontract_production()
@@ -106,7 +109,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.move_ids.price_unit = 0
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()
mo = picking_receipt._get_subcontract_production()
@@ -168,7 +172,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
})
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 10.0
move1.move_line_ids.quantity = 10.0
move1.picked = True
move1._action_done()
move2 = self.env['stock.move'].create({
'name': 'IN 10 units @ 20.00 per unit',
@@ -181,7 +186,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
})
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 10.0
move2.move_line_ids.quantity = 10.0
move2.picked = True
move2._action_done()
all_amls_ids = self.env['account.move.line'].search([]).ids
@@ -195,7 +201,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt = picking_form.save()
picking_receipt.move_ids.price_unit = 30.0
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()
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
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.quantity_done = todo_nb
move.quantity = todo_nb
picking_receipt = picking_form.save()
picking_receipt.action_reset_draft()
# Mimic the extra cost on the po line
picking_receipt.move_ids.price_unit = 50
picking_receipt.action_confirm()
picking_receipt.action_clear_quantities_to_zero()
# We should be able to call the 'record_components' button
self.assertTrue(picking_receipt.display_action_record_components)
@@ -275,7 +282,7 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
mo.subcontracting_record_component()
# We should not be able to call the 'record_components' button
picking_receipt.move_ids.picked = True
picking_receipt.button_validate()
f_layers = self.finished.stock_valuation_layer_ids
@@ -307,7 +314,6 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 10
receipt = receipt_form.save()
receipt.action_reset_draft()
# add an extra cost
receipt.move_ids.price_unit = 50
receipt.action_confirm()
@@ -371,7 +377,8 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.move_ids.price_unit = 15.0
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()
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
@@ -69,8 +69,7 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
po.button_confirm()
delivery = po.picking_ids
res = delivery.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
delivery.button_validate()
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
@@ -104,7 +103,8 @@ class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon)
return_wizard = return_form.save()
return_id, _ = return_wizard._create_returns()
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()
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_id, _ = return_wizard._create_returns()
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()
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)
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()
self.assertEqual(delivery.state, 'done')
@@ -259,7 +260,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
return_picking_id, _pick_type_id = return_wizard._create_returns()
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()
self.assertEqual(delivery_return01.state, 'done')
@@ -276,7 +278,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
return_picking_id, _pick_type_id = return_wizard._create_returns()
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()
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.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()
self.assertEqual(po.order_line.qty_received, 1.0)
@@ -57,16 +57,12 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
# Deliver the first one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[0])
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Deliver the third one
picking = sale_order.picking_ids.filtered(lambda p: p.partner_id == partners[2])
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 0)
# Cancel the second one
@@ -106,9 +102,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(sale_order.order_line.qty_delivered, 0.0)
picking = sale_order.picking_ids
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(sale_order.order_line.qty_delivered, 1.0)
for case in ['return', 'deliver again']:
@@ -120,9 +114,7 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
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)
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
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):
@@ -166,7 +158,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
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()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
@@ -179,19 +172,22 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
res = wizard.create_returns()
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()
self.assertEqual(sale_order.order_line.qty_delivered, 0.0, "Delivered components: 0/4")
picking02 = picking01.backorder_ids
picking02.move_ids.quantity_done = 1
picking02.move_ids.quantity = 1
picking02.move_ids.picked = True
action = picking02.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.0, "Delivered components: 1/4")
picking03 = picking02.backorder_ids
picking03.move_ids.quantity_done = 1
picking03.move_ids.quantity = 1
picking03.move_ids.picked = True
picking03.button_validate()
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()
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()
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()
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()
self.assertEqual(sale_order.order_line.qty_delivered, 1, "Delivered components: 4/4")
@@ -286,7 +284,8 @@ class TestSaleDropshippingFlows(TestMrpSubcontractingCommon):
sale_order.action_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[1].button_validate()
@@ -98,7 +98,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
# check that a neg qty can't proprogate once receipt is done
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()
self.assertEqual(receipt.state, 'done')
self.assertEqual(sub_mos.state, 'done')
@@ -127,7 +128,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
self.assertTrue(mo)
receipt = po.picking_ids
receipt.move_ids.quantity_done = 10
receipt.move_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate()
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_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()
self.assertEqual(self.finished2.qty_available, 7.0)
@@ -169,7 +172,8 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
self.assertTrue(mo)
receipt = po.picking_ids
receipt.move_ids.quantity_done = 10
receipt.move_ids.quantity = 10
receipt.move_ids.picked = True
receipt.button_validate()
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_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()
self.assertEqual(self.finished2.qty_available, 7.0)
@@ -276,12 +281,14 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
action = po.action_view_subcontracting_resupply()
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()
action = po.action_view_picking()
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()
action = po.action_create_invoice()
@@ -308,16 +315,15 @@ class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon):
'order_line': [Command.create({
'name': self.finished.name,
'product_id': self.finished.id,
'product_uom_qty': 10,
'product_qty': 10,
'product_uom': self.finished.uom_id.id,
'price_unit': 1,
})],
})
purchase.button_confirm()
# receive product
receipt = purchase.picking_ids
receipt.move_ids.quantity_done = 10
receipt.move_ids.picked = True
receipt.button_validate()
# create bill
purchase.action_create_invoice()
+2 -2
View File
@@ -804,7 +804,7 @@ class PosSession(models.Model):
signed_product_qty = move.product_qty
if move._is_in():
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)
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)
@@ -833,7 +833,7 @@ class PosSession(models.Model):
signed_product_qty = move.product_qty
if move._is_in():
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)
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)
+6 -62
View File
@@ -81,7 +81,6 @@ class StockPicking(models.Model):
'picking_type_id': self.picking_type_id.id,
'product_id': first_line.product_id.id,
'product_uom_qty': abs(sum(order_lines.mapped('qty'))),
'state': 'draft',
'location_id': self.location_id.id,
'location_dest_id': self.location_dest_id.id,
'company_id': self.company_id.id,
@@ -97,6 +96,7 @@ class StockPicking(models.Model):
moves = self.env['stock.move'].create(move_vals)
confirmed_moves = moves._action_confirm()
confirmed_moves._add_mls_related_to_order(lines, are_qties_done=True)
confirmed_moves.picked = True
self._link_owner_on_return_picking(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
returnable_qty_by_product = {}
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 keys in returnable_qty_by_product:
if move.product_id.id == keys[0] and keys[1] and returnable_qty_by_product[keys] > 0:
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):
@@ -198,22 +198,6 @@ class StockMove(models.Model):
lines_data[product_id].update({'order_lines': self.env['pos.order.line'].concat(*olines)})
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):
''' 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):
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_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))
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
existing_lots = moves_remaining._create_production_lots_for_pos_order(related_order_lines)
move_lines_to_create = []
mls_qties = []
if are_qties_done:
for move in moves_remaining:
for line in lines_data[move.product_id.id]['order_lines']:
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})
move.quantity = move.product_uom_qty
else:
for move in moves_remaining:
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(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()
# I test that the generated journal entries are correct.
@@ -289,7 +289,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo()
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 2
move1.move_line_ids.quantity = 2
move1.picked = True
move1._action_done()
move2 = self.env['stock.move'].create({
@@ -303,7 +304,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo()
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 1
move2.move_line_ids.quantity = 1
move2.picked = True
move2._action_done()
# open a session
@@ -349,7 +351,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo()
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 2
move1.move_line_ids.quantity = 2
move1.picked = True
move1._action_done()
move2 = self.env['stock.move'].create({
@@ -363,7 +366,8 @@ class TestPosMargin(TestPoSCommon):
}).sudo()
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 1
move2.move_line_ids.quantity = 1
move2.picked = True
move2._action_done()
# open a session
+8 -7
View File
@@ -56,7 +56,8 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
})
sale_order.action_confirm()
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()
wizard = Form(self.env[action['res_model']].with_context(action['context']))
wizard.save().process()
@@ -75,9 +76,9 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
#assert that sales order qty are correctly updated
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.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.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):
""" 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()
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[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.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.main_pos_config.current_session_id.close_session_from_ui()
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[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(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))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
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.
@@ -61,7 +61,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
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):
@@ -90,7 +90,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, False)
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))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
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.
@@ -137,7 +137,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
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):
@@ -160,7 +160,7 @@ class TestStockLot(StockGenerateCommon):
self.assertEqual(len(move.move_line_ids), len(list_lot_and_qty))
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.qty_done, 1)
self.assertEqual(move_line.quantity, 1)
self.assertEqual(move_line.expiration_date, list_lot_and_qty[i]["datetime"])
@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(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[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[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[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[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[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[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[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))
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)
self._import_lots(lot_name, move)
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))
+16 -14
View File
@@ -51,7 +51,6 @@ class TestStockLot(TestStockCommon):
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
'state': 'draft',
'immediate_transfer': False,
})
move_a = self.MoveObj.create({
@@ -70,10 +69,11 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments.
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
# Transfer Incoming Shipment.
move_a.picked = True
picking_in._action_done()
# run scheduled tasks
@@ -148,7 +148,6 @@ class TestStockLot(TestStockCommon):
'picking_type_id': self.picking_type_in,
'location_id': self.supplier_location,
'state': 'draft',
'immediate_transfer': False,
'location_dest_id': self.stock_location})
move_b = self.MoveObj.create({
@@ -166,7 +165,7 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments.
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
# Transfer Incoming Shipment.
@@ -197,7 +196,6 @@ class TestStockLot(TestStockCommon):
'picking_type_id': self.picking_type_in,
'location_id': self.supplier_location,
'state': 'draft',
'immediate_transfer': False,
'location_dest_id': self.stock_location})
move_c = self.MoveObj.create({
@@ -215,7 +213,7 @@ class TestStockLot(TestStockCommon):
# Replace pack operation of incoming shipments.
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
# Transfer Incoming Shipment.
@@ -313,12 +311,12 @@ class TestStockLot(TestStockCommon):
# Defines a date during the receipt.
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.expiration_date = expiration_date
line.qty_done = 4
move = move_form.save()
move.picked = True
receipt._action_done()
# Get back the lot created when the picking was done...
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')
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.apple_product
move.quantity_done = 4
move.quantity = 4
move.picked = True
receipt = picking_form.save()
# Defines a date during the receipt.
@@ -424,8 +423,9 @@ class TestStockLot(TestStockCommon):
'lot_id': good_lot.id,
'product_id': self.apple_product.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()
# Validate a delivery for good products must not raise anything.
self.assertEqual(res, True)
@@ -449,7 +449,7 @@ class TestStockLot(TestStockCommon):
'lot_id': good_lot.id,
'product_id': self.apple_product.id,
'product_uom_id': self.apple_product.uom_id.id,
'qty_done': 4,
'quantity': 4,
}), (0, 0, {
'company_id': self.env.company.id,
'location_id': delivery_2.move_ids.location_id.id,
@@ -457,8 +457,9 @@ class TestStockLot(TestStockCommon):
'lot_id': expired_lot_1.id,
'product_id': self.apple_product.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()
# Validate a delivery containing expired products must raise a confirmation wizard.
self.assertNotEqual(res, True)
@@ -482,8 +483,9 @@ class TestStockLot(TestStockCommon):
'lot_id': expired_lot_1.id,
'product_id': self.apple_product.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()
# Validate a delivery containing expired products must raise a confirmation wizard.
self.assertNotEqual(res, True)
@@ -535,7 +537,7 @@ class TestStockLot(TestStockCommon):
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
'product_id': self.apple_product.id,
'qty_done': 3,
'quantity': 3,
'product_uom_id': self.apple_product.uom_id.id,
'lot_id': lot.id,
'company_id': self.env.company.id,
@@ -11,17 +11,6 @@
</xpath>
</field>
</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">
<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
# suggest biggest suitable packaging
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')
def _onchange_product_packaging_id(self):
@@ -60,9 +60,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
po = po_form.save()
po.button_confirm()
action = po.picking_ids.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
po.picking_ids.button_validate()
action = po.action_create_invoice()
invoice = self.env['account.move'].browse(action['res_id'])
@@ -133,7 +131,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
po.button_confirm()
receipt = po.picking_ids
receipt.move_line_ids.qty_done = 1
receipt.move_line_ids.quantity = 1
receipt.button_validate()
self.assertEqual(receipt.state, 'done')
@@ -156,7 +154,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
})],
})
delivery.action_confirm()
delivery.move_ids.move_line_ids.qty_done = 1
delivery.move_ids.move_line_ids.quantity = 1
delivery.button_validate()
self.assertEqual(component01.stock_valuation_layer_ids.mapped('value'), [25, -25])
@@ -173,7 +171,7 @@ class TestAngloSaxonValuationPurchaseMRP(TransactionCase):
wizard = wizard_form.save()
action = wizard.create_returns()
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()
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())
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):
""" Helper to check expected quantities based on a dict following this structure :
@@ -214,7 +215,7 @@ class TestPurchaseMrpFlow(TransactionCase):
move._action_confirm()
move._action_assign()
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()
def test_kit_component_cost(self):
@@ -232,7 +233,6 @@ class TestPurchaseMrpFlow(TransactionCase):
line.price_unit = 1260
po = po.save()
po.button_confirm()
po.picking_ids.action_set_quantities_to_reservation()
po.picking_ids.button_validate()
# Unit price equaly dived among bom lines (cost share not set)
@@ -285,7 +285,6 @@ class TestPurchaseMrpFlow(TransactionCase):
po = po.save()
po.button_confirm()
po.picking_ids.action_set_quantities_to_reservation()
po.picking_ids.button_validate()
layer = po.picking_ids.move_ids.stock_valuation_layer_ids
@@ -349,7 +348,7 @@ class TestPurchaseMrpFlow(TransactionCase):
# Process only 7 units of each component
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
pick = po.picking_ids[0]
@@ -456,9 +455,7 @@ class TestPurchaseMrpFlow(TransactionCase):
return_pick = self.env['stock.picking'].browse(res['res_id'])
# Process all components and validate the picking
wiz_act = return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
return_pick.button_validate()
# Now quantity received should be 3 again
self.assertEqual(order_line.qty_received, 3)
@@ -475,7 +472,7 @@ class TestPurchaseMrpFlow(TransactionCase):
# Process all components except one of each
for move in return_of_return_pick.move_ids:
move.write({
'quantity_done': expected_quantities[move.product_id] - 1,
'quantity': expected_quantities[move.product_id] - 1,
'to_refund': True
})
@@ -283,7 +283,7 @@ class TestPurchaseRequisitionStock(TestPurchaseRequisitionCommon):
self.env['procurement.group'].run_scheduler()
# Find the internal move (Input -> Stock) that has been generated
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
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.")
@@ -308,8 +308,9 @@ class TestPurchaseRequisitionStock(TestPurchaseRequisitionCommon):
# Set all qty as done and validate the alternative PO's picking
in_picking = alt_po.picking_ids
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()
# 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.")
+4 -6
View File
@@ -223,8 +223,6 @@ class PurchaseOrder(models.Model):
'location_dest_id': self._get_destination_location(),
'location_id': self.partner_id.property_stock_supplier.id,
'company_id': self.company_id.id,
'state': 'draft',
'immediate_transfer': False,
}
def _create_picking(self):
@@ -339,7 +337,7 @@ class PurchaseOrderLine(models.Model):
if move.state == 'done':
if move._is_purchase_return():
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():
# 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
@@ -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:
pass
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.qty_received = total
@@ -523,10 +521,10 @@ class PurchaseOrderLine(models.Model):
qty = 0.0
outgoing_moves, incoming_moves = self._get_outgoing_incoming_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')
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')
return qty
+4 -4
View File
@@ -26,7 +26,7 @@ class ResPartner(models.Model):
('order_id.state', 'in', ['done', 'purchase']),
('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([
('purchase_line_id', 'in', order_lines.ids),
('state', '=', 'done')])
@@ -34,13 +34,13 @@ class ResPartner(models.Model):
order_lines.read(['date_planned', 'partner_id', 'product_uom_qty'], load='')
moves.read(['purchase_line_id', 'date'], load='')
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')):
lines_qty_done[move.purchase_line_id.id] += qty_done
for move, quantity in zip(moves, moves.mapped('quantity')):
lines_quantity[move.purchase_line_id.id] += quantity
partner_dict = {}
for line in order_lines:
on_time, ordered = partner_dict.get(line.partner_id, (0, 0))
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)
seen_partner = self.env['res.partner']
for partner, numbers in partner_dict.items():
+1 -1
View File
@@ -46,7 +46,7 @@ class StockMove(models.Model):
order = line.order_id
received_qty = line.qty_received
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:
move_layer = line.move_ids.sudo().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(m.product_qty) AS qty_total,
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
END) AS qty_on_time
FROM stock_move m
@@ -100,7 +100,8 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon):
stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
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()
# Refund the invoice
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# 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.tests import tagged, Form
from odoo.tests import tagged
import time
@@ -52,8 +52,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# 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')
@@ -76,8 +75,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
purchase_order_2.button_confirm()
# Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0]
res = picking.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
picking.button_validate()
# 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')
@@ -97,10 +95,8 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
})
# Assign this outgoing shipment and process the delivery
outgoing_shipment.action_reset_draft()
outgoing_shipment.action_assign()
res = outgoing_shipment.button_validate()
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
outgoing_shipment.button_validate()
# 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!')
@@ -124,8 +120,7 @@ class TestAveragePrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 3 in grams
picking = purchase_order_3.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# 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')
@@ -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')
picking = purchase_order.picking_ids[0]
picking.action_set_quantities_to_reservation()
picking.move_ids.picked = True
# clear cash to ensure access rights verification
self.env.invalidate_all()
picking.with_user(self.res_users_stock_user).button_validate()
@@ -55,8 +55,7 @@ class TestCreatePicking(common.TestProductCommon):
# Validate first shipment
self.picking = self.po.picking_ids[0]
for ml in self.picking.move_line_ids:
ml.qty_done = ml.reserved_uom_qty
self.picking.move_ids.picked = True
self.picking._action_done()
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
picking.action_confirm()
picking.move_ids.quantity_done = 100.0
picking.move_ids.quantity = 100.0
picking.move_ids.picked = True
picking.button_validate()
# mts move will be automatically assigned
@@ -219,8 +219,11 @@ class TestCreatePicking(common.TestProductCommon):
self.assertEqual(move2.product_qty, 12)
# deliver everything
move1.quantity_done = 24
move2.quantity_done = 1
move1.quantity = 24
move1.picked = True
move2.quantity = 1
move2.picked = True
po.picking_ids.button_validate()
# check the delivered quantity
@@ -273,7 +276,6 @@ class TestCreatePicking(common.TestProductCommon):
'procure_method': 'make_to_order',
'picking_id': customer_picking.id,
})
customer_picking.action_reset_draft()
customer_picking.action_confirm()
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.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_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()
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):
""" 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)
# deliver everything
move1.quantity_done = 2.0
move1.quantity = 2.0
move1.picked = True
po.picking_ids.button_validate()
# check the delivered quantity
@@ -420,7 +425,6 @@ class TestCreatePicking(common.TestProductCommon):
'picking_id': delivery_order.id,
})
delivery_order.action_reset_draft()
delivery_order.action_confirm()
# find created po the product
purchase_order = self.env['purchase.order'].search([('partner_id', '=', partner.id)])
@@ -471,7 +475,8 @@ class TestCreatePicking(common.TestProductCommon):
po.button_approve()
po.picking_ids.move_line_ids.write({
'qty_done': 1.0
'quantity': 1.0,
'picked': True
})
po.picking_ids.button_validate()
@@ -486,7 +491,8 @@ class TestCreatePicking(common.TestProductCommon):
po.button_confirm()
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
backorder_wizard_dict = first_picking.button_validate()
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()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
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()
self.assertEqual(po.order_line.qty_received, 3)
@@ -672,12 +679,13 @@ class TestCreatePicking(common.TestProductCommon):
line.product_qty = 10
po = po_form.save()
po.button_approve()
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()
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()
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()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
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()
push_pick = return_pick.move_ids.move_dest_ids.picking_id
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()
self.assertEqual(po.order_line.qty_received, 8)
+15 -27
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# 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.tests import tagged, Form
from odoo.tests import tagged
import time
@@ -50,8 +50,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1 and set date
picking = purchase_order_1.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Check the standard price of the product (fifo icecream), that should have changed
# because the unit cost of the purchase order is 50
@@ -75,8 +74,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order 2
picking = purchase_order_2.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Check the standard price of the product, that should have not changed because we
# still have icecream in stock
@@ -99,12 +97,10 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
})
# I assign this outgoing shipment
outgoing_shipment.action_reset_draft()
outgoing_shipment.action_assign()
# Process the delivery of the outgoing shipment
res = outgoing_shipment.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
outgoing_shipment.button_validate()
# Check stock value became 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
outgoing_shipment_uom.action_reset_draft()
outgoing_shipment_uom.action_assign()
# Process the delivery of the outgoing shipment
res = outgoing_shipment_uom.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
outgoing_shipment_uom.button_validate()
# Check stock valuation and qty in stock
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()
# Process the reception of purchase order with USD
picking = purchase_order_usd.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Create delivery order of 49.5 kg
outgoing_shipment_cur = self.env['stock.picking'].create({
@@ -186,12 +179,10 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
})
# I assign this outgoing shipment
outgoing_shipment_cur.action_reset_draft()
outgoing_shipment_cur.action_assign()
# Process the delivery of the outgoing shipment
res = outgoing_shipment_cur.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
outgoing_shipment_cur.button_validate()
# Do a delivery of an extra 10 kg
outgoing_shipment_ret = self.env['stock.picking'].create({
@@ -209,10 +200,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
})
# I assign this outgoing shipment
outgoing_shipment_ret.action_reset_draft()
outgoing_shipment_ret.action_assign()
res = outgoing_shipment_ret.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
outgoing_shipment_ret.button_validate()
# 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.')
@@ -248,7 +237,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the delivery of the first outgoing shipment
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()
# Check qty available = -100
@@ -274,7 +264,8 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the delivery of the outgoing shipments
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()
# Check qty available = -500
@@ -297,8 +288,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order neg
picking = purchase_order_neg.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Receive purchase order with 600 kg FIFO Ice Cream at 80 euro/kg
purchase_order_neg2 = self.env['purchase.order'].create({
@@ -317,8 +307,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
# Process the reception of purchase order neg2
picking = purchase_order_neg2.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
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')
@@ -362,8 +351,7 @@ class TestFifoPrice(ValuationReconciliationTestCommon):
self.assertEqual(purchase_order.state, 'purchase')
picking = purchase_order.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
self.assertEqual(super_product.standard_price, 0.035)
self.assertEqual(super_product.value_svl, 35.0)
@@ -58,8 +58,7 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Process the reception of purchase order 1
picking = purchase_order_1.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Check the standard price of the product (fifo icecream)
self.assertAlmostEqual(product_fiforet_icecream.standard_price, 50)
@@ -67,8 +66,7 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Confirm the second purchase order
purchase_order_2.button_confirm()
picking = purchase_order_2.picking_ids[0]
res = picking.button_validate()
Form(self.env[res['res_model']].with_context(res['context'])).save().process()
picking.button_validate()
# Return the goods of purchase order 2
picking = purchase_order_2.picking_ids[0]
@@ -81,7 +79,8 @@ class TestFifoReturns(ValuationReconciliationTestCommon):
# Important to pass through confirmation and assignation
return_picking = self.env['stock.picking'].browse(return_picking_id)
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()
# 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.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.picking_out.action_reset_draft()
self.picking_out.action_confirm()
# Find PO related to picking.
@@ -82,7 +81,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
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.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()
# Find PO related to picking.
@@ -113,7 +111,6 @@ class TestMoveCancelPropagation(PurchaseTestCommon):
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.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()
# 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.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.picking_out.action_reset_draft()
self.picking_out.action_confirm()
# 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.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.picking_out.action_reset_draft()
self.picking_out.action_confirm()
# 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.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.picking_out.action_reset_draft()
self.picking_out.action_confirm()
# 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.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.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.incoming_picking_count, 1, 'Purchase: one picking should be created"')
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.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'])
# 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()
# Check Received quantity
@@ -188,9 +190,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po1.button_confirm()
picking = po1.picking_ids
wiz_act = picking.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
picking.button_validate()
# Return 5 units
stock_return_picking_form = Form(self.env['stock.return.picking'].with_context(
@@ -206,9 +206,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
})
res = return_wiz.create_returns()
return_pick = self.env['stock.picking'].browse(res['res_id'])
wiz_act = return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
return_pick.button_validate()
self.assertEqual(po1.order_line.qty_received, 5)
@@ -246,9 +244,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
)
# receive products
wiz_act = po.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
po.picking_ids.button_validate()
# update second line
old_date = po.order_line[1].date_planned
@@ -300,12 +296,13 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm()
picking = po.picking_ids[0]
# 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()
# No 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
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)
# Create a purchase order with 80% qty received for company B
@@ -319,12 +316,13 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm()
picking = po.picking_ids[0]
# 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()
# No 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
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)
# Tricky corner case
@@ -372,7 +370,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
})
po.button_confirm()
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()
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(len(purchase_order.picking_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
@@ -422,7 +421,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
purchase_order.button_confirm()
self.assertEqual(len(purchase_order.picking_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):
self.env.user.write({'company_id': self.company_data['company'].id})
@@ -441,19 +440,19 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
_purchase_order.button_confirm()
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 = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process()
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 = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process()
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 = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
backorder_wizard.process()
@@ -604,7 +603,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
picking = po.picking_ids
self.assertEqual(po.state, "purchase")
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()
self.env[res_dict['res_model']].with_context(res_dict['context']).process()
backorder = picking.backorder_ids
@@ -623,7 +623,8 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
line.product_qty = 5
po = po_form.save()
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()
self.assertEqual(po.picking_ids.state, 'done')
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)
def test_po_edit_after_receive(self):
po_1 = self.env['purchase.order'].create(self.po_vals)
po_1.button_confirm()
po_1.picking_ids.move_ids.quantity_done = 5
po_1.picking_ids.button_validate()
self.assertEqual(po_1.picking_ids.move_ids.mapped('product_uom_qty'), [5.0, 5.0])
po_1.with_context(import_file=True).order_line[0].product_qty = 10
self.assertEqual(po_1.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)
self.po = self.env['purchase.order'].create(self.po_vals)
self.po.button_confirm()
self.po.picking_ids.move_ids.quantity = 5
self.po.picking_ids.move_ids.picked = True
self.po.picking_ids.button_validate()
self.assertEqual(self.po.picking_ids.move_ids.mapped('product_uom_qty'), [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])
def test_receive_returned_product_without_po_update(self):
"""
@@ -670,7 +651,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
po.button_confirm()
receipt01 = po.picking_ids
receipt01.move_ids.quantity_done = 5
receipt01.move_ids.quantity = 5
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()
@@ -678,7 +659,7 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
res = wizard.create_returns()
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()
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()
receipt02 = self.env['stock.picking'].browse(res['res_id'])
receipt02.move_ids.quantity_done = 5
receipt02.move_ids.quantity = 5
receipt02.button_validate()
self.assertEqual(po.order_line[0].qty_received, 5)
@@ -41,7 +41,10 @@ class TestAccruedPurchaseStock(AccountTestInvoicingCommon):
def test_purchase_stock_accruals(self):
# receive 2 on 2020-01-02
pick = self.purchase_order.picking_ids
pick.move_ids.write({'quantity_done': 2})
pick.move_ids.write({
'quantity': 2,
'picked': True,
})
pick.button_validate()
wiz_act = pick.button_validate()
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
pick = pick.copy()
pick.move_ids.write({'quantity_done': 3})
pick.move_ids.write({'quantity': 3, 'picked': True})
wiz_act = pick.button_validate()
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):
# deliver 2 on 2020-01-02
pick = self.purchase_order.picking_ids
pick.move_ids.write({'quantity_done': 2})
pick.move_ids.write({'quantity': 2, 'picked': True})
pick.button_validate()
wiz_act = pick.button_validate()
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
pick = pick.copy()
pick.move_ids.write({'quantity_done': 3})
pick.move_ids.write({'quantity': 3, 'picked': True})
wiz_act = pick.button_validate()
pick.move_ids.write({'date': fields.Date.to_date('2020-01-06')})
@@ -43,9 +43,7 @@ class TestPurchaseStockReports(TestReportsCommon):
# Receives 5 products.
receipt = po.picking_ids
res_dict = receipt.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
receipt.button_validate()
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_purchase_qty = docs['draft_purchase_qty']
@@ -118,9 +116,7 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt = po.picking_ids
# Receives 4 products.
res_dict = receipt.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
receipt.button_validate()
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_purchase_qty = docs['draft_purchase_qty']
@@ -227,9 +223,10 @@ class TestPurchaseStockReports(TestReportsCommon):
'location_dest_id': receipt_move.location_dest_id.id,
'product_id': self.product.id,
'product_uom_id': uom_12.id,
'qty_done': 1,
'quantity': 1,
'picking_id': receipt.id,
})]
receipt.move_ids.picked = True
receipt.button_validate()
data = self.env['vendor.delay.report'].read_group(
@@ -267,16 +264,17 @@ class TestPurchaseStockReports(TestReportsCommon):
'location_dest_id': child_loc_01.id,
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'qty_done': 6,
'quantity': 6,
'picking_id': receipt.id,
}), (0, 0, {
'location_id': receipt_move.location_id.id,
'location_dest_id': child_loc_02.id,
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'qty_done': 4,
'quantity': 4,
'picking_id': receipt.id,
})]
receipt.move_ids.picked = True
receipt.button_validate()
data = self.env['vendor.delay.report'].read_group(
@@ -306,7 +304,7 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6
receipt01_move.quantity = 6
action = receipt01.button_validate()
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)
receipt02 = receipt01.backorder_ids
receipt02.move_ids.quantity_done = 4
receipt02.move_ids.quantity = 4
receipt02.move_ids.picked = True
receipt02.button_validate()
(receipt01 | receipt02).move_ids.invalidate_recordset()
@@ -349,7 +348,8 @@ class TestPurchaseStockReports(TestReportsCommon):
receipt01 = po.picking_ids
receipt01_move = receipt01.move_ids
receipt01_move.quantity_done = 6
receipt01_move.quantity = 6
receipt01_move.picked = True
action = receipt01.button_validate()
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_uom_qty = 10.0
customer_picking = picking_form.save()
customer_picking.action_confirm()
# 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
# 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_2.id).virtual_available, -10)
@@ -360,7 +362,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0
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.action_reset_draft()
customer_picking.action_confirm()
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
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.action_reset_draft()
customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint'].flush_model()
@@ -460,7 +460,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0
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.action_reset_draft()
customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint']._get_orderpoint_action()
orderpoint_product = self.env['stock.warehouse.orderpoint'].search(
@@ -503,7 +502,6 @@ class TestReorderingRule(TransactionCase):
move.product_uom_qty = 10.0
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.action_reset_draft()
customer_picking.action_confirm()
self.env['stock.warehouse.orderpoint'].flush_model()
@@ -722,13 +720,11 @@ class TestReorderingRule(TransactionCase):
po.button_confirm()
picking = po.picking_ids
action = picking.button_validate()
wizard = Form(self.env[(action.get('res_model'))].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
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, 'qty_done': 2.0, 'state': 'done', 'location_dest_id': sub_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, 'quantity': 2.0, 'state': 'done', 'location_dest_id': sub_location.id},
])
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,
})],
'state': 'draft',
'immediate_transfer': False,
})
picking.with_user(user).action_reset_draft()
picking.with_user(user).action_assign()
# check that the PO line quantity has been updated
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()
picking = last_po_id.picking_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()
self.assertEqual(move.stock_valuation_layer_ids.unit_cost,
@@ -166,9 +167,7 @@ class TestStockValuation(TransactionCase):
# validate the receipt
res_dict = picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
picking1.button_validate()
# the unit price of the valuationlayer used the latest value
self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 200)
@@ -214,9 +213,7 @@ class TestStockValuation(TransactionCase):
self.assertEqual(move1.price_unit, 11)
# validate the receipt
res_dict = picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
picking1.button_validate()
# the unit price of the valuation layer used the latest value
self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 12)
@@ -245,14 +242,16 @@ class TestStockValuation(TransactionCase):
picking1 = po1.picking_ids[0]
move1 = picking1.move_ids[0]
move1.quantity_done = 15
move1.quantity = 15
move1.picked = True
picking1.button_validate()
# there should be only one move
self.assertEqual(len(picking1.move_ids), 1)
self.assertEqual(move1.price_unit, 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)
def test_backorder_fifo_1(self):
@@ -277,7 +276,8 @@ class TestStockValuation(TransactionCase):
picking1 = po1.picking_ids[0]
move1 = picking1.move_ids[0]
move1.quantity_done = 5
move1.quantity = 5
move1.picked = True
res_dict = picking1.button_validate()
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'])
@@ -411,7 +411,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
wizard.product_return_moves.quantity = qty
action = wizard.create_returns()
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 return_picking
@@ -476,9 +477,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
self.assertAlmostEqual(move1.price_unit, price_unit_usd, places=2)
# validate the receipt
res_dict = picking1.button_validate()
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
wizard.process()
picking1.button_validate()
# 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)
@@ -506,7 +505,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})
po1.button_confirm()
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()
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
@@ -532,7 +532,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})
po2.button_confirm()
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()
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_action = stock_return_picking.create_returns()
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()
# valuation of product1 should be 200 as the first items will be sent out
@@ -598,7 +600,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1
receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate()
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()
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()
# valuation of product1 should be 15 as the tax with no account set
@@ -719,7 +723,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
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()
# Create a vendor bill
@@ -818,7 +823,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
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()
# Create a vendor bill
@@ -1195,7 +1201,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids
(picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 1.0}))
.write({'quantity': 1.0, 'picked': True}))
picking.button_validate()
# 5 Units received at rate 0.7 = 42.86
@@ -1294,7 +1300,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids
(picking.move_ids
.filtered(lambda l: l.purchase_line_id == line_product_avg)
.write({'quantity_done': 1.0}))
.write({'quantity': 1.0, 'picked': True}))
picking.button_validate()
# 5 Units received at rate 2 = 42.86
@@ -1422,7 +1428,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking = po.picking_ids
(picking.move_ids
.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._action_done() # Create Backorder
@@ -1456,7 +1462,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
backorder_picking = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
(backorder_picking.move_ids
.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()
# 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)
@@ -1545,7 +1551,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1
receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate()
# Create an invoice with a different price and a discount
@@ -1594,7 +1601,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1
receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate()
# Create an invoice with a different price and a discount
@@ -1641,7 +1649,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive the goods
receipt = order.picking_ids[0]
receipt.move_ids.quantity_done = 1
receipt.move_ids.quantity = 1
receipt.move_ids.picked = True
receipt.button_validate()
# Create an invoice with a different price and a discount
@@ -1754,7 +1763,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive 7 Hundred
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()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process()
@@ -1764,7 +1774,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
# Receive 3 Hundred
receipt02 = receipt01.backorder_ids
receipt02.move_ids._set_quantities_to_reservation()
receipt02.move_ids.picked = True
receipt02.button_validate()
expected_svl_values += [3 * 50]
@@ -1784,11 +1794,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id,
})],
'state': 'draft',
'immediate_transfer': False,
})
delivery01.action_reset_draft()
delivery01.action_confirm()
delivery01.move_ids._set_quantities_to_reservation()
delivery01.move_ids.picked = True
delivery01.button_validate()
expected_svl_values += [-5 * 50]
@@ -1858,11 +1866,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id,
})],
'state': 'draft',
'immediate_transfer': False,
})
delivery02.action_reset_draft()
delivery02.action_confirm()
delivery02.move_ids._set_quantities_to_reservation()
delivery02.move_ids.picked = True
delivery02.button_validate()
expected_svl_values += [-2 * 50 + -2 * 10]
@@ -1899,7 +1905,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
receipt = po.picking_ids[0]
receipt.move_ids._set_quantities_to_reservation()
receipt.move_ids.picked = True
receipt.button_validate()
delivery = self.env['stock.picking'].create({
@@ -1914,11 +1920,9 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
'location_dest_id': customer_location.id,
})],
'state': 'draft',
'immediate_transfer': False,
})
delivery.action_reset_draft()
delivery.action_confirm()
delivery.move_ids._set_quantities_to_reservation()
delivery.move_ids.picked = True
delivery.button_validate()
bill01_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
@@ -1973,19 +1977,22 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
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()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process()
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()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process()
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()
expected_svl_values = [40, 30, 50]
@@ -2121,7 +2128,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
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()
expected_svl_values += [100.0]
expected_svl_remaining_values += [100.0]
@@ -2149,7 +2157,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})],
})
delivery.action_confirm()
delivery.move_ids.quantity_done = 3.0
delivery.move_ids.quantity = 3.0
delivery.move_ids.picked = True
delivery.button_validate()
expected_svl_values += [-36.0]
expected_svl_remaining_values += [0.0]
@@ -2185,7 +2194,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
})],
})
delivery.action_confirm()
delivery.move_ids.quantity_done = 1.0
delivery.move_ids.quantity = 1.0
delivery.move_ids.picked = True
delivery.button_validate()
expected_svl_values += [-9.0]
expected_svl_remaining_values += [0.0]
@@ -2274,7 +2284,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
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()
expected_svl_values += [100.0]
expected_svl_remaining_values += [100.0]
@@ -2367,13 +2378,15 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
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()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(action['context'])).save()
backorder_wizard.process()
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()
bill01 = self._bill(po, 1.0, 11)
@@ -2471,7 +2484,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
picking = po.picking_ids[0]
move = picking.move_ids[0]
move.quantity_done = 10
move.quantity = 10
move.picked = True
res_dict = picking.button_validate()
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'])
@@ -2482,7 +2496,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking2 = po.picking_ids.filtered(lambda p: p.backorder_id)
move2 = picking2.move_ids[0]
move2.quantity_done = 2
move2.quantity = 2
move2.picked = True
picking2.button_validate()
self.assertAlmostEqual(move2.stock_valuation_layer_ids.unit_cost, price_unit_USD)
@@ -2515,7 +2530,8 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
po.button_confirm()
picking = po.picking_ids[0]
move = picking.move_ids[0]
move.quantity_done = 10
move.quantity = 10
move.picked = True
res_dict = picking.button_validate()
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'])
@@ -2526,6 +2542,7 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
picking2 = po.picking_ids.filtered(lambda p: p.backorder_id)
move2 = picking2.move_ids[0]
move2.quantity_done = 2
move2.quantity = 2
move2.picked = True
picking2.button_validate()
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')
stock_move.quantity_done = 1
stock_move.quantity = 1
stock_move.picked = True
stock_move.picking_id.button_validate()
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):
res = self.env['stock.move'].search_count([
('repair_line_type', '=', 'remove'),
('repair_line_type', 'in', ['remove', 'recycle']),
('product_uom_qty', '=', 1),
('move_line_ids.lot_id', 'in', sn_lot.id),
('move_line_ids.lot_id', '=', sn_lot.id),
('state', '=', 'done'),
('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.is_returned = True
@api.depends('state',
'move_ids.reserved_availability',
'move_ids.quantity_done')
@api.depends('state', 'move_ids.quantity', 'move_ids.product_uom_qty')
def _compute_show_qty_button(self):
self.show_set_qty_button = False
self.show_clear_qty_button = False
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
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
@api.depends('move_ids', 'state', 'move_ids.product_uom_qty')
def _compute_unreserve_visible(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.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()
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):
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
# during the creation of stock.move.
self = self.with_context(clean_context(self._context))
self._check_product_tracking()
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
product_move_vals = []
# Cancel moves with 0 quantity_done
self.move_ids.filtered(lambda m: float_is_zero(m.quantity_done, precision_rounding=m.product_uom.rounding))._action_cancel()
# Cancel moves with 0 quantity
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']
#SOL qty delivered = repair.move_ids.quantity_done
#SOL qty delivered = repair.move_ids.quantity
for repair in self:
if repair.sale_order_line_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, {
'product_id': repair.product_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,
'qty_done': repair.product_qty,
'quantity': repair.product_qty,
'package_id': False,
'result_package_id': False,
'owner_id': owner_id,
@@ -436,7 +432,6 @@ class Repair(models.Model):
product_moves = self.env['stock.move'].create(product_move_vals)
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._action_done()
@@ -454,7 +449,7 @@ class Repair(models.Model):
"""
if self.filtered(lambda repair: repair.state != 'under_repair'):
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['default_repair_ids'] = self.ids
return {
@@ -476,9 +471,6 @@ class Repair(models.Model):
self._action_repair_confirm()
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):
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'})
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):
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:
continue
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()
@api.model_create_multi
+1 -1
View File
@@ -113,7 +113,7 @@ class StockMove(models.Model):
for move in self:
if move.sale_line_id or move.repair_line_type != 'add' or not move.repair_id.sale_order_id:
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({
'order_id': move.repair_id.sale_order_id.id,
'product_id': move.product_id.id,
+27 -28
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# 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.tools import float_compare, float_is_zero
@@ -215,7 +215,7 @@ class TestRepair(common.TransactionCase):
# No partner warning -> working case -> already linked warning
# 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
with self.assertRaises(UserError) as err:
repair.action_create_sale_order()
@@ -223,11 +223,10 @@ class TestRepair(common.TransactionCase):
repair.partner_id = self.res_partner_12.id
repair.action_create_sale_order()
# Ensure SO and SOL were created
self.assertNotEqual(repair.sale_order_id.__len__(), 0)
self.assertEqual(repair.sale_order_id.order_line.__len__(), 3)
self.assertNotEqual(len(repair.sale_order_id), 0)
self.assertEqual(len(repair.sale_order_id.order_line), 3)
with self.assertRaises(UserError) as err:
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)
# PRE
@@ -271,15 +270,16 @@ class TestRepair(common.TransactionCase):
# under_repair -> done (action_repair_end -> action_repair_done)
# PRE
# 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
# 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
# move_ids.state == (done || cancel)
# state == done
# move_ids with qty_done (LOWER or HIGHER than) product_uom_qty MUST NOT be splitted
# Any line with qty_done < product_uom_qty => Warning
# move_ids with quantity (LOWER or HIGHER than) product_uom_qty MUST NOT be splitted
# Any line with quantity < product_uom_qty => Warning
repair.move_ids.picked = True
end_action = repair.action_repair_end()
self.assertEqual(end_action.get("res_model"), "repair.warn.uncomplete.move")
warn_uncomplete_wizard = Form(
@@ -287,22 +287,22 @@ class TestRepair(common.TransactionCase):
.with_context(**end_action['context'])
).save()
# 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()
self.assertIn("Serial number is required for operation lines", err.exception.args[0])
# LineB with lots
move_line = lineB.move_line_ids[0]
move_line.qty_done = move_line.reserved_uom_qty
lineB.move_line_ids.lot_id = lot
lineA.quantity_done = 2 # qty_done = product_uom_qty
lineC.quantity_done = 2 # qty_done > product_uom_qty (No warning)
lineA.quantity = 2 # quantity = product_uom_qty
lineC.quantity = 2 # quantity > product_uom_qty (No warning)
lineD = self._create_simple_part_move(repair.id, 0.0)
repair.move_ids |= lineD # product_uom_qty = 0 : state is cancelled
self.assertEqual(lineD.state, 'assigned')
num_of_lines = repair.move_ids.__len__()
self.assertEqual(repair.move_id.__len__(), 0)
num_of_lines = len(repair.move_ids)
self.assertFalse(repair.move_id)
repair.action_repair_end()
self.assertEqual(repair.state, "done")
@@ -310,15 +310,14 @@ class TestRepair(common.TransactionCase):
#line a,b,c are 'done', line d is 'cancel'
self.assertTrue(all(m.state == 'done' for m in done_moves))
self.assertEqual(lineD.state, 'cancel')
self.assertEqual(repair.move_id.__len__(), 1)
self.assertEqual(repair.move_ids.__len__(), num_of_lines) # No split
self.assertEqual(len(repair.move_id), 1)
self.assertEqual(len(repair.move_ids), num_of_lines) # No split
# (*) -> cancel (action_repair_cancel)
# PRE
# state != done !-> UserError
with self.assertRaises(UserError) as err:
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):
# Binding from SO to RO(s)
@@ -411,13 +410,14 @@ class TestRepair(common.TransactionCase):
# repair_order.action_repair_end()
# -> order_line.qty_delivered == order_line.product_uom_qty
# -> "RO Lines"'s SOL.qty_delivered == move.quantity_done
for line in repair_order.move_ids:
line.quantity_done = line.product_uom_qty
# -> "RO Lines"'s SOL.qty_delivered == move.quantity
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()
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))
def test_03_sale_order_delivered_qty(self):
@@ -546,9 +546,8 @@ class TestRepair(common.TransactionCase):
picking = picking_form.save()
picking.action_confirm()
picking.action_assign()
res_dict = picking.button_validate()
wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
wizard.process()
picking.button_validate()
self.assertEqual(picking.state, 'done')
# Create a return
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_action = stock_return_picking.create_returns()
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()
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_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_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_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"/>
@@ -147,7 +146,6 @@
<field name="picking_code" column_invisible="True"/>
<field name="product_type" 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="additional" 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="date" 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_uom_qty" string="Demand" readonly="state in ('done', 'cancel')"/>
<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 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="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="picked" string="Used"/>
<field name="lot_ids" widget="many2many_tags"
groups="stock.group_production_lot"
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')
or (m.location_dest_id.usage != 'customer'
and m.state == 'done'
and float_compare(m.quantity_done,
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'),
and float_compare(m.quantity,
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)
for m in moves) or not moves:
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')
for move_line in kit_lines:
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
super(StockMoveLine, self - kit_lines)._compute_sale_price()
@@ -96,7 +96,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
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
pick = so.picking_ids
# To check the products on the picking
wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
pick.button_validate()
# Create the invoice
so._create_invoices()
invoice = so.invoice_ids
@@ -244,7 +242,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 20, 60]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# Sell 3 kits
@@ -267,7 +265,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids
while picking:
pickings.append(picking)
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -288,7 +286,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100,
})
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# 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_picking_id, dummy = return_wizard._create_returns()
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()
# Add a credit note for the returned kit
@@ -349,7 +347,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 20, 60]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# Sell 3 kits
@@ -372,7 +370,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids
while picking:
pickings.append(picking)
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -393,7 +391,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100,
})
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# 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_picking_id, dummy = return_wizard._create_returns()
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()
# Create a new invoice for the returned kit
@@ -460,7 +458,7 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
invoice = so._create_invoices()
@@ -513,7 +511,8 @@ class TestSaleMRPAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
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())
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):
""" Helper to check expected quantities based on a dict following this structure :
@@ -231,7 +234,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
move._action_confirm()
move._action_assign()
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()
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
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 = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
@@ -561,9 +564,9 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
pick_2 = so.picking_ids.filtered('backorder_id')
for move in pick_2.move_ids:
if move.product_id.id == product_desk_bolt.id:
move.write({'quantity_done': 19})
move.write({'quantity': 19, 'picked': True})
else:
move.write({'quantity_done': 4})
move.write({'quantity': 4, 'picked': True})
pick_2.button_validate()
del_qty = sum(sol.qty_delivered for sol in so.order_line)
@@ -670,9 +673,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
pick = self.so.picking_ids
# To check the products on the picking
self.assertEqual(pick.move_ids.mapped('product_id'), self.component1 | self.component2)
wiz_act = pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
pick.button_validate()
# Create the invoice
self.so._create_invoices()
self.invoice = self.so.invoice_ids
@@ -749,7 +750,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self._assert_quantities(move_ids, expected_quantities)
# 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 = 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
# 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 = 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 :
# - Now only 3 kits should be delivered
# - 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 = 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
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
wiz_act = picking_original.button_validate()
@@ -976,9 +977,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_pick = self.env['stock.picking'].browse(res['res_id'])
# Process all components and validate the picking
wiz_act = return_pick.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
return_pick.button_validate()
# Now quantity delivered should be 3 again
self.assertEqual(order_line.qty_delivered, 3)
@@ -995,7 +994,8 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Process all components except one of each
for move in return_of_return_pick.move_ids:
move.write({
'quantity_done': expected_quantities[move.product_id] - 1,
'quantity': expected_quantities[move.product_id] - 1,
'picked': True,
'to_refund': True
})
@@ -1702,7 +1702,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Validate delivery
pick = order.picking_ids
pick.move_ids.write({'quantity_done': 1})
pick.move_ids.write({'quantity': 1, 'picked': True})
pick.button_validate()
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')
@@ -1718,7 +1718,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
})
res = return_wiz.create_returns()
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
# Delivered quantities to the client should be 0
@@ -1766,9 +1766,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so = so_form.save()
so.action_confirm()
wiz_act = so.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
so.picking_ids.button_validate()
p2_bom.type = "normal"
@@ -1902,8 +1900,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so.action_confirm()
# Deliver the products
pick = so.picking_ids
wiz_act = pick.button_validate()
Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save().process()
pick.button_validate()
# Create the invoice
so._create_invoices()
# Validate the invoice
@@ -1946,9 +1943,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
# Check picking creation
self.assertEqual(len(so.picking_ids), 1, "A picking should be created after the SO validation")
wiz_act = so.picking_ids.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
so.picking_ids.button_validate()
so._action_cancel()
so.action_draft()
@@ -1989,9 +1984,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.assertEqual(price, 10)
picking = so.picking_ids
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
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))
@@ -2049,8 +2042,6 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
{'product_id': self.component_f.id, 'product_uom_qty': 100},
{'product_id': self.component_g.id, 'product_uom_qty': 200},
])
delivery.action_set_quantities_to_reservation()
delivery.button_validate()
# Return 2 [uom_ten] x kit_3
@@ -2060,7 +2051,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_wizard.product_return_moves[1].quantity = 40
action = return_wizard.create_returns()
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()
# Adapt the SOL qty according to the delivered one
@@ -2069,10 +2060,10 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
line.product_uom_qty = 8
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_g.id, 'location_dest_id': custo_location.id, 'quantity_done': 200, 'state': 'done'},
{'product_id': self.component_f.id, 'location_dest_id': stock_location.id, 'quantity_done': 20, 'state': 'done'},
{'product_id': self.component_g.id, 'location_dest_id': stock_location.id, 'quantity_done': 40, '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': 200, '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': 40, 'state': 'done'},
])
def test_kit_decrease_sol_qty_to_zero(self):
@@ -2139,7 +2130,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
delivery = so.picking_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()
self.assertEqual(delivery.state, 'done')
@@ -2150,7 +2141,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
return_picking_id, dummy = return_wizard._create_returns()
return_picking = self.env['stock.picking'].browse(return_picking_id)
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()
self.assertEqual(return_picking.state, 'done')
@@ -2197,7 +2188,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 50]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
so = self.env['sale.order'].create({
@@ -2215,7 +2206,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
so.action_confirm()
picking = so.picking_ids
picking.move_ids.quantity_done = 1.0
picking.move_ids.write({'quantity': 1.0, 'picked': True})
picking.button_validate()
invoice01 = so._create_invoices()
@@ -2279,7 +2270,8 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
})],
})
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()
invoice = so._create_invoices()
@@ -2310,7 +2302,7 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.bom_kit_1.unlink()
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()
self.assertEqual(so.picking_ids.state, 'done')
@@ -184,7 +184,7 @@ class TestSaleMrpKitBom(TransactionCase):
self.assertEqual(so.order_line.qty_delivered, 0)
picking = so.picking_ids
picking.move_ids.quantity_done = 0.86000
picking.move_ids.write({'quantity': 0.86000, 'picked': True})
picking.button_validate()
# Checks the delivery amount (must be 10).
@@ -249,9 +249,7 @@ class TestSaleMrpKitBom(TransactionCase):
self.assertEqual(so.order_line.qty_delivered, 0)
picking = so.picking_ids
action = picking.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
picking.button_validate()
# Checks the delivery amount (must be 1).
self.assertEqual(so.order_line.qty_delivered, 1)
@@ -386,7 +384,7 @@ class TestSaleMrpKitBom(TransactionCase):
ship = so.picking_ids[1]
for move in pick.move_ids:
move.quantity_done = 1
move.write({'quantity': 1, 'picked': True})
pick.action_put_in_pack()
pick.button_validate()
@@ -395,7 +393,7 @@ class TestSaleMrpKitBom(TransactionCase):
ship.package_level_ids._set_is_done()
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):
"""
@@ -511,7 +509,7 @@ class TestSaleMrpKitBom(TransactionCase):
so.action_confirm()
picking = so.picking_ids
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()
self.assertEqual(picking.state, 'done')
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# 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
@@ -57,9 +57,7 @@ class TestSaleMrpInvoices(common.TransactionCase):
})
so.action_confirm()
action = so.picking_ids.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
so.picking_ids.button_validate()
invoice = so._create_invoices()
invoice.action_post()
@@ -71,7 +71,7 @@ class TestSalePurchaseStockFlow(TransactionCase):
'location_id': sm.location_id.id,
'location_dest_id': sm.location_dest_id.id,
'product_id': sm.product_id.id,
'qty_done': 12,
'quantity': 12,
'company_id': sm.company_id.id,
'product_uom_id': sm.product_uom.id,
'picking_id': delivery.id,
@@ -79,8 +79,8 @@ class TestSalePurchaseStockFlow(TransactionCase):
delivery.button_validate()
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)
sm.move_line_ids.qty_done = 10
sm.move_line_ids.quantity = 10
self.assertEqual(so.order_line.qty_delivered, 10)
+9 -9
View File
@@ -61,7 +61,7 @@ class AccountMove(models.Model):
continue
product = sml.product_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_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')
)
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
qty_done = returned_qty - qty_done
quantity = returned_qty - quantity
previous_qty_invoiced = previous_qties_invoiced[product]
previous_qty_delivered = previous_qties_delivered[product]
# If we return more than currently delivered (i.e., qty_done < 0), we remove the surplus
# from the previously delivered (and qty_done becomes zero). If it's a delivery, we first
# If we return more than currently delivered (i.e., quantity < 0), we remove the surplus
# from the previously delivered (and quantity becomes zero). If it's a delivery, we first
# 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:
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
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():
# 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.free_qty_today = 0
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.scheduled_date = line.order_id.commitment_date or line._expected_date()
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']:
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):
super(SaleOrderLine, self)._compute_qty_delivered()
@@ -159,11 +159,11 @@ class SaleOrderLine(models.Model):
for move in outgoing_moves:
if move.state != 'done':
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:
if move.state != 'done':
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
@api.model_create_multi
@@ -237,10 +237,10 @@ class SaleOrderLine(models.Model):
qty = 0.0
outgoing_moves, incoming_moves = self._get_outgoing_incoming_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')
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')
return qty
+2 -2
View File
@@ -106,7 +106,7 @@ class StockPicking(models.Model):
sale_order = move.picking_id.sale_id
# 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.
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
product = move.product_id
so_line_vals = {
@@ -115,7 +115,7 @@ class StockPicking(models.Model):
'order_id': sale_order.id,
'product_id': product.id,
'product_uom_qty': 0,
'qty_delivered': move.quantity_done,
'qty_delivered': move.quantity,
'product_uom': move.product_uom.id,
}
if product.invoice_policy == 'delivery':
@@ -57,7 +57,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 8,
})
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_2 = self.env['stock.move'].create({
'name': 'a',
@@ -69,7 +69,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10,
})
in_move_2._action_confirm()
in_move_2.quantity_done = 1
in_move_2.write({'quantity': 1, 'picked': True})
in_move_2._action_done()
# -------------------------------------------------------------------------
@@ -127,7 +127,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -160,7 +160,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0
# 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()
# change the standard price to 16
@@ -199,7 +199,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -208,7 +208,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0
# 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()
# Invoice the sale order.
@@ -266,7 +266,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -299,7 +299,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0
# 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()
# change the standard price to 16
@@ -338,7 +338,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -347,7 +347,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.product.standard_price = 14.0
# 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()
# Invoice the sale order.
@@ -418,7 +418,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -456,7 +456,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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()
# Invoice the sale order.
@@ -503,7 +503,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
so.action_confirm()
pick = so.picking_ids
pick.move_ids.write({'quantity_done': 5})
pick.move_ids.write({'quantity': 5, 'picked': True})
pick.button_validate()
product.standard_price = 40
@@ -516,7 +516,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
res = return_wiz.create_returns()
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()
picking = self.env['stock.picking'].create({
@@ -532,7 +532,8 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'picking_id': picking.id,
'product_id': product.id,
'product_uom': product.uom_id.id,
'quantity_done': 1,
'quantity': 1,
'picked': True,
})
picking.button_validate()
@@ -573,7 +574,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -610,7 +611,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
# Create and confirm a sale order for 2@12
sale_order = self._so_and_confirm_two_units()
# 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()
# Invoice the sale order.
@@ -648,7 +649,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
# Create and confirm a sale order for 2@12
sale_order = self._so_and_confirm_two_units()
# 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()
# 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)
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()
invoice = sale_order._create_invoices()
@@ -747,7 +748,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -790,7 +791,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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()
# Invoice the sale order.
@@ -843,7 +844,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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 = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save()
wiz.process()
@@ -887,7 +888,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order = self._so_and_confirm_two_units()
# 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()
# Invoice the sale order.
@@ -927,7 +928,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10,
})
in_move_1._action_confirm()
in_move_1.quantity_done = 8
in_move_1.write({'quantity': 8, 'picked': True})
in_move_1._action_done()
# Create and confirm a sale order for 2@12
@@ -946,7 +947,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order.action_confirm()
# 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()
# Make the second receipt
@@ -960,7 +961,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12,
})
in_move_2._action_confirm()
in_move_2.quantity_done = 2
in_move_2.write({'quantity': 2, 'picked': True})
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].quantity, 0)
@@ -1003,7 +1004,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 8,
})
in_move_1._action_confirm()
in_move_1.quantity_done = 5
in_move_1.write({'quantity': 5, 'picked': True})
in_move_1._action_done()
# +8@12
@@ -1017,7 +1018,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12,
})
in_move_2._action_confirm()
in_move_2.quantity_done = 8
in_move_2.write({'quantity': 8, 'picked': True})
in_move_2._action_done()
# sale 1@20, deliver, invoice
@@ -1034,7 +1035,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
invoice = sale_order._create_invoices()
invoice.action_post()
@@ -1053,7 +1054,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
invoice = sale_order._create_invoices()
invoice.action_post()
@@ -1081,7 +1082,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10,
})
in_move_1._action_confirm()
in_move_1.quantity_done = 8
in_move_1.write({'quantity': 8, 'picked': True})
in_move_1._action_done()
# Create and confirm a sale order for 10@12
@@ -1100,7 +1101,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
sale_order.action_confirm()
# 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()
# Invoice the sale order.
@@ -1118,7 +1119,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 12,
})
in_move_2._action_confirm()
in_move_2.quantity_done = 2
in_move_2.write({'quantity': 2, 'picked': True})
in_move_2._action_done()
# check the last anglo saxon move line
@@ -1143,12 +1144,12 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 10,
})
in_move_1._action_confirm()
in_move_1.quantity_done = 2
in_move_1.write({'quantity': 2, 'picked': True})
in_move_1._action_done()
# Create, confirm and deliver a sale order for 2@12 (SO1)
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()
# Return 1 from SO1
@@ -1161,7 +1162,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
stock_return_picking_action = stock_return_picking.create_returns()
return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
return_pick.action_assign()
return_pick.move_ids.quantity_done = 1
return_pick.move_ids.write({'quantity': 1, 'picked': True})
return_pick._action_done()
# Create, confirm and deliver a sale order for 1@12 (SO2)
@@ -1178,7 +1179,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
# Receive 1@20
@@ -1192,7 +1193,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 20,
})
in_move_2._action_confirm()
in_move_2.quantity_done = 1
in_move_2.write({'quantity': 1, 'picked': True})
in_move_2._action_done()
# Re-deliver returned 1 from SO1
@@ -1205,7 +1206,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
stock_redeliver_picking_action = stock_redeliver_picking.create_returns()
redeliver_pick = self.env['stock.picking'].browse(stock_redeliver_picking_action['res_id'])
redeliver_pick.action_assign()
redeliver_pick.move_ids.quantity_done = 1
redeliver_pick.move_ids.write({'quantity': 1, 'picked': True})
redeliver_pick._action_done()
# Invoice the sale orders
@@ -1274,7 +1275,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
# Invoice the sale order.
@@ -1317,7 +1318,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 1.0,
})
in_move_1._action_confirm()
in_move_1.quantity_done = quantity
in_move_1.write({'quantity': quantity, 'picked': True})
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)
@@ -1334,7 +1335,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
})],
})
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()
# Invoice the sale order.
@@ -1384,7 +1385,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 20, 60]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# Sell 3 units
@@ -1407,7 +1408,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids
while picking:
pickings.append(picking)
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1428,7 +1429,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100,
})
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# 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_picking_id, dummy = return_wizard._create_returns()
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()
# Add a credit note for the returned product
@@ -1478,7 +1479,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 20, 60]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# Sell 3 units
@@ -1501,7 +1502,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
picking = so.picking_ids
while picking:
pickings.append(picking)
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1522,7 +1523,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': 100,
})
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
# 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_picking_id, dummy = return_wizard._create_returns()
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()
# Create a new invoice for the returned product
@@ -1567,7 +1568,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in svl_values])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
so = self.env['sale.order'].create({
@@ -1589,7 +1590,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
invoices = self.env['account.move']
picking = so.picking_ids
while picking:
picking.move_ids.quantity_done = 1
picking.move_ids.write({'quantity': 1, 'picked': True})
action = picking.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
@@ -1657,7 +1658,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
'price_unit': p,
} for p in [10, 50]])
in_moves._action_confirm()
in_moves.quantity_done = 1
in_moves.write({'quantity': 1, 'picked': True})
in_moves._action_done()
so = self.env['sale.order'].create({
@@ -1675,7 +1676,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
so.action_confirm()
picking = so.picking_ids
picking.move_ids.quantity_done = 1.0
picking.move_ids.write({'quantity': 1.0, 'picked': True})
picking.button_validate()
invoice01 = so._create_invoices()
@@ -1714,7 +1715,7 @@ class TestAngloSaxonValuation(ValuationReconciliationTestCommon):
self.assertEqual(sale_order.order_line.product_uom_qty, 2.0)
# 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()
svl_am = sale_order.order_line.move_ids.stock_valuation_layer_ids.account_move_id
svl_am.button_draft()

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