From 3c0b22b47e196f963f84594574bf2eef8fb1b348 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Fri, 11 Sep 2020 14:25:40 -0700 Subject: [PATCH] [IMP] rma_sale: Release 13.0.1.2.0! Add additional support for Sale Warranty return/exchange. --- rma_sale/__manifest__.py | 2 +- rma_sale/models/product.py | 10 +++ rma_sale/models/rma.py | 12 ++- rma_sale/tests/test_rma.py | 135 +++++++++++++++++++++++++++- rma_sale/views/portal_templates.xml | 4 +- rma_sale/views/product_views.xml | 2 + rma_sale/views/rma_views.xml | 1 + 7 files changed, 161 insertions(+), 5 deletions(-) diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py index 2cbf748b..e9fa3f5b 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.1.0', + 'version': '13.0.1.2.0', 'category': 'Sale', 'author': 'Hibou Corp.', 'license': 'OPL-1', diff --git a/rma_sale/models/product.py b/rma_sale/models/product.py index 2e338a39..06308b76 100644 --- a/rma_sale/models/product.py +++ b/rma_sale/models/product.py @@ -14,3 +14,13 @@ class ProductTemplate(models.Model): 'A positive number will allow the product to be ' 'returned up to that number of days. A negative ' 'number prevents the return of the product.') + + rma_sale_warranty_validity = fields.Integer(string='RMA Eligible Days (Sale Warranty)', + help='Determines the number of days from the time ' + 'of the sale that the product is eligible to ' + 'be returned for warranty claims. ' + '0 (default) will allow the product to be ' + 'returned for an indefinite period of time. ' + 'A positive number will allow the product to be ' + 'returned up to that number of days. A negative ' + 'number prevents the return of the product.') diff --git a/rma_sale/models/rma.py b/rma_sale/models/rma.py index 03344398..fb800915 100644 --- a/rma_sale/models/rma.py +++ b/rma_sale/models/rma.py @@ -20,6 +20,9 @@ class RMATemplate(models.Model): _inherit = 'rma.template' usage = fields.Selection(selection_add=[('sale_order', 'Sale Order')]) + sale_order_warranty = fields.Boolean(string='Sale Order Warranty', + help='Determines if the regular return validity or ' + 'Warranty validity is used.') so_decrement_order_qty = fields.Boolean(string='SO Decrement Ordered Qty.', help='When completing the RMA, the Ordered Quantity will be decremented by ' 'the RMA qty.') @@ -87,7 +90,10 @@ class RMATemplate(models.Model): return super(RMATemplate, self)._portal_values(request_user, res_id=res_id) def _rma_sale_line_validity(self, so_line): - validity_days = so_line.product_id.rma_sale_validity + if self.sale_order_warranty: + validity_days = so_line.product_id.rma_sale_warranty_validity + else: + validity_days = so_line.product_id.rma_sale_validity if validity_days < 0: return '' elif validity_days > 0: @@ -195,6 +201,10 @@ class RMA(models.Model): pass return sale_orders.mapped('invoice_ids') - original_invoices + def _invoice_values_sale_order(self): + # the RMA invoice API will not be used as invoicing will happen at the SO level + return False + def action_add_so_lines(self): make_line_obj = self.env['rma.sale.make.lines'] for rma in self: diff --git a/rma_sale/tests/test_rma.py b/rma_sale/tests/test_rma.py index 446260d6..7cd4d329 100644 --- a/rma_sale/tests/test_rma.py +++ b/rma_sale/tests/test_rma.py @@ -167,7 +167,140 @@ class TestRMASale(TestRMA): # Existing lot cannot be re-used. with self.assertRaises(ValidationError): rma2.in_picking_id.action_done() - + # RMA cannot be completed because the inbound picking state is confirmed with self.assertRaises(UserError): rma2.action_done() + + def test_30_product_sale_return_warranty(self): + self.template_sale_return.write({ + 'usage': 'sale_order', + 'invoice_done': True, + 'sale_order_warranty': True, + 'in_to_refund': True, + 'so_decrement_order_qty': False, # invoice on decremented delivered not decremented order + 'next_rma_template_id': self.template_rtv.id, + }) + + validity = 100 # eligible for 100 days + warranty_validity = validity + 100 # eligible for 200 days + + self.product1.write({ + 'rma_sale_validity': validity, + 'rma_sale_warranty_validity': warranty_validity, + 'type': 'product', + 'invoice_policy': 'delivery', + 'tracking': 'serial', + 'standard_price': 1.5, + }) + + order = self.env['sale.order'].create({ + 'partner_id': self.partner1.id, + 'partner_invoice_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'user_id': self.user1.id, + 'order_line': [(0, 0, { + 'product_id': self.product1.id, + 'product_uom_qty': 1.0, + 'product_uom': self.product1.uom_id.id, + 'price_unit': 10.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.') + + # Try to RMA item not delivered yet + 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') + # Do not allow warranty return. + self.product1.rma_sale_warranty_validity = -1 + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + + with self.assertRaises(UserError): + wizard.add_lines() + + # Allows returns, but not forever + self.product1.rma_sale_warranty_validity = warranty_validity + original_date_order = order.date_order + order.write({'date_order': original_date_order - timedelta(days=warranty_validity+1)}) + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + with self.assertRaises(UserError): + wizard.add_lines() + + # Allows returns due to date, due to warranty option + order.write({'date_order': original_date_order - timedelta(days=validity+1)}) + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + wizard.add_lines() + + # finish outbound so that we can invoice. + order.picking_ids.action_assign() + pack_opt = order.picking_ids.move_line_ids[0] + lot = self.env['stock.production.lot'].create({ + 'product_id': self.product1.id, + 'name': 'X100', + 'product_uom_id': self.product1.uom_id.id, + 'company_id': self.env.user.company_id.id, + }) + pack_opt.qty_done = 1.0 + pack_opt.lot_id = lot + order.picking_ids.button_validate() + self.assertEqual(order.picking_ids.state, 'done') + self.assertEqual(order.order_line.qty_delivered, 1.0) + + # Invoice the order so that only the core product is invoiced at the end... + self.assertFalse(order.invoice_ids) + wiz = self.env['sale.advance.payment.inv'].with_context(active_ids=order.ids).create({}) + wiz.create_invoices() + order.flush() + self.assertTrue(order.invoice_ids) + order_invoice = order.invoice_ids + + self.assertEqual(rma.lines.product_id, self.product1) + rma.action_confirm() + self.assertTrue(rma.in_picking_id) + self.assertEqual(rma.in_picking_id.state, 'assigned') + pack_opt = rma.in_picking_id.move_line_ids[0] + pack_opt.lot_id = lot.id + pack_opt.qty_done = 1.0 + rma.in_picking_id.button_validate() + self.assertEqual(rma.in_picking_id.state, 'done') + order.flush() + # self.assertEqual(order.order_line.qty_delivered, 0.0) + rma.action_done() + self.assertEqual(rma.state, 'done') + order.flush() + + rma_invoice = rma.invoice_ids + self.assertTrue(rma_invoice) + sale_line = rma_invoice.invoice_line_ids.filtered(lambda l: l.sale_line_ids) + so_line = sale_line.sale_line_ids + self.assertTrue(sale_line) + self.assertEqual(sale_line.price_unit, so_line.price_unit) + + # Invoices do not have their anglo-saxon cost lines until they post + order_invoice.post() + rma_invoice.post() + + # Find the return to vendor RMA + rtv_rma = self.env['rma.rma'].search([('parent_id', '=', rma.id)]) + self.assertTrue(rtv_rma) + self.assertFalse(rtv_rma.out_picking_id) + + wiz = self.env['rma.make.rtv'].with_context(active_model='rma.rma', active_ids=rtv_rma.ids).create({}) + self.assertTrue(wiz.rma_line_ids) + wiz.partner_id = self.partner2 + wiz.create_batch() + self.assertTrue(rtv_rma.out_picking_id) + self.assertEqual(rtv_rma.out_picking_id.partner_id, self.partner2) diff --git a/rma_sale/views/portal_templates.xml b/rma_sale/views/portal_templates.xml index 6340352d..bb97b9b7 100644 --- a/rma_sale/views/portal_templates.xml +++ b/rma_sale/views/portal_templates.xml @@ -57,8 +57,8 @@
- Product image + Product Image + Product Image
diff --git a/rma_sale/views/product_views.xml b/rma_sale/views/product_views.xml index 4fa17000..41ea3e62 100644 --- a/rma_sale/views/product_views.xml +++ b/rma_sale/views/product_views.xml @@ -9,6 +9,7 @@ + @@ -22,6 +23,7 @@ + diff --git a/rma_sale/views/rma_views.xml b/rma_sale/views/rma_views.xml index f97ce8a8..9c39bf9b 100644 --- a/rma_sale/views/rma_views.xml +++ b/rma_sale/views/rma_views.xml @@ -9,6 +9,7 @@ +