From 80bafb795c187f0a13c4780fb5bc2e7935294ceb Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Thu, 13 Jun 2019 17:55:29 +0200 Subject: [PATCH] [IMP] stock_request: Improve cancellation detection --- stock_request/models/stock_move.py | 17 ++++ stock_request/models/stock_request.py | 41 +++++--- .../models/stock_request_allocation.py | 3 +- stock_request/models/stock_request_order.py | 22 ++--- stock_request/tests/test_stock_request.py | 96 +++++++++++++++++++ stock_request/views/stock_request_views.xml | 6 +- 6 files changed, 159 insertions(+), 26 deletions(-) diff --git a/stock_request/models/stock_move.py b/stock_request/models/stock_move.py index eec5e0312..7cef93ec5 100644 --- a/stock_request/models/stock_move.py +++ b/stock_request/models/stock_move.py @@ -37,3 +37,20 @@ class StockMove(models.Model): raise ValidationError( _('The company of the stock request must match with ' 'that of the location.')) + + def copy_data(self, default=None): + if not default: + default = {} + if 'allocation_ids' not in default: + default['allocation_ids'] = [] + for alloc in self.allocation_ids: + default['allocation_ids'].append((0, 0, { + 'stock_request_id': alloc.stock_request_id.id, + 'requested_product_uom_qty': alloc.requested_product_uom_qty, + })) + return super(StockMove, self).copy_data(default) + + def _action_cancel(self): + res = super()._action_cancel() + self.mapped('allocation_ids.stock_request_id').check_done() + return res diff --git a/stock_request/models/stock_request.py b/stock_request/models/stock_request.py index 73be05788..e68fa1df3 100644 --- a/stock_request/models/stock_request.py +++ b/stock_request/models/stock_request.py @@ -83,6 +83,11 @@ class StockRequest(models.Model): readonly=True, compute='_compute_qty', store=True, help="Quantity completed", ) + qty_cancelled = fields.Float( + 'Qty Cancelled', digits=dp.get_precision('Product Unit of Measure'), + readonly=True, compute='_compute_qty', store=True, + help="Quantity cancelled", + ) picking_count = fields.Integer(string='Delivery Orders', compute='_compute_picking_ids', readonly=True, @@ -124,12 +129,13 @@ class StockRequest(models.Model): 'Stock Request name must be unique'), ] - @api.depends('allocation_ids') + @api.depends('allocation_ids', 'allocation_ids.stock_move_id') def _compute_move_ids(self): for request in self: request.move_ids = request.allocation_ids.mapped('stock_move_id') - @api.depends('allocation_ids') + @api.depends('allocation_ids', 'allocation_ids.stock_move_id', + 'allocation_ids.stock_move_id.picking_id') def _compute_picking_ids(self): for request in self: request.picking_count = 0 @@ -146,11 +152,15 @@ class StockRequest(models.Model): done_qty = sum(request.allocation_ids.mapped( 'allocated_product_qty')) open_qty = sum(request.allocation_ids.mapped('open_product_qty')) - request.qty_done = request.product_id.uom_id._compute_quantity( + uom = request.product_id.uom_id + request.qty_done = uom._compute_quantity( done_qty, request.product_uom_id) - request.qty_in_progress = \ - request.product_id.uom_id._compute_quantity( - open_qty, request.product_uom_id) + request.qty_in_progress = uom._compute_quantity( + open_qty, request.product_uom_id) + request.qty_cancelled = max(0, uom._compute_quantity( + request.product_qty - done_qty - open_qty, + request.product_uom_id + )) if request.allocation_ids else 0 @api.constrains('order_id', 'requested_by') def check_order_requested_by(self): @@ -210,7 +220,7 @@ class StockRequest(models.Model): @api.multi def _action_confirm(self): self._action_launch_procurement_rule() - self.state = 'open' + self.write({'state': 'open'}) @api.multi def action_confirm(self): @@ -223,13 +233,12 @@ class StockRequest(models.Model): def action_cancel(self): self.sudo().mapped('move_ids')._action_cancel() - self.state = 'cancel' + self.write({'state': 'cancel'}) return True def action_done(self): - self.state = 'done' - if self.order_id: - self.order_id.check_done() + self.write({'state': 'done'}) + self.mapped('order_id').check_done() return True def check_done(self): @@ -243,8 +252,18 @@ class StockRequest(models.Model): if float_compare(qty_done, request.product_uom_qty, precision_digits=precision) >= 0: request.action_done() + elif request._check_done_allocation(): + request.action_done() return True + def _check_done_allocation(self): + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + self.ensure_one() + return self.allocation_ids and float_compare( + self.qty_cancelled, 0, precision_digits=precision + ) > 0 + def _prepare_procurement_values(self, group_id=False): """ Prepare specific key for moves or other components that diff --git a/stock_request/models/stock_request_allocation.py b/stock_request/models/stock_request_allocation.py index 99b05fb7f..1e69601dd 100644 --- a/stock_request/models/stock_request_allocation.py +++ b/stock_request/models/stock_request_allocation.py @@ -44,6 +44,7 @@ class StockRequestAllocation(models.Model): ) allocated_product_qty = fields.Float( 'Allocated Quantity', + copy=False, help='Quantity of the stock request allocated to the stock move, ' 'in the default UoM of the product', ) @@ -62,7 +63,7 @@ class StockRequestAllocation(models.Model): 'stock_move_id', 'stock_move_id.state') def _compute_open_product_qty(self): for rec in self: - if rec.stock_move_id.state == 'cancel': + if rec.stock_move_id.state in ['cancel', 'done']: rec.open_product_qty = 0.0 else: rec.open_product_qty = \ diff --git a/stock_request/models/stock_request_order.py b/stock_request/models/stock_request_order.py index 392617a1b..8813e865c 100644 --- a/stock_request/models/stock_request_order.py +++ b/stock_request/models/stock_request_order.py @@ -201,30 +201,28 @@ class StockRequestOrder(models.Model): @api.multi def action_confirm(self): - for line in self.stock_request_ids: - line.action_confirm() - self.state = 'open' + self.mapped('stock_request_ids').action_confirm() + self.write({'state': 'open'}) return True def action_draft(self): - for line in self.stock_request_ids: - line.action_draft() - self.state = 'draft' + self.mapped('stock_request_ids').action_draft() + self.write({'state': 'draft'}) return True def action_cancel(self): - for line in self.stock_request_ids: - line.action_cancel() - self.state = 'cancel' + self.mapped('stock_request_ids').action_cancel() + self.write({'state': 'cancel'}) return True def action_done(self): - self.state = 'done' + self.write({'state': 'done'}) return True def check_done(self): - if not self.stock_request_ids.filtered(lambda r: r.state != 'done'): - self.action_done() + for rec in self: + if not rec.stock_request_ids.filtered(lambda r: r.state != 'done'): + rec.action_done() return @api.multi diff --git a/stock_request/tests/test_stock_request.py b/stock_request/tests/test_stock_request.py index 51b98fa6f..5f05df4d1 100644 --- a/stock_request/tests/test_stock_request.py +++ b/stock_request/tests/test_stock_request.py @@ -880,3 +880,99 @@ class TestStockRequestBase(TestStockRequest): order.stock_request_ids.onchange_warehouse_id() self.assertEqual( order.stock_request_ids[0].location_id, self.virtual_loc) + + def test_cancellation(self): + group = self.env['procurement.group'].create({ + 'name': 'Procurement group' + }) + product2 = self._create_product('SH2', 'Shoes2', False) + product3 = self._create_product('SH3', 'Shoes3', False) + self.product.type = 'consu' + product2.type = 'consu' + product3.type = 'consu' + vals = { + 'company_id': self.main_company.id, + 'warehouse_id': self.warehouse.id, + 'location_id': self.virtual_loc.id, + 'procurement_group_id': group.id, + 'stock_request_ids': [(0, 0, { + 'product_id': self.product.id, + 'product_uom_id': self.product.uom_id.id, + 'procurement_group_id': group.id, + 'product_uom_qty': 5.0, + 'company_id': self.main_company.id, + 'warehouse_id': self.warehouse.id, + 'location_id': self.virtual_loc.id, + }), (0, 0, { + 'product_id': product2.id, + 'product_uom_id': self.product.uom_id.id, + 'procurement_group_id': group.id, + 'product_uom_qty': 5.0, + 'company_id': self.main_company.id, + 'warehouse_id': self.warehouse.id, + 'location_id': self.virtual_loc.id, + }), (0, 0, { + 'product_id': product3.id, + 'product_uom_id': self.product.uom_id.id, + 'procurement_group_id': group.id, + 'product_uom_qty': 5.0, + 'company_id': self.main_company.id, + 'warehouse_id': self.warehouse.id, + 'location_id': self.virtual_loc.id, + })] + } + order = self.request_order.create(vals) + self.product.route_ids = [(6, 0, self.route.ids)] + product2.route_ids = [(6, 0, self.route.ids)] + product3.route_ids = [(6, 0, self.route.ids)] + order.action_confirm() + picking = order.picking_ids + self.assertEqual(1, len(picking)) + picking.action_assign() + self.assertEqual(3, len(picking.move_lines)) + line = picking.move_lines.filtered( + lambda r: r.product_id == self.product) + line.quantity_done = 1 + sr1 = order.stock_request_ids.filtered( + lambda r: r.product_id == self.product) + sr2 = order.stock_request_ids.filtered( + lambda r: r.product_id == product2) + sr3 = order.stock_request_ids.filtered( + lambda r: r.product_id == product3) + self.assertNotEqual(sr1.state, 'done') + self.assertNotEqual(sr2.state, 'done') + self.assertNotEqual(sr3.state, 'done') + self.env['stock.backorder.confirmation'].create( + {'pick_ids': [(4, picking.id)]} + ).process() + sr1.refresh() + sr2.refresh() + sr3.refresh() + self.assertNotEqual(sr1.state, 'done') + self.assertNotEqual(sr2.state, 'done') + self.assertNotEqual(sr3.state, 'done') + picking = order.picking_ids.filtered(lambda r: r.state not in [ + 'done', 'cancel' + ]) + self.assertEqual(1, len(picking)) + picking.action_assign() + self.assertEqual(3, len(picking.move_lines)) + line = picking.move_lines.filtered( + lambda r: r.product_id == self.product) + line.quantity_done = 4 + line = picking.move_lines.filtered( + lambda r: r.product_id == product2) + line.quantity_done = 1 + + self.env['stock.backorder.confirmation'].create( + {'pick_ids': [(4, picking.id)]} + ).process_cancel_backorder() + sr1.refresh() + sr2.refresh() + sr3.refresh() + self.assertEqual(sr1.state, 'done') + self.assertEqual(sr1.qty_cancelled, 0) + self.assertEqual(sr2.state, 'done') + self.assertEqual(sr2.qty_cancelled, 4) + self.assertEqual(sr3.state, 'done') + self.assertEqual(sr3.qty_cancelled, 5) diff --git a/stock_request/views/stock_request_views.xml b/stock_request/views/stock_request_views.xml index 80eec6f76..f18489134 100644 --- a/stock_request/views/stock_request_views.xml +++ b/stock_request/views/stock_request_views.xml @@ -20,6 +20,7 @@ + @@ -109,8 +110,9 @@ options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/> - - + + +