diff --git a/oca_dependencies.txt b/oca_dependencies.txt new file mode 100644 index 00000000..a5bd2445 --- /dev/null +++ b/oca_dependencies.txt @@ -0,0 +1 @@ +manufacture https://github.com/OCA/manufacture 13.0 diff --git a/rma_account/wizards/rma_add_account_move.py b/rma_account/wizards/rma_add_account_move.py index 894b5e43..eac2754d 100644 --- a/rma_account/wizards/rma_add_account_move.py +++ b/rma_account/wizards/rma_add_account_move.py @@ -82,8 +82,12 @@ class RmaAddAccountMove(models.TransientModel): "uom_id": line.product_uom_id.id, "operation_id": operation.id, "product_qty": line.quantity, - "price_unit": line.move_id.currency_id.compute( - line.price_unit, line.currency_id, round=False + "price_unit": line.move_id.currency_id._convert( + line.price_unit, + line.currency_id, + line.company_id, + line.date, + round=False, ), "delivery_address_id": line.move_id.partner_id.id, "invoice_address_id": line.move_id.partner_id.id, diff --git a/rma_repair/README.rst b/rma_repair/README.rst new file mode 100644 index 00000000..e19e5f22 --- /dev/null +++ b/rma_repair/README.rst @@ -0,0 +1,49 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :alt: License LGPL-3 + +========== +RMA Repair +========== + +This module allows you to create repairs from one or more RMA lines. + +Installation +============ + +This module depends on ``repair_refurbish`` which is available at +`OCA/manufacture `_. + +Usage +===== + +To create repairs from RMA lines: + +#. Go to an approved RMA. +#. Click on *Create Repair Order*. +#. Fill the required information in the lines. +#. Hit *Create Repair Orders*. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Jordi Ballester Alomar +* Aaron Henriquez +* Lois Rilo +* Akim Juillerat +* Bhavesh Odedra + +Maintainer +---------- + +This module is maintained by Eficent diff --git a/rma_repair/__init__.py b/rma_repair/__init__.py new file mode 100644 index 00000000..8051eeb4 --- /dev/null +++ b/rma_repair/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models +from . import wizards diff --git a/rma_repair/__manifest__.py b/rma_repair/__manifest__.py new file mode 100644 index 00000000..73341db5 --- /dev/null +++ b/rma_repair/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "RMA Repair", + "version": "13.0.1.0.0", + "license": "LGPL-3", + "category": "RMA", + "summary": "Links RMA with Repairs.", + "author": "ForgeFlow S.L., Odoo Community Association (OCA)", + "website": "https://github.com/Eficent/stock-rma", + "depends": ["rma_account", "repair_refurbish"], + "data": [ + "views/rma_order_view.xml", + "views/rma_operation_view.xml", + "views/repair_view.xml", + "wizards/rma_order_line_make_repair_view.xml", + "views/rma_order_line_view.xml", + "data/repair_sequence.xml", + ], + "installable": True, +} diff --git a/rma_repair/data/repair_sequence.xml b/rma_repair/data/repair_sequence.xml new file mode 100644 index 00000000..23f61a16 --- /dev/null +++ b/rma_repair/data/repair_sequence.xml @@ -0,0 +1,8 @@ + + + + + RO + + + diff --git a/rma_repair/models/__init__.py b/rma_repair/models/__init__.py new file mode 100644 index 00000000..71df692a --- /dev/null +++ b/rma_repair/models/__init__.py @@ -0,0 +1,6 @@ +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from . import repair +from . import rma_order_line +from . import rma_order +from . import rma_operation diff --git a/rma_repair/models/repair.py b/rma_repair/models/repair.py new file mode 100644 index 00000000..f247bf7d --- /dev/null +++ b/rma_repair/models/repair.py @@ -0,0 +1,18 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class RepairOrder(models.Model): + _inherit = "repair.order" + + rma_line_id = fields.Many2one( + comodel_name="rma.order.line", string="RMA", ondelete="restrict" + ) + under_warranty = fields.Boolean( + related="rma_line_id.under_warranty", readonly=False + ) + invoice_status = fields.Selection( + related="invoice_id.state", string="Invoice Status" + ) diff --git a/rma_repair/models/rma_operation.py b/rma_repair/models/rma_operation.py new file mode 100644 index 00000000..c2ee3dd1 --- /dev/null +++ b/rma_repair/models/rma_operation.py @@ -0,0 +1,21 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class RmaOperation(models.Model): + _inherit = "rma.operation" + + repair_type = fields.Selection( + [ + ("no", "Not required"), + ("ordered", "Based on Ordered Quantities"), + ("received", "Based on Received Quantities"), + ], + string="Repair Policy", + default="no", + ) + delivery_policy = fields.Selection( + selection_add=[("repair", "Based on Repair Quantities")] + ) diff --git a/rma_repair/models/rma_order.py b/rma_repair/models/rma_order.py new file mode 100644 index 00000000..6e24b994 --- /dev/null +++ b/rma_repair/models/rma_order.py @@ -0,0 +1,24 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class RmaOrder(models.Model): + _inherit = "rma.order" + + def _compute_repair_count(self): + for rma in self: + repairs = rma.mapped("rma_line_ids.repair_ids") + rma.repair_count = len(repairs) + + repair_count = fields.Integer( + compute="_compute_repair_count", string="# of Repairs" + ) + + def action_view_repair_order(self): + action = self.env.ref("repair.action_repair_order_tree") + result = action.read()[0] + repair_ids = self.mapped("rma_line_ids.repair_ids").ids + result["domain"] = [("id", "in", repair_ids)] + return result diff --git a/rma_repair/models/rma_order_line.py b/rma_repair/models/rma_order_line.py new file mode 100644 index 00000000..ee8f4f86 --- /dev/null +++ b/rma_repair/models/rma_order_line.py @@ -0,0 +1,170 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class RmaOrderLine(models.Model): + _inherit = "rma.order.line" + + @api.depends("repair_ids", "repair_type", "repair_ids.state", "qty_to_receive") + def _compute_qty_to_repair(self): + for line in self: + if line.repair_type == "no": + line.qty_to_repair = 0.0 + elif line.repair_type == "ordered": + qty = line._get_rma_repaired_qty() + line._get_rma_under_repair_qty() + line.qty_to_repair = line.product_qty - qty + elif line.repair_type == "received": + qty = line._get_rma_repaired_qty() + line._get_rma_under_repair_qty() + line.qty_to_repair = line.qty_received - qty + else: + line.qty_to_repair = 0.0 + + @api.depends("repair_ids", "repair_type", "repair_ids.state", "qty_to_receive") + def _compute_qty_repaired(self): + for line in self: + line.qty_repaired = line._get_rma_repaired_qty() + + @api.depends("repair_ids", "repair_type", "repair_ids.state", "qty_to_receive") + def _compute_qty_under_repair(self): + for line in self: + line.qty_under_repair = line._get_rma_under_repair_qty() + + @api.depends("repair_ids") + def _compute_repair_count(self): + for line in self: + line.repair_count = len(line.repair_ids) + + repair_ids = fields.One2many( + comodel_name="repair.order", + inverse_name="rma_line_id", + string="Repair Orders", + readonly=True, + states={"draft": [("readonly", False)]}, + copy=False, + ) + qty_to_repair = fields.Float( + string="Qty To Repair", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_to_repair", + store=True, + ) + qty_under_repair = fields.Float( + string="Qty Under Repair", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_under_repair", + store=True, + ) + qty_repaired = fields.Float( + string="Qty Repaired", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_repaired", + store=True, + help="Quantity repaired or being repaired.", + ) + repair_type = fields.Selection( + selection=[ + ("no", "Not required"), + ("ordered", "Based on Ordered Quantities"), + ("received", "Based on Received Quantities"), + ], + string="Repair Policy", + default="no", + required=True, + ) + repair_count = fields.Integer( + compute="_compute_repair_count", string="# of Repairs" + ) + + delivery_policy = fields.Selection( + selection_add=[("repair", "Based on Repair Quantities")] + ) + qty_to_pay = fields.Float(compute="_compute_qty_to_pay") + qty_to_deliver = fields.Float(compute="_compute_qty_to_deliver") + + @api.depends( + "delivery_policy", + "product_qty", + "type", + "repair_ids", + "repair_ids.state", + "repair_ids.invoice_method", + "repair_type", + "repair_ids.invoice_status", + ) + def _compute_qty_to_pay(self): + for rec in self.filtered(lambda l: l.delivery_policy == "repair"): + qty_to_pay = 0.0 + for repair in rec.repair_ids.filtered( + lambda r: r.invoice_method != "none" and r.invoice_status != "posted" + ): + qty_to_pay += repair.product_qty + rec.qty_to_pay = qty_to_pay + + def action_view_repair_order(self): + action = self.env.ref("repair.action_repair_order_tree") + result = action.read()[0] + repair_ids = self.repair_ids.ids + if len(repair_ids) != 1: + result["domain"] = [("id", "in", repair_ids)] + elif len(repair_ids) == 1: + res = self.env.ref("repair.view_repair_order_form", False) + result["views"] = [(res and res.id or False, "form")] + result["res_id"] = repair_ids[0] + return result + + def _get_rma_repaired_qty(self): + self.ensure_one() + qty = 0.0 + for repair in self.repair_ids.filtered(lambda p: p.state == "done"): + repair_qty = self.uom_id._compute_quantity( + repair.product_qty, repair.product_uom + ) + qty += repair_qty + return qty + + def _get_rma_under_repair_qty(self): + self.ensure_one() + qty = 0.0 + for repair in self.repair_ids.filtered( + lambda p: p.state not in ("draft", "cancel", "done") + ): + repair_qty = self.uom_id._compute_quantity( + repair.product_qty, repair.product_uom + ) + qty += repair_qty + return qty + + @api.onchange("operation_id") + def _onchange_operation_id(self): + result = super(RmaOrderLine, self)._onchange_operation_id() + if self.operation_id: + self.repair_type = self.operation_id.repair_type or "no" + return result + + @api.depends( + "move_ids", + "move_ids.state", + "delivery_policy", + "product_qty", + "type", + "qty_delivered", + "qty_received", + "repair_ids", + "repair_type", + "repair_ids.state", + "qty_to_pay", + "repair_ids.invoice_status", + ) + def _compute_qty_to_deliver(self): + res = super(RmaOrderLine, self)._compute_qty_to_deliver() + for rec in self.filtered(lambda l: l.delivery_policy == "repair"): + rec.qty_to_deliver = rec.qty_repaired - rec.qty_delivered + return res diff --git a/rma_repair/tests/__init__.py b/rma_repair/tests/__init__.py new file mode 100644 index 00000000..f962eaf8 --- /dev/null +++ b/rma_repair/tests/__init__.py @@ -0,0 +1,2 @@ +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) +from . import test_rma_repair diff --git a/rma_repair/tests/test_rma_repair.py b/rma_repair/tests/test_rma_repair.py new file mode 100644 index 00000000..fa32f894 --- /dev/null +++ b/rma_repair/tests/test_rma_repair.py @@ -0,0 +1,271 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields +from odoo.tests import common + + +class TestRmaRepair(common.SingleTransactionCase): + @classmethod + def setUpClass(cls): + super(TestRmaRepair, cls).setUpClass() + + cls.rma_obj = cls.env["rma.order"] + cls.rma_line_obj = cls.env["rma.order.line"] + cls.rma_op = cls.env["rma.operation"] + cls.rma_add_invoice_wiz = cls.env["rma_add_account_move"] + cls.rma_make_repair_wiz = cls.env["rma.order.line.make.repair"] + cls.repair_line_obj = cls.env["repair.line"] + cls.acc_obj = cls.env["account.account"] + cls.inv_obj = cls.env["account.move"] + cls.invl_obj = cls.env["account.move.line"] + cls.product_obj = cls.env["product.product"] + cls.partner_obj = cls.env["res.partner"] + cls.acc_bank_stmt_model = cls.env["account.bank.statement"] + cls.rma_route_cust = cls.env.ref("rma.route_rma_customer") + + # Create partners + cls.customer1 = cls.partner_obj.create({"name": "Customer 1"}) + + # Create RMA group and operation: + cls.rma_group_customer = cls.rma_obj.create( + {"partner_id": cls.customer1.id, "type": "customer"} + ) + cls.operation_1 = cls.rma_op.create( + { + "code": "TEST", + "name": "Repair afer receive", + "type": "customer", + "receipt_policy": "ordered", + "repair_type": "received", + "in_route_id": cls.rma_route_cust.id, + "out_route_id": cls.rma_route_cust.id, + } + ) + cls.operation_2 = cls.rma_op.create( + { + "code": "TEST", + "name": "Repair on order", + "type": "customer", + "receipt_policy": "ordered", + "repair_type": "ordered", + "in_route_id": cls.rma_route_cust.id, + "out_route_id": cls.rma_route_cust.id, + } + ) + cls.operation_3 = cls.rma_op.create( + { + "code": "TEST", + "name": "Deliver after repair", + "type": "customer", + "receipt_policy": "ordered", + "repair_type": "ordered", + "delivery_policy": "repair", + "in_route_id": cls.rma_route_cust.id, + "out_route_id": cls.rma_route_cust.id, + } + ) + # Create products + cls.product_1 = cls.product_obj.create( + { + "name": "Test Product 1", + "type": "product", + "list_price": 100.0, + "rma_customer_operation_id": cls.operation_1.id, + } + ) + cls.product_2 = cls.product_obj.create( + { + "name": "Test Product 2", + "type": "product", + "list_price": 150.0, + "rma_customer_operation_id": cls.operation_2.id, + } + ) + cls.product_3 = cls.product_obj.create( + { + "name": "Test Product 3", + "type": "product", + "list_price": 1.0, + "rma_customer_operation_id": cls.operation_3.id, + } + ) + # Create Invoices: + + cls.company_id = cls.env.user.company_id + cls.currency_id = cls.company_id.currency_id + + cls.inv_customer = cls.env["account.move"].create( + [ + { + "type": "out_invoice", + "partner_id": cls.customer1.id, + "invoice_date": fields.Date.from_string("2016-01-01"), + "currency_id": cls.currency_id.id, + "invoice_line_ids": [ + ( + 0, + None, + { + "product_id": cls.product_1.id, + "product_uom_id": cls.product_1.uom_id.id, + "quantity": 12, + "price_unit": 1000, + }, + ), + ( + 0, + None, + { + "product_id": cls.product_2.id, + "product_uom_id": cls.product_2.uom_id.id, + "quantity": 15.0, + "price_unit": 150.0, + }, + ), + ( + 0, + None, + { + "product_id": cls.product_3.id, + "product_uom_id": cls.product_3.uom_id.id, + "quantity": 1, + "price_unit": 1000, + }, + ), + ], + } + ] + ) + + cls.inv_line_1 = cls.inv_customer.invoice_line_ids[0] + cls.inv_line_2 = cls.inv_customer.invoice_line_ids[1] + cls.inv_line_3 = cls.inv_customer.invoice_line_ids[2] + + cls.rma_group_customer_2 = cls.rma_obj.create( + {"partner_id": cls.customer1.id, "type": "customer"} + ) + cls.bank_journal = cls.env["account.journal"].search( + [("type", "=", "bank")], limit=1 + ) + cls.material = cls.product_obj.create({"name": "Materials", "type": "product"}) + + cls.material.product_tmpl_id.standard_price = 10 + cls.stock_location = cls.env.ref("stock.stock_location_stock") + + def test_01_add_from_invoice_customer(self): + """Test wizard to create RMA from a customer invoice.""" + add_inv = self.rma_add_invoice_wiz.with_context( + { + "customer": True, + "active_ids": self.rma_group_customer.id, + "active_model": "rma.order", + } + ).create({"line_ids": [(6, 0, self.inv_customer.invoice_line_ids.ids)]}) + add_inv.add_lines() + self.assertEqual(len(self.rma_group_customer.rma_line_ids), 3) + rma_1 = self.rma_group_customer.rma_line_ids.filtered( + lambda r: r.product_id == self.product_1 + ) + rma_1.repair_type = self.operation_1.repair_type + self.assertEquals( + rma_1.operation_id, self.operation_1, "Operation should be operation_1" + ) + rma_2 = self.rma_group_customer.rma_line_ids.filtered( + lambda r: r.product_id == self.product_2 + ) + rma_2.repair_type = self.operation_2.repair_type + self.assertEquals( + rma_2.operation_id, self.operation_2, "Operation should be operation_2" + ) + + def test_02_rma_repair_operation(self): + """Test RMA quantities using repair operations.""" + # Received repair_type: + rma_1 = self.rma_group_customer.rma_line_ids.filtered( + lambda r: r.product_id == self.product_1 + ) + self.assertEquals( + rma_1.operation_id.repair_type, "received", "Incorrect Repair operation" + ) + self.assertEquals(rma_1.qty_to_repair, 0.0, "Quantity to repair should be 0.0") + # Ordered repair_type: + rma_2 = self.rma_group_customer.rma_line_ids.filtered( + lambda r: r.product_id == self.product_2 + ) + self.assertEquals( + rma_2.operation_id.repair_type, "ordered", "Incorrect Repair operation" + ) + self.assertEqual(rma_2.qty_to_repair, 15.0) + + def test_03_create_repair_order(self): + """Generate a Repair Order from a customer RMA.""" + rma = self.rma_group_customer.rma_line_ids.filtered( + lambda r: r.product_id == self.product_2 + ) + rma.action_rma_to_approve() + rma.action_rma_approve() + self.assertEqual(rma.repair_count, 0) + self.assertEqual(rma.qty_to_repair, 15.0) + self.assertEqual(rma.qty_repaired, 0.0) + make_repair = self.rma_make_repair_wiz.with_context( + {"customer": True, "active_ids": rma.ids, "active_model": "rma.order.line"} + ).new() + make_repair.make_repair_order() + rma.repair_ids.action_repair_confirm() + self.assertEqual(rma.repair_count, 1) + self.assertEqual(rma.qty_to_repair, 0.0) + self.assertEqual(rma.qty_repaired, 0.0) + self.assertEqual(rma.qty_under_repair, 15.0) + + def test_04_deliver_after_repair(self): + """Only deliver after repair""" + add_inv = self.rma_add_invoice_wiz.with_context( + { + "customer": True, + "active_ids": self.rma_group_customer_2.id, + "active_model": "rma.order", + } + ).create({"line_ids": [(6, 0, self.inv_customer.invoice_line_ids.ids)]}) + add_inv.add_lines() + rma = self.rma_group_customer_2.rma_line_ids.filtered( + lambda r: r.product_id == self.product_3 + ) + rma.operation_id = self.operation_3.id + rma._onchange_operation_id() + rma.action_rma_to_approve() + rma.action_rma_approve() + self.assertEqual(rma.qty_to_deliver, 0.0) + make_repair = self.rma_make_repair_wiz.with_context( + {"customer": True, "active_ids": rma.ids, "active_model": "rma.order.line"} + ).new() + make_repair.make_repair_order() + repair = rma.repair_ids + line = self.repair_line_obj.create( + { + "name": "consume stuff to repair", + "repair_id": repair.id, + "type": "add", + "product_id": self.material.id, + "product_uom": self.material.uom_id.id, + "product_uom_qty": 1.0, + "location_id": self.stock_location.id, + "location_dest_id": self.stock_location.id, + "price_unit": 10.0, + } + ) + line.onchange_product_id() + repair.invoice_method = "after_repair" + repair.action_repair_confirm() + repair.action_repair_start() + repair.action_repair_end() + repair.action_repair_invoice_create() + self.assertEqual(rma.qty_repaired, 1.0) + self.assertEqual(rma.qty_to_deliver, 1.0) + repair.invoice_id.post() + repair.invoice_id.action_invoice_register_payment() + self.assertEqual(repair.invoice_status, "posted") + self.assertEqual(rma.qty_to_pay, 0.0) + self.assertEqual(rma.qty_repaired, 1.0) + self.assertEqual(rma.delivery_policy, "repair") + self.assertEqual(rma.qty_delivered, 0.0) diff --git a/rma_repair/views/repair_view.xml b/rma_repair/views/repair_view.xml new file mode 100644 index 00000000..8a37e7e2 --- /dev/null +++ b/rma_repair/views/repair_view.xml @@ -0,0 +1,16 @@ + + + + + repair.order.form rma_repair + repair.order + + + + + + + + + + diff --git a/rma_repair/views/rma_operation_view.xml b/rma_repair/views/rma_operation_view.xml new file mode 100644 index 00000000..5d1f9060 --- /dev/null +++ b/rma_repair/views/rma_operation_view.xml @@ -0,0 +1,26 @@ + + + + + rma.operation.tree - rma_repair + rma.operation + + + + + + + + + + rma.operation.form - rma_repair + rma.operation + + + + + + + + + diff --git a/rma_repair/views/rma_order_line_view.xml b/rma_repair/views/rma_order_line_view.xml new file mode 100644 index 00000000..d9a5f5a2 --- /dev/null +++ b/rma_repair/views/rma_order_line_view.xml @@ -0,0 +1,52 @@ + + + + + rma.order.line.form - rma_repair + rma.order.line + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + rma.order.line.form - rma_repair + rma.order.line + + +
+
+
+
+ +
diff --git a/rma_repair/views/rma_order_view.xml b/rma_repair/views/rma_order_view.xml new file mode 100644 index 00000000..73546bcc --- /dev/null +++ b/rma_repair/views/rma_order_view.xml @@ -0,0 +1,21 @@ + + + + + rma.order.form - rma_repair + rma.order + + +
+ +
+
+
+ +
diff --git a/rma_repair/wizards/__init__.py b/rma_repair/wizards/__init__.py new file mode 100644 index 00000000..9a165090 --- /dev/null +++ b/rma_repair/wizards/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import rma_order_line_make_repair diff --git a/rma_repair/wizards/rma_order_line_make_repair.py b/rma_repair/wizards/rma_order_line_make_repair.py new file mode 100644 index 00000000..f016d38e --- /dev/null +++ b/rma_repair/wizards/rma_order_line_make_repair.py @@ -0,0 +1,173 @@ +# Copyright 2020 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class RmaLineMakeRepair(models.TransientModel): + _name = "rma.order.line.make.repair" + _description = "Make Repair Order from RMA Line" + + item_ids = fields.One2many( + comodel_name="rma.order.line.make.repair.item", + inverse_name="wiz_id", + string="Items", + ) + + @api.model + def _prepare_item(self, line): + if line.product_id.refurbish_product_id: + to_refurbish = True + refurbish_product_id = line.product_id.refurbish_product_id.id + else: + to_refurbish = refurbish_product_id = False + return { + "line_id": line.id, + "product_id": line.product_id.id, + "product_qty": line.qty_to_repair, + "rma_id": line.rma_id.id, + "out_route_id": line.out_route_id.id, + "product_uom_id": line.uom_id.id, + "partner_id": line.partner_id.id, + "to_refurbish": to_refurbish, + "refurbish_product_id": refurbish_product_id, + "location_id": line.location_id.id, + "location_dest_id": line.location_id.id, + "invoice_method": "after_repair", + } + + @api.model + def default_get(self, fields_list): + res = super(RmaLineMakeRepair, self).default_get(fields_list) + rma_line_obj = self.env["rma.order.line"] + rma_line_ids = self.env.context["active_ids"] or [] + active_model = self.env.context["active_model"] + + if not rma_line_ids: + return res + assert active_model == "rma.order.line", "Bad context propagation" + items = [] + lines = rma_line_obj.browse(rma_line_ids) + for line in lines: + items.append([0, 0, self._prepare_item(line)]) + res["item_ids"] = items + return res + + def make_repair_order(self): + res = [] + repair_obj = self.env["repair.order"] + for item in self.item_ids: + rma_line = item.line_id + data = item._prepare_repair_order(rma_line) + repair = repair_obj.create(data) + res.append(repair.id) + return { + "domain": [("id", "in", res)], + "name": _("Repairs"), + "view_mode": "tree,form", + "res_model": "repair.order", + "view_id": False, + "context": False, + "type": "ir.actions.act_window", + } + + +class RmaLineMakeRepairItem(models.TransientModel): + _name = "rma.order.line.make.repair.item" + _description = "RMA Line Make Repair Item" + + @api.constrains("product_qty") + def _check_prodcut_qty(self): + for rec in self: + if rec.product_qty <= 0.0: + raise ValidationError(_("Quantity must be positive.")) + + @api.onchange("to_refurbish") + def _onchange_to_refurbish(self): + if self.to_refurbish: + self.refurbish_product_id = self.product_id.refurbish_product_id + else: + self.refurbish_product_id = False + + wiz_id = fields.Many2one( + comodel_name="rma.order.line.make.repair", string="Wizard", ondelete="cascade" + ) + line_id = fields.Many2one( + comodel_name="rma.order.line", string="RMA", required=True + ) + rma_id = fields.Many2one( + comodel_name="rma.order", related="line_id.rma_id", string="RMA Order" + ) + product_id = fields.Many2one( + comodel_name="product.product", string="Product", readonly=True + ) + product_qty = fields.Float(string="Quantity to repair", digits="Product UoS") + product_uom_id = fields.Many2one( + comodel_name="uom.uom", string="UoM", readonly=True + ) + out_route_id = fields.Many2one( + comodel_name="stock.location.route", + string="Outbound Route", + domain=[("rma_selectable", "=", True)], + ) + partner_id = fields.Many2one( + comodel_name="res.partner", + string="Customer", + required=False, + domain=[("customer", "=", True)], + readonly=True, + ) + location_id = fields.Many2one( + comodel_name="stock.location", string="Location", required=True + ) + location_dest_id = fields.Many2one( + comodel_name="stock.location", string="Destination location", required=True + ) + to_refurbish = fields.Boolean(string="To Refurbish?") + refurbish_product_id = fields.Many2one( + comodel_name="product.product", string="Refurbished Product" + ) + invoice_method = fields.Selection( + string="Invoice Method", + selection=[ + ("none", "No Invoice"), + ("b4repair", "Before Repair"), + ("after_repair", "After Repair"), + ], + required=True, + help="Selecting 'Before Repair' or 'After Repair' will allow you " + "to generate invoice before or after the repair is done " + "respectively. 'No invoice' means you don't want to generate " + "invoice for this repair order.", + ) + + @api.model + def _prepare_repair_order(self, rma_line): + location_dest = ( + self.location_dest_id + if not self.to_refurbish + else rma_line.product_id.property_stock_refurbish + ) + refurbish_location_dest_id = ( + self.location_dest_id.id if self.to_refurbish else False + ) + addr = rma_line.partner_id.address_get(["delivery", "invoice"]) + return { + "product_id": rma_line.product_id.id, + "partner_id": rma_line.partner_id.id, + "pricelist_id": rma_line.partner_id.property_product_pricelist.id or False, + "product_qty": self.product_qty, + "rma_line_id": rma_line.id, + "product_uom": rma_line.product_id.uom_po_id.id, + "company_id": rma_line.company_id.id, + "location_id": self.location_id.id, + "location_dest_id": location_dest.id, + "refurbish_location_dest_id": refurbish_location_dest_id, + "refurbish_product_id": self.refurbish_product_id.id, + "to_refurbish": self.to_refurbish, + "invoice_method": self.invoice_method, + "address_id": addr["delivery"], + "partner_invoice_id": addr["invoice"], + "lot_id": rma_line.lot_id.id, + } diff --git a/rma_repair/wizards/rma_order_line_make_repair_view.xml b/rma_repair/wizards/rma_order_line_make_repair_view.xml new file mode 100644 index 00000000..8d5cfb99 --- /dev/null +++ b/rma_repair/wizards/rma_order_line_make_repair_view.xml @@ -0,0 +1,49 @@ + + + + + RMA Line Make Repair + rma.order.line.make.repair + form + +
+ + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + Create Repair + ir.actions.act_window + rma.order.line.make.repair + form + new + + + +