From ce71a05b8aedcef8fc918eb7e553c24abbcb59e3 Mon Sep 17 00:00:00 2001 From: Jordi Ballester Date: Tue, 26 Jul 2022 18:13:36 +0200 Subject: [PATCH] [ADD] rma_put_away --- rma_put_away/README.rst | 47 +++++ rma_put_away/__init__.py | 2 + rma_put_away/__manifest__.py | 18 ++ rma_put_away/models/__init__.py | 5 + rma_put_away/models/rma_operation.py | 29 +++ rma_put_away/models/rma_order.py | 31 +++ rma_put_away/models/rma_order_line.py | 119 +++++++++++ rma_put_away/models/stock_move.py | 21 ++ rma_put_away/models/stock_rule.py | 33 +++ rma_put_away/security/ir.model.access.csv | 7 + rma_put_away/tests/__init__.py | 1 + rma_put_away/tests/test_rma_put_away.py | 235 +++++++++++++++++++++ rma_put_away/views/rma_operation_view.xml | 32 +++ rma_put_away/views/rma_order_line_view.xml | 55 +++++ rma_put_away/views/rma_order_view.xml | 25 +++ rma_put_away/wizards/__init__.py | 1 + rma_put_away/wizards/rma_make_put_away.py | 141 +++++++++++++ rma_put_away/wizards/rma_put_away_view.xml | 72 +++++++ 18 files changed, 874 insertions(+) create mode 100644 rma_put_away/README.rst create mode 100644 rma_put_away/__init__.py create mode 100644 rma_put_away/__manifest__.py create mode 100644 rma_put_away/models/__init__.py create mode 100644 rma_put_away/models/rma_operation.py create mode 100644 rma_put_away/models/rma_order.py create mode 100644 rma_put_away/models/rma_order_line.py create mode 100644 rma_put_away/models/stock_move.py create mode 100644 rma_put_away/models/stock_rule.py create mode 100644 rma_put_away/security/ir.model.access.csv create mode 100644 rma_put_away/tests/__init__.py create mode 100644 rma_put_away/tests/test_rma_put_away.py create mode 100644 rma_put_away/views/rma_operation_view.xml create mode 100644 rma_put_away/views/rma_order_line_view.xml create mode 100644 rma_put_away/views/rma_order_view.xml create mode 100644 rma_put_away/wizards/__init__.py create mode 100644 rma_put_away/wizards/rma_make_put_away.py create mode 100644 rma_put_away/wizards/rma_put_away_view.xml diff --git a/rma_put_away/README.rst b/rma_put_away/README.rst new file mode 100644 index 00000000..7b1d0aba --- /dev/null +++ b/rma_put_away/README.rst @@ -0,0 +1,47 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License LGPL-3 + +============ +RMA Put Away +============ + +This module allows you to put away the products after you have received them. + +Configuration +============= + +Go to *RMA / Configuration / Customer Operations* and define there: + +#. The Put Away Policy +#. The route that you wish to use to put away the products. +#. The default destination location (optional). + +Usage +===== + +#. Go to a Customer RMA. +#. Click on *Put Away*. +#. Indicate the quantity that you want to put away and destination location. + +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 +* David Jimenez + + +Maintainer +---------- + +This module is maintained by ForgeFlow diff --git a/rma_put_away/__init__.py b/rma_put_away/__init__.py new file mode 100644 index 00000000..976591c9 --- /dev/null +++ b/rma_put_away/__init__.py @@ -0,0 +1,2 @@ +from . import wizards +from . import models diff --git a/rma_put_away/__manifest__.py b/rma_put_away/__manifest__.py new file mode 100644 index 00000000..2d5821bd --- /dev/null +++ b/rma_put_away/__manifest__.py @@ -0,0 +1,18 @@ +{ + "name": "RMA Put Away", + "version": "14.0.1.0.0", + "license": "LGPL-3", + "category": "RMA", + "summary": "Allows to put away the received products in odoo", + "author": "ForgeFlow", + "website": "https://github.com/ForgeFlow/stock-rma", + "depends": ["rma"], + "data": [ + "security/ir.model.access.csv", + "views/rma_operation_view.xml", + "views/rma_order_line_view.xml", + "views/rma_order_view.xml", + "wizards/rma_put_away_view.xml", + ], + "installable": True, +} diff --git a/rma_put_away/models/__init__.py b/rma_put_away/models/__init__.py new file mode 100644 index 00000000..1946dbcb --- /dev/null +++ b/rma_put_away/models/__init__.py @@ -0,0 +1,5 @@ +from . import rma_operation +from . import stock_move +from . import stock_rule +from . import rma_order_line +from . import rma_order diff --git a/rma_put_away/models/rma_operation.py b/rma_put_away/models/rma_operation.py new file mode 100644 index 00000000..504d6014 --- /dev/null +++ b/rma_put_away/models/rma_operation.py @@ -0,0 +1,29 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). + +from odoo import fields, models + + +class RmaOperation(models.Model): + _inherit = "rma.operation" + + put_away_policy = fields.Selection( + selection=[ + ("no", "Not required"), + ("ordered", "Based on Ordered Quantities"), + ("received", "Based on Received Quantities"), + ], + string="Put Away Policy", + default="no", + ) + put_away_route_id = fields.Many2one( + comodel_name="stock.location.route", + string="Put Away Route", + domain=[("rma_selectable", "=", True)], + default=lambda self: self._default_routes(), + ) + + put_away_location_id = fields.Many2one( + comodel_name="stock.location", + string="Put Away Destination Location", + ) diff --git a/rma_put_away/models/rma_order.py b/rma_put_away/models/rma_order.py new file mode 100644 index 00000000..671701ac --- /dev/null +++ b/rma_put_away/models/rma_order.py @@ -0,0 +1,31 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) +from odoo import fields, models + + +class RmaOrder(models.Model): + _inherit = "rma.order" + + def _compute_put_away_count(self): + for order in self: + pickings = ( + order.mapped("rma_line_ids.move_ids") + .filtered(lambda m: m.is_rma_put_away) + .mapped("picking_id") + ) + order.put_away_count = len(pickings) + + put_away_count = fields.Integer( + compute="_compute_put_away_count", string="# Put Away" + ) + + def action_view_put_away_transfers(self): + self.ensure_one() + action = self.env.ref("stock.action_picking_tree_all") + result = action.sudo().read()[0] + pickings = self.env["stock.picking"] + for line in self.rma_line_ids: + pickings |= line.move_ids.filtered(lambda m: m.is_rma_put_away).mapped( + "picking_id" + ) + return self._view_shipments(result, pickings) diff --git a/rma_put_away/models/rma_order_line.py b/rma_put_away/models/rma_order_line.py new file mode 100644 index 00000000..b74c8eeb --- /dev/null +++ b/rma_put_away/models/rma_order_line.py @@ -0,0 +1,119 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) +from odoo import api, fields, models + + +class RmaOrderLine(models.Model): + _inherit = "rma.order.line" + + @api.depends( + "move_ids", + "move_ids.state", + "move_ids.is_rma_put_away", + "qty_put_away", + "put_away_policy", + "product_qty", + ) + def _compute_qty_to_put_away(self): + for rec in self: + rec.qty_to_put_away = 0.0 + if rec.put_away_policy == "ordered": + rec.qty_to_put_away = rec.product_qty - rec.qty_put_away + elif rec.put_away_policy == "received": + rec.qty_to_put_away = rec.qty_received - rec.qty_put_away + + @api.depends("move_ids", "move_ids.state", "move_ids.is_rma_put_away") + def _compute_qty_in_put_away(self): + product_obj = self.env["uom.uom"] + for rec in self: + qty = 0.0 + for move in rec.move_ids.filtered( + lambda m: m.state in ["draft", "confirmed", "assigned"] + and m.is_rma_put_away + ): + qty += product_obj._compute_quantity(move.product_uom_qty, rec.uom_id) + rec.qty_in_put_away = qty + + @api.depends("move_ids", "move_ids.state", "move_ids.is_rma_put_away") + def _compute_qty_put_away(self): + product_obj = self.env["uom.uom"] + for rec in self: + qty = 0.0 + for move in rec.move_ids.filtered( + lambda m: m.state in ["done"] and m.is_rma_put_away + ): + qty += product_obj._compute_quantity(move.product_uom_qty, rec.uom_id) + rec.qty_put_away = qty + + def _compute_put_away_count(self): + for line in self: + pickings = self.move_ids.filtered(lambda m: m.is_rma_put_away).mapped( + "picking_id" + ) + line.put_away_count = len(pickings) + + qty_to_put_away = fields.Float( + string="Qty To Put Away", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_to_put_away", + store=True, + ) + qty_in_put_away = fields.Float( + string="Qty In Put Away", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_in_put_away", + store=True, + ) + qty_put_away = fields.Float( + string="Qty Put Away", + copy=False, + digits="Product Unit of Measure", + readonly=True, + compute="_compute_qty_put_away", + store=True, + ) + put_away_policy = fields.Selection( + selection=[ + ("no", "Not required"), + ("ordered", "Based on Ordered Quantities"), + ("received", "Based on Received Quantities"), + ], + string="Put Away Policy", + default="no", + required=True, + readonly=False, + ) + put_away_count = fields.Integer( + compute="_compute_put_away_count", string="# Put Aways" + ) + + @api.onchange("operation_id") + def _onchange_operation_id(self): + res = super(RmaOrderLine, self)._onchange_operation_id() + if self.operation_id: + self.put_away_policy = self.operation_id.put_away_policy or "no" + return res + + def action_view_put_away_transfers(self): + action = self.env.ref("stock.action_picking_tree_all") + result = action.sudo().read()[0] + pickings = self.env["stock.picking"] + for line in self: + if line.lot_id: + line.move_ids.lot_ids |= line.lot_id + line.move_ids.move_line_ids.lot_id = line.lot_id + pickings |= line.move_ids.filtered(lambda m: m.is_rma_put_away).mapped( + "picking_id" + ) + # choose the view_mode accordingly + if len(pickings) != 1: + result["domain"] = "[('id', 'in', " + str(pickings.ids) + ")]" + elif len(pickings) == 1: + res = self.env.ref("stock.view_picking_form", False) + result["views"] = [(res and res.id or False, "form")] + result["res_id"] = pickings.ids[0] + return result diff --git a/rma_put_away/models/stock_move.py b/rma_put_away/models/stock_move.py new file mode 100644 index 00000000..6b7f4e7d --- /dev/null +++ b/rma_put_away/models/stock_move.py @@ -0,0 +1,21 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + is_rma_put_away = fields.Boolean( + string="Is RMA Put Away", + copy=False, + help="This Stock Move has been created from a Put Away operation in " + "the RMA.", + ) + + def _is_in_out_rma_move(self, op, states, location_type): + res = super(StockMove, self)._is_in_out_rma_move(op, states, location_type) + if self.is_rma_put_away: + return False + return res diff --git a/rma_put_away/models/stock_rule.py b/rma_put_away/models/stock_rule.py new file mode 100644 index 00000000..10875c5f --- /dev/null +++ b/rma_put_away/models/stock_rule.py @@ -0,0 +1,33 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import models + + +class StockRule(models.Model): + _inherit = "stock.rule" + + def _get_stock_move_values( + self, + product_id, + product_qty, + product_uom, + location_id, + name, + origin, + company_id, + values, + ): + res = super()._get_stock_move_values( + product_id, + product_qty, + product_uom, + location_id, + name, + origin, + company_id, + values, + ) + if "is_rma_put_away" in values: + res["is_rma_put_away"] = values.get("is_rma_put_away") + return res diff --git a/rma_put_away/security/ir.model.access.csv b/rma_put_away/security/ir.model.access.csv new file mode 100644 index 00000000..0b37f6a7 --- /dev/null +++ b/rma_put_away/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_rma_put_away_wizard,rma.order.manager,model_rma_make_put_away_wizard,rma.group_rma_manager,1,1,1,1 +access_rma_put_away_wizard_customer,rma.order.manager,model_rma_make_put_away_wizard,rma.group_rma_customer_user,1,1,1,1 +access_rma_put_away_wizard_supplier,rma.order.manager,model_rma_make_put_away_wizard,rma.group_rma_supplier_user,1,1,1,1 +access_rma_put_away_item_wizard,rma.order.manager,model_rma_make_put_away_item_wizard,rma.group_rma_manager,1,1,1,1 +access_rma_put_away_item_wizard_customer,rma.order.manager,model_rma_make_put_away_item_wizard,rma.group_rma_customer_user,1,1,1,1 +access_rma_put_away_item_wizard_supplier,rma.order.manager,model_rma_make_put_away_item_wizard,rma.group_rma_supplier_user,1,1,1,1 diff --git a/rma_put_away/tests/__init__.py b/rma_put_away/tests/__init__.py new file mode 100644 index 00000000..5dfeea6c --- /dev/null +++ b/rma_put_away/tests/__init__.py @@ -0,0 +1 @@ +from . import test_rma_put_away diff --git a/rma_put_away/tests/test_rma_put_away.py b/rma_put_away/tests/test_rma_put_away.py new file mode 100644 index 00000000..bfb6b901 --- /dev/null +++ b/rma_put_away/tests/test_rma_put_away.py @@ -0,0 +1,235 @@ +from odoo.tests import common + + +class TestRmaPutAway(common.SingleTransactionCase): + @classmethod + def setUpClass(cls): + super(TestRmaPutAway, cls).setUpClass() + + cls.rma_obj = cls.env["rma.order"] + cls.rma_make_picking = cls.env["rma_make_picking.wizard"] + cls.rma_line_obj = cls.env["rma.order.line"] + cls.rma_op_obj = cls.env["rma.operation"] + cls.rma_make_put_away_wiz = cls.env["rma_make_put_away.wizard"] + cls.product_obj = cls.env["product.product"] + cls.partner_obj = cls.env["res.partner"] + + cls.rma_route_cust = cls.env.ref("rma.route_rma_customer") + + # Create customer + cls.customer1 = cls.partner_obj.create({"name": "Customer 1"}) + + # Create products + cls.product_1 = cls.product_obj.create( + {"name": "Test Product 1", "type": "product", "list_price": 100.0} + ) + cls.product_2 = cls.product_obj.create( + { + "name": "Test Product 2", + "type": "product", + "list_price": 150.0, + "tracking": "lot", + } + ) + + cls.lot = cls.env["stock.production.lot"].create( + { + "name": "Lot for tests", + "product_id": cls.product_2.id, + "company_id": cls.env.ref("base.main_company").id, + } + ) + + cls.warehouse = cls.env["stock.warehouse"].create( + { + "name": "Warehouse", + "reception_steps": "one_step", + "delivery_steps": "ship_only", + "code": "WH1", + } + ) + + cls.warehouse_rma = cls.env["stock.warehouse"].create( + { + "name": "Warehouse RMA", + "reception_steps": "one_step", + "delivery_steps": "ship_only", + "code": "WH2", + } + ) + + cls.rma_loc = cls.env["stock.location"].create( + {"name": "WH RMA", "location_id": cls.warehouse_rma.view_location_id.id} + ) + + cls.put_away_loc = cls.env["stock.location"].create( + { + "name": "WH Put Away Location", + "location_id": cls.warehouse.view_location_id.id, + } + ) + + cls.route1 = cls.env["stock.location.route"].create( + { + "name": "Transfer WH1", + "rma_selectable": True, + "sequence": 10, + } + ) + + cls.env["stock.rule"].create( + { + "name": "Transfer", + "route_id": cls.route1.id, + "location_src_id": cls.rma_loc.id, + "location_id": cls.put_away_loc.id, + "action": "pull", + "picking_type_id": cls.warehouse.int_type_id.id, + "procure_method": "make_to_stock", + "warehouse_id": cls.warehouse.id, + } + ) + + # Create RMA group and operation: + cls.rma_group = cls.rma_obj.create({"partner_id": cls.customer1.id}) + + cls.operation_1 = cls.rma_op_obj.create( + { + "code": "TEST1", + "name": "Operation 1", + "type": "customer", + "receipt_policy": "ordered", + "put_away_policy": "received", + "put_away_route_id": cls.route1.id, + "put_away_location_id": cls.put_away_loc.id, + "in_route_id": cls.rma_route_cust.id, + "out_route_id": cls.rma_route_cust.id, + } + ) + cls.operation_2 = cls.rma_op_obj.create( + { + "code": "TEST2", + "name": "Operation 2", + "type": "customer", + "receipt_policy": "ordered", + "put_away_policy": "ordered", + "put_away_route_id": cls.route1.id, + "put_away_location_id": cls.put_away_loc.id, + "in_route_id": cls.rma_route_cust.id, + "out_route_id": cls.rma_route_cust.id, + } + ) + + def test_01_rma_put_away_without_lot(self): + """Generate a Sales Order from a customer RMA.""" + rma = self.rma_line_obj.create( + { + "partner_id": self.customer1.id, + "product_id": self.product_1.id, + "operation_id": self.operation_1.id, + "uom_id": self.product_1.uom_id.id, + "in_route_id": self.operation_1.in_route_id.id, + "out_route_id": self.operation_1.out_route_id.id, + "in_warehouse_id": self.operation_1.in_warehouse_id.id, + "out_warehouse_id": self.operation_1.out_warehouse_id.id, + "location_id": self.rma_loc.id, + } + ) + rma._onchange_operation_id() + rma.action_rma_to_approve() + wizard = self.rma_make_picking.with_context( + { + "active_ids": rma.id, + "active_model": "rma.order.line", + "picking_type": "incoming", + "active_id": 1, + } + ).create({}) + wizard._create_picking() + wizard = self.rma_make_put_away_wiz.with_context( + { + "active_ids": rma.id, + "active_model": "rma.order.line", + "item_ids": [ + 0, + 0, + { + "line_id": rma.id, + "product_id": rma.product_id.id, + "product_qty": rma.product_qty, + "location_id": rma.location_id, + "qty_to_put_away": rma.qty_to_put_away, + "uom_id": rma.uom_id.id, + }, + ], + } + ).create({}) + action = wizard.action_create_put_away() + picking = self.env["stock.picking"].browse([action["res_id"]]) + self.assertEqual(picking.location_dest_id.id, self.put_away_loc.id) + self.assertEqual(picking.location_id.id, self.rma_loc.id) + move = picking.move_ids_without_package + self.assertEqual(move.product_id.id, self.product_1.id) + self.assertEqual(move.product_uom_qty, 1) + move.quantity_done = 1 + self.assertTrue(picking.action_assign()) + self.assertTrue(picking.button_validate()) + + def test_02_rma_put_away_with_lot(self): + """Generate a Sales Order from a customer RMA.""" + rma = self.rma_line_obj.create( + { + "partner_id": self.customer1.id, + "product_id": self.product_2.id, + "lot_id": self.lot.id, + "operation_id": self.operation_1.id, + "uom_id": self.product_2.uom_id.id, + "in_route_id": self.operation_1.in_route_id.id, + "out_route_id": self.operation_1.out_route_id.id, + "in_warehouse_id": self.operation_1.in_warehouse_id.id, + "out_warehouse_id": self.operation_1.out_warehouse_id.id, + "location_id": self.rma_loc.id, + } + ) + rma._onchange_operation_id() + rma._onchange_lot_id() + rma.action_rma_to_approve() + wizard = self.rma_make_picking.with_context( + { + "active_ids": rma.id, + "active_model": "rma.order.line", + "picking_type": "incoming", + "active_id": 1, + } + ).create({}) + wizard._create_picking() + wizard = self.rma_make_put_away_wiz.with_context( + { + "active_ids": rma.id, + "active_model": "rma.order.line", + "item_ids": [ + 0, + 0, + { + "line_id": rma.id, + "product_id": rma.product_id.id, + "product_qty": rma.product_qty, + "lot_id": rma.lot_id.id, + "location_id": rma.location_id, + "qty_to_put_away": rma.qty_to_put_away, + "uom_id": rma.uom_id.id, + }, + ], + } + ).create({}) + action = wizard.action_create_put_away() + picking = self.env["stock.picking"].browse([action["res_id"]]) + self.assertEqual(picking.location_dest_id.id, self.put_away_loc.id) + self.assertEqual(picking.location_id.id, self.rma_loc.id) + move = picking.move_ids_without_package + self.assertEqual(move.product_id.id, self.product_2.id) + self.assertEqual(move.product_uom_qty, 1) + self.assertEqual(move.lot_ids.id, self.lot.id) + move.quantity_done = 1 + move.move_line_ids.lot_id = self.lot + self.assertTrue(picking.button_validate()) diff --git a/rma_put_away/views/rma_operation_view.xml b/rma_put_away/views/rma_operation_view.xml new file mode 100644 index 00000000..a60f2fb0 --- /dev/null +++ b/rma_put_away/views/rma_operation_view.xml @@ -0,0 +1,32 @@ + + + + + rma.operation.tree + rma.operation + + + + + + + + + + rma.operation.form - rma_put_away + rma.operation + + + + + + + + + + + + + + + diff --git a/rma_put_away/views/rma_order_line_view.xml b/rma_put_away/views/rma_order_line_view.xml new file mode 100644 index 00000000..186a0bc0 --- /dev/null +++ b/rma_put_away/views/rma_order_line_view.xml @@ -0,0 +1,55 @@ + + + + + rma.order.line.form + rma.order.line + + + + + + + + + + + + + + + + + + + rma.order.line.select - rma_put_away + rma.order.line + + + + + + + + + + + diff --git a/rma_put_away/views/rma_order_view.xml b/rma_put_away/views/rma_order_view.xml new file mode 100644 index 00000000..9184fda7 --- /dev/null +++ b/rma_put_away/views/rma_order_view.xml @@ -0,0 +1,25 @@ + + + + rma.order.form + rma.order + + + + + + + + + + diff --git a/rma_put_away/wizards/__init__.py b/rma_put_away/wizards/__init__.py new file mode 100644 index 00000000..abe4c211 --- /dev/null +++ b/rma_put_away/wizards/__init__.py @@ -0,0 +1 @@ +from . import rma_make_put_away diff --git a/rma_put_away/wizards/rma_make_put_away.py b/rma_put_away/wizards/rma_make_put_away.py new file mode 100644 index 00000000..c5ceac7b --- /dev/null +++ b/rma_put_away/wizards/rma_make_put_away.py @@ -0,0 +1,141 @@ +# Copyright 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). + +import time + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT + + +class RmaMakePutAway(models.TransientModel): + _name = "rma_make_put_away.wizard" + _description = "Wizard to create put_away from rma lines" + + item_ids = fields.One2many( + comodel_name="rma_make_put_away_item.wizard", + inverse_name="wiz_id", + string="Items", + ) + + @api.returns("rma.order.line") + def _prepare_item(self, line): + values = { + "product_id": line.product_id.id, + "product_qty": line.product_qty, + "location_id": line.operation_id.put_away_location_id.id, + "uom_id": line.uom_id.id, + "qty_to_put_away": line.qty_to_put_away, + "line_id": line.id, + "rma_id": line.rma_id and line.rma_id.id or False, + } + return values + + @api.model + def default_get(self, fields_list): + """Default values for wizard, if there is more than one supplier on + lines the supplier field is empty otherwise is the unique line + supplier. + """ + context = self._context.copy() + res = super(RmaMakePutAway, 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 + context.update({"items_ids": items}) + return res + + def _create_put_away(self): + """Method called when the user clicks on create picking""" + procurements = [] + errors = [] + for item in self.item_ids: + line = item.line_id + if line.state != "approved": + raise ValidationError(_("RMA %s is not approved") % line.name) + procurement = self._prepare_procurement(item) + procurements.append(procurement) + try: + self.env["procurement.group"].run(procurements) + except UserError as error: + errors.append(error.args[0]) + if errors: + raise UserError("\n".join(errors)) + return procurements + + def action_create_put_away(self): + self._create_put_away() + action = self.item_ids.line_id.action_view_put_away_transfers() + return action + + @api.model + def _get_procurement_data(self, item): + line = item.line_id + route = line.operation_id.put_away_route_id + if not route: + raise ValidationError(_("No route specified")) + procurement_data = { + "name": line.rma_id and line.rma_id.name or line.name, + "origin": line.name, + "date_planned": time.strftime(DT_FORMAT), + "product_id": item.product_id.id, + "product_qty": item.product_qty, + "qty_to_put_away": item.product_qty, + "product_uom": line.product_id.product_tmpl_id.uom_id.id, + "location_id": item.location_id.id, + "rma_line_id": line.id, + "route_ids": route, + "company_id": line.company_id, + "is_rma_put_away": True, + } + return procurement_data + + @api.model + def _prepare_procurement(self, item): + line = item.line_id + values = self._get_procurement_data(item) + values = dict(values, rma_line_id=item.line_id, rma_id=item.line_id.rma_id) + procurement = self.env["procurement.group"].Procurement( + item.line_id.product_id, + item.product_qty, + item.line_id.product_id.product_tmpl_id.uom_id, + item.location_id, + values.get("origin"), + values.get("origin"), + line.company_id, + values, + ) + return procurement + + +class RmaMakePutAwayItem(models.TransientModel): + _name = "rma_make_put_away_item.wizard" + _description = "Items to Put Away" + + wiz_id = fields.Many2one("rma_make_put_away.wizard", string="Wizard", required=True) + line_id = fields.Many2one( + "rma.order.line", string="RMA order Line", ondelete="cascade" + ) + rma_id = fields.Many2one("rma.order", related="line_id.rma_id", string="RMA Group") + product_id = fields.Many2one("product.product", string="Product") + product_qty = fields.Float( + related="line_id.product_qty", + string="Quantity Ordered", + copy=False, + digits="Product Unit of Measure", + ) + location_id = fields.Many2one("stock.location", string="Destination Location") + qty_to_put_away = fields.Float( + string="Quantity To put away", digits="Product Unit of Measure" + ) + uom_id = fields.Many2one("uom.uom", string="Unit of Measure") diff --git a/rma_put_away/wizards/rma_put_away_view.xml b/rma_put_away/wizards/rma_put_away_view.xml new file mode 100644 index 00000000..1c6bfbc5 --- /dev/null +++ b/rma_put_away/wizards/rma_put_away_view.xml @@ -0,0 +1,72 @@ + + + + Create Put Away + rma_make_put_away.wizard + +
+ + + + + + + + + + + + +
+
+ +
+
+ + + Create Put Away + ir.actions.act_window + rma_make_put_away.wizard + form + new + + + + + + + rma.order.line.form + rma.order.line + + +
+
+
+
+