diff --git a/rma/__manifest__.py b/rma/__manifest__.py index 8f5c7313..8caaeba5 100644 --- a/rma/__manifest__.py +++ b/rma/__manifest__.py @@ -2,7 +2,7 @@ { 'name': 'Hibou RMAs', - 'version': '13.0.1.3.0', + 'version': '13.0.1.4.0', 'category': 'Warehouse', 'author': 'Hibou Corp.', 'license': 'OPL-1', diff --git a/rma/models/rma.py b/rma/models/rma.py index 1ef70883..7f7779a7 100644 --- a/rma/models/rma.py +++ b/rma/models/rma.py @@ -556,6 +556,12 @@ class RMA(models.Model): 'procure_method': self.template_id.in_procure_method, 'to_refund': self.template_id.in_to_refund, } + + def _get_old_move(self, old_picking, line): + return old_picking.move_lines.filtered( + lambda ol: ol.state == 'done' and + ol.product_id == line.product_id + )[0] def _new_in_moves(self, old_picking, new_picking, move_update): lines = self.lines.filtered(lambda l: l.product_uom_qty >= 1) @@ -564,7 +570,7 @@ class RMA(models.Model): moves = self.env['stock.move'] for l in lines: - return_move = old_picking.move_lines.filtered(lambda ol: ol.state == 'done' and ol.product_id.id == l.product_id.id)[0] + return_move = self._get_old_move(old_picking, l) copy_vals = self._new_in_move_vals(l, new_picking, return_move) copy_vals.update(move_update) r = return_move.copy(copy_vals) diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py index 2f3f70ff..fd434129 100644 --- a/rma_sale/__manifest__.py +++ b/rma_sale/__manifest__.py @@ -2,7 +2,7 @@ { 'name': 'Hibou RMAs for Sale Orders', - 'version': '13.0.1.2.0', + 'version': '13.0.1.3.0', 'category': 'Sale', 'author': 'Hibou Corp.', 'license': 'OPL-1', diff --git a/rma_sale/models/rma.py b/rma_sale/models/rma.py index 997d736a..75f74760 100644 --- a/rma_sale/models/rma.py +++ b/rma_sale/models/rma.py @@ -51,6 +51,7 @@ class RMATemplate(models.Model): raise ValidationError('Product is past the return period.') lines.append((0, 0, { 'product_id': line.product_id.id, + 'sale_line_id': line.id, 'product_uom_id': line.product_uom.id, 'product_uom_qty': qty, })) @@ -164,29 +165,18 @@ class RMA(models.Model): def _so_action_done(self): warnings = [] for rma in self: - sale_orders = rma.sale_order_id if rma.template_id.so_decrement_order_qty: - sale_orders = self.env['sale.order'].browse() for rma_line in rma.lines: - so_lines = rma.sale_order_id.order_line.filtered(lambda l: l.product_id == rma_line.product_id) - qty_remaining = rma_line.product_uom_qty - for sale_line in so_lines: - if qty_remaining == 0: - continue - sale_line_qty = sale_line.product_uom_qty - sale_line_qty = sale_line_qty - qty_remaining + sale_line = rma_line.sale_line_id + if sale_line: + sale_line_qty = sale_line.product_uom_qty - rma_line.product_uom_qty if sale_line_qty < 0: - qty_remaining = abs(sale_line_qty) + warnings.append((rma, rma.sale_order_id, rma_line, abs(sale_line_qty))) sale_line_qty = 0 - else: - qty_remaining = 0 sale_line.with_context(rma_done=True).write({'product_uom_qty': sale_line_qty}) - sale_orders |= sale_line.order_id - if qty_remaining: - warnings.append((rma, rma.sale_order_id, rma_line, qty_remaining)) # Try to invoice if we don't already have an invoice (e.g. from resetting to draft) - if sale_orders and rma.template_id.invoice_done and not rma.invoice_ids: - rma.invoice_ids |= rma._sale_invoice_done(sale_orders) + if rma.sale_order_id and rma.template_id.invoice_done and not rma.invoice_ids: + rma.invoice_ids |= rma._sale_invoice_done(rma.sale_order_id) if warnings: return {'warning': _('Could not reduce all ordered qty:\n %s' % '\n'.join( ['%s %s %s : %s' % (w[0].name, w[1].name, w[2].product_id.display_name, w[3]) for w in warnings]))} @@ -263,3 +253,21 @@ class RMA(models.Model): new_picking = self._new_out_picking(old_picking) self._new_out_moves(old_picking, new_picking, {}) return new_picking + + def _get_old_move(self, old_picking, line): + if self.template_usage != 'sale_order': + return super(RMA, self)._get_old_move(old_picking, line) + return old_picking.move_lines.filtered( + lambda ol: ol.state == 'done' and + ol.product_id == line.product_id and + ol.sale_line_id == line.sale_line_id + )[0] + + +class RMALine(models.Model): + _inherit = 'rma.line' + + sale_line_id = fields.Many2one('sale.order.line', 'Sale Order Line') + sale_line_product_uom_qty = fields.Float('Ordered', related='sale_line_id.product_uom_qty') + sale_line_qty_delivered = fields.Float('Delivered', related='sale_line_id.qty_delivered') + sale_line_qty_invoiced = fields.Float('Invoiced', related='sale_line_id.qty_invoiced') diff --git a/rma_sale/tests/test_rma.py b/rma_sale/tests/test_rma.py index 7cd4d329..d7acfff5 100644 --- a/rma_sale/tests/test_rma.py +++ b/rma_sale/tests/test_rma.py @@ -304,3 +304,76 @@ class TestRMASale(TestRMA): wiz.create_batch() self.assertTrue(rtv_rma.out_picking_id) self.assertEqual(rtv_rma.out_picking_id.partner_id, self.partner2) + + def test_40_product_on_multiple_lines(self): + self.template_sale_return.write({ + 'usage': 'sale_order', + 'so_decrement_order_qty': True, + 'invoice_done': True, + }) + self.assertTrue(self.template_sale_return.in_require_return, "Inbound Require return not set") + + self.product1.write({ + 'type': 'product', + 'invoice_policy': 'delivery', + }) + order = self.env['sale.order'].create({ + 'partner_id': self.partner1.id, + 'partner_invoice_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'order_line': [ + (0, 0, { + 'product_id': self.product1.id, + 'product_uom_qty': 3.0, + 'product_uom': self.product1.uom_id.id, + 'price_unit': 10.0, + }), + (0, 0, { + 'product_id': self.product1.id, + 'product_uom_qty': 2.0, + 'product_uom': self.product1.uom_id.id, + 'price_unit': 13.0, + }), + ] + }) + order.action_confirm() + self.assertTrue(order.state in ('sale', 'done')) + self.assertEqual(len(order.picking_ids), 1, 'Tests only run with single stage delivery.') + + order.picking_ids.action_assign() + out_moves = order.picking_ids.move_ids_without_package + out_moves[0].quantity_done = 3.0 + out_moves[1].quantity_done = 2.0 + order.picking_ids.button_validate() + self.assertEqual(order.picking_ids.state, 'done') + + rma = self.env['rma.rma'].create({ + 'template_id': self.template_sale_return.id, + 'partner_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'sale_order_id': order.id, + }) + self.assertEqual(rma.state, 'draft') + + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.mapped('qty_delivered'), [3.0, 2.0]) + # Partially return line 1 to make sure delivered/order qty get decremented properly on second line + wizard.line_ids[0].product_uom_qty = 1.0 + wizard.line_ids[1].product_uom_qty = 2.0 + wizard.add_lines() + + self.assertEqual(len(rma.lines), 2) + rma.action_confirm() + + self.assertEqual(rma.in_picking_id.state, 'assigned') + in_moves = rma.in_picking_id.move_ids_without_package + in_moves[0].quantity_done = 1 + in_moves[1].quantity_done = 2 + rma.in_picking_id.button_validate() + self.assertEqual(in_moves.mapped('sale_line_id'), order.order_line, "Inbound stock moves not linked to SO") + self.assertEqual(rma.in_picking_id.state, 'done') + self.assertEqual(order.order_line.mapped('qty_delivered'), [2.0, 0.0]) + + rma.action_done() + self.assertEqual(order.order_line.mapped('product_uom_qty'), [2.0, 0.0]) + self.assertEqual(order.order_line.mapped('qty_delivered'), [2.0, 0.0]) diff --git a/rma_sale/views/rma_views.xml b/rma_sale/views/rma_views.xml index 9c39bf9b..97f122f1 100644 --- a/rma_sale/views/rma_views.xml +++ b/rma_sale/views/rma_views.xml @@ -31,6 +31,11 @@