[FIX] stock: scrap: allow scrapping consumable

A previous revision[0] forbid to scrap consumables. The rationale at the
time was that, since consumables do not have quant, scrapping them does
not make sense. It appears that people use this feature on consumable
for traceability reason, so we bring back the old behavior.

A slight change was done in stock_scrap.py: if the product is a
consumable, do not raise the error if the quantity to scrap is not
available in stock for obvious reasons.

Also, a leftover todo in the code indicate that we should unreserve the
any move having reserved the scrapped product. This is actually already
done, since this mechanism is in the move line `action_done` and a scrap
uses a classical stock move and stock move lines.

We introduce some test to prove that it is possible to scrap a stockable
product and the quant are impacted, to scrap a consumable product and
the stock is not impacted and to scrap something that was reseved by a
move and that the move changes its state.

[0] 86f08c091e
This commit is contained in:
Simon Lejeune
2017-07-19 18:29:16 +02:00
parent 886137d0c8
commit cc181711dd
4 changed files with 75 additions and 12 deletions
+1 -3
View File
@@ -778,8 +778,6 @@ class Picking(models.Model):
@api.multi
def button_scrap(self):
self.ensure_one()
# only stockable products are scrapeable
scrapeable_products = self.move_line_ids.mapped('product_id').filtered(lambda p: p.type == 'product')
return {
'name': _('Scrap'),
'view_type': 'form',
@@ -787,7 +785,7 @@ class Picking(models.Model):
'res_model': 'stock.scrap',
'view_id': self.env.ref('stock.stock_scrap_form_view2').id,
'type': 'ir.actions.act_window',
'context': {'default_picking_id': self.id, 'product_ids': scrapeable_products.ids},
'context': {'default_picking_id': self.id, 'product_ids': self.move_line_ids.mapped('product_id').ids},
'target': 'new',
}
+7 -7
View File
@@ -79,15 +79,15 @@ class StockScrap(models.Model):
def do_scrap(self):
for scrap in self:
move = self.env['stock.move'].create(scrap._prepare_move_values())
quantity_in_stock = self.env['stock.quant']._get_quantity(
self.product_id, self.location_id, lot_id=self.lot_id,
package_id=self.package_id, owner_id=self.owner_id, strict=True
)
if quantity_in_stock < move.product_qty: # FIXME: float compare
raise UserError(_('You cannot scrap a move without having available stock for %s. You can correct it with an inventory adjustment.') % move.product_id.name)
if self.product_id.type == 'product':
quantity_in_stock = self.env['stock.quant']._get_quantity(
self.product_id, self.location_id, lot_id=self.lot_id,
package_id=self.package_id, owner_id=self.owner_id, strict=True
)
if quantity_in_stock < move.product_qty: # FIXME: float compare
raise UserError(_('You cannot scrap a move without having available stock for %s. You can correct it with an inventory adjustment.') % move.product_id.name)
move.action_done()
scrap.write({'move_id': move.id, 'state': 'done'})
# TODO: unreserve reserved
return True
def _prepare_move_values(self):
+66 -1
View File
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
class StockMove(TransactionCase):
@@ -30,6 +30,11 @@ class StockMove(TransactionCase):
'tracking': 'lot',
'categ_id': self.env.ref('product.product_category_all').id,
})
self.product4 = self.env['product.product'].create({
'name': 'Product A',
'type': 'consu',
'categ_id': self.env.ref('product.product_category_all').id,
})
def test_in_1(self):
""" Receive products from a supplier. Check that a move line is created and that the
@@ -2220,3 +2225,63 @@ class StockMove(TransactionCase):
move1.product_id = self.product2
move1.onchange_product_id()
self.assertEqual(move1.product_uom_qty, 100)
def test_scrap_1(self):
""" Check the created stock move and the impact on quants when we scrap a
stockable product.
"""
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1)
scrap = self.env['stock.scrap'].create({
'product_id': self.product1.id,
'product_uom_id':self.product1.uom_id.id,
'scrap_qty': 1,
})
self.assertEqual(scrap.state, 'done')
move = scrap.move_id
self.assertEqual(move.state, 'done')
self.assertEqual(move.quantity_done, 1)
self.assertEqual(move.scrapped, True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0)
def test_scrap_2(self):
""" Check the created stock move and the impact on quants when we scrap a
consumable product.
"""
scrap = self.env['stock.scrap'].create({
'product_id': self.product4.id,
'product_uom_id':self.product4.uom_id.id,
'scrap_qty': 1,
})
self.assertEqual(scrap.state, 'done')
move = scrap.move_id
self.assertEqual(move.state, 'done')
self.assertEqual(move.quantity_done, 1)
self.assertEqual(move.scrapped, True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product4, self.stock_location), 0)
def test_scrap_3(self):
""" Scrap the product of a reserved move line. Check that the move line is
correctly deleted and that the associated stock move is not assigned anymore.
"""
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1)
move1 = self.env['stock.move'].create({
'name': 'test_set_quantity_done_1',
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
'product_id': self.product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 1.0,
})
move1.action_confirm()
move1.action_assign()
self.assertEqual(move1.state, 'assigned')
self.assertEqual(len(move1.move_line_ids), 1)
scrap = self.env['stock.scrap'].create({
'product_id': self.product1.id,
'product_uom_id':self.product1.uom_id.id,
'scrap_qty': 1,
})
self.assertEqual(move1.state, 'confirmed')
self.assertEqual(len(move1.move_line_ids), 0)
+1 -1
View File
@@ -43,7 +43,7 @@
</div>
<group>
<group>
<field name="product_id" domain="[('type', '=', 'product')]"/>
<field name="product_id"/>
<label for="scrap_qty"/>
<div class="o_row">
<field name="scrap_qty"/>