[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:
@@ -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"/>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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: () => {},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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']))
|
||||
|
||||
@@ -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
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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'},
|
||||
])
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 < 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 >= 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 > 0.0001"
|
||||
decoration-warning="quantity_done - product_uom_qty > 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 > 0.0001"
|
||||
decoration-warning="quantity - product_uom_qty > 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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 < 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 > 0.0001" decoration-success="not is_done and quantity_done - product_uom_qty < 0.0001" js_class="subcontracting_portal_move_list_view">
|
||||
<tree delete="0" create="0" decoration-muted="is_done" decoration-warning="quantity - product_uom_qty > 0.0001" decoration-success="not is_done and quantity - product_uom_qty < 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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
])
|
||||
|
||||
@@ -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]]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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 < 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 >= 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'"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user