diff --git a/rma_free_of_charge_true/__init__.py b/rma_free_of_charge_true/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/rma_free_of_charge_true/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/rma_free_of_charge_true/__manifest__.py b/rma_free_of_charge_true/__manifest__.py
new file mode 100644
index 00000000..a4b7fc3a
--- /dev/null
+++ b/rma_free_of_charge_true/__manifest__.py
@@ -0,0 +1,16 @@
+{
+ "name": "RMA Free of Charge True",
+ "summary": "Make the field free of charge true by default",
+ "version": "14.0.1.1.0",
+ "author": "ForgeFlow, " "Odoo Community Association (OCA)",
+ "website": "https://github.com/ForgeFlow/stock-rma",
+ "category": "RMA",
+ "depends": ["account_move_line_rma_order_line"],
+ "license": "AGPL-3",
+ "data": [
+
+ ],
+ "installable": True,
+ "maintainers": ["DavidJForgeFlow"],
+ "development_status": "Beta",
+}
diff --git a/rma_free_of_charge_true/models/__init__.py b/rma_free_of_charge_true/models/__init__.py
new file mode 100644
index 00000000..afb14633
--- /dev/null
+++ b/rma_free_of_charge_true/models/__init__.py
@@ -0,0 +1 @@
+from . import rma_line_make_purchase_order_item
diff --git a/rma_free_of_charge_true/models/rma_line_make_purchase_order_item.py b/rma_free_of_charge_true/models/rma_line_make_purchase_order_item.py
new file mode 100644
index 00000000..6aa6b351
--- /dev/null
+++ b/rma_free_of_charge_true/models/rma_line_make_purchase_order_item.py
@@ -0,0 +1,7 @@
+from odoo import fields, models
+
+
+class RmaLineMakePurchaseOrderItem(models.TransientModel):
+ _inherit = "rma.order.line.make.purchase.order.item"
+
+ free_of_charge = fields.Boolean(default="True")
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..5bb1cda2
--- /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 recieved products"
+ "in odoo",
+ "author": "ForgeFlow",
+ "website": "https://github.com/ForgeFlow/stock-rma",
+ "depends": ["rma"],
+ "data": [
+ "views/rma_put_away_view.xml",
+ "views/rma_operation_view.xml",
+ "views/rma_order_view.xml",
+ "views/rma_order_line_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..b1fae22f
--- /dev/null
+++ b/rma_put_away/models/__init__.py
@@ -0,0 +1,2 @@
+from . import rma_order_line
+from . import rma_operation
diff --git a/rma_put_away/models/rma_operation.py b/rma_put_away/models/rma_operation.py
new file mode 100644
index 00000000..659ff310
--- /dev/null
+++ b/rma_put_away/models/rma_operation.py
@@ -0,0 +1,33 @@
+# Copyright 2020 ForgeFlow S.L.
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.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",
+ )
+
+ internal_route_id = fields.Many2one(
+ comodel_name="stock.location.route",
+ string="Internal Route",
+ domain=[("rma_selectable", "=", True)],
+ default=lambda self: self._default_routes(),
+ )
+
+ internal_warehouse_id = fields.Many2one(
+ comodel_name="stock.warehouse",
+ string="Internal Destination Warehouse",
+ default=lambda self: self._default_warehouse_id(),
+ )
+
+
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..ba5b84f7
--- /dev/null
+++ b/rma_put_away/models/rma_order_line.py
@@ -0,0 +1,52 @@
+from odoo import _, api, fields, models
+
+
+class RmaOrderLine(models.Model):
+ _inherit = "rma.order.line"
+
+ qty_to_put_away = fields.Float(
+ string="Qty To Put Away",
+ digits="Product Unit of Measure",
+ compute="_compute_qty_to_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,
+ help="Quantity Put Away.",
+ )
+
+ 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,
+ )
+
+ 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
+
+ def _compute_qty_put_away(self):
+ for rec in self:
+ rec.qty_put_away = 0.0
+
+ @api.onchange("operation_id")
+ def _onchange_operation_id(self):
+ result = super(RmaOrderLine, self)._onchange_operation_id()
+ if self.operation_id:
+ self.put_away_policy = self.operation_id.put_away_policy or "no"
+ return result
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..e62b89af
--- /dev/null
+++ b/rma_put_away/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+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,group_rma_manager,1,1,1,1
+access_rma_put_away_wizard_customer,rma.order.manager,model_rma_make_put_away.wizard,group_rma_customer_user,1,1,1,1
+access_rma_put_away_wizard_supplier,rma.order.manager,model_rma_make_put_away.wizard,group_rma_supplier_user,1,1,1,1
+
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..cc5428e9
--- /dev/null
+++ b/rma_put_away/views/rma_operation_view.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ rma.operation.tree - rma_put_away
+ 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..efcb5760
--- /dev/null
+++ b/rma_put_away/views/rma_order_line_view.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ rma.order.line.form - 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..fa72c942
--- /dev/null
+++ b/rma_put_away/views/rma_order_view.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ rma.order.form - rma_put_away
+ rma.order
+
+
+
+
+
+
+
+
+
diff --git a/rma_put_away/views/rma_put_away_view.xml b/rma_put_away/views/rma_put_away_view.xml
new file mode 100644
index 00000000..a489a774
--- /dev/null
+++ b/rma_put_away/views/rma_put_away_view.xml
@@ -0,0 +1,76 @@
+
+
+
+ Create Put Away
+ rma_make_put_away.wizard
+
+
+
+
+
+
+ Create Put Away
+ ir.actions.act_window
+ rma_make_put_away.wizard
+ form
+ new
+
+
+ {'picking_type': 'internal'}
+
+
+
+
+ rma.order.line.form
+ rma.order.line
+
+
+
+
+
+
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..99516110
--- /dev/null
+++ b/rma_put_away/wizards/rma_make_put_away.py
@@ -0,0 +1,177 @@
+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("rma_make_picking.wizard.item", "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,
+ "uom_id": line.uom_id.id,
+ "qty_to_receive": line.qty_to_receive,
+ "qty_to_deliver": line.qty_to_deliver,
+ "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)
+ if len(lines.mapped("partner_id")) > 1:
+ raise ValidationError(
+ _(
+ "Only RMA lines from the same partner can be processed at "
+ "the same time"
+ )
+ )
+ 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"""
+ picking_type = "internal"
+ procurements = []
+ for item in self.item_ids:
+ line = item.line_id
+ if line.state != "approved":
+ raise ValidationError(_("RMA %s is not approved") % line.name)
+ if line.receipt_policy == "no" and picking_type == "incoming":
+ raise ValidationError(_("No shipments needed for this operation"))
+ if line.delivery_policy == "no" and picking_type == "outgoing":
+ raise ValidationError(_("No deliveries needed for this operation"))
+ procurement = self._create_procurement(item, picking_type)
+ procurements.extend(procurement)
+ return procurements
+
+ def action_create_put_away(self):
+ self._create_put_away()
+ action = self.item_ids.line_id.action_view_in_shipments()
+ return action
+
+ @api.model
+ def _get_address(self, item):
+ if item.line_id.customer_to_supplier:
+ delivery_address = item.line_id.supplier_address_id
+ elif item.line_id.supplier_to_customer:
+ delivery_address = item.line_id.customer_address_id
+ elif item.line_id.delivery_address_id:
+ delivery_address = item.line_id.delivery_address_id
+ elif item.line_id.partner_id:
+ delivery_address = item.line_id.partner_id
+ else:
+ raise ValidationError(_("Unknown delivery address"))
+ return delivery_address
+
+ @api.model
+ def _get_address_location(self, delivery_address_id, a_type):
+ if a_type == "supplier":
+ return delivery_address_id.property_stock_supplier
+ elif a_type == "customer":
+ return delivery_address_id.property_stock_customer
+
+ @api.model
+ def _get_procurement_data(self, item, group, qty, picking_type):
+ line = item.line_id
+ delivery_address_id = self._get_address(item)
+ location = self._get_address_location(delivery_address_id, line.type)
+ warehouse = line.out_warehouse_id
+ route = line.out_route_id
+ if not route:
+ raise ValidationError(_("No route specified"))
+ if not warehouse:
+ raise ValidationError(_("No warehouse specified"))
+ procurement_data = {
+ "name": line.rma_id and line.rma_id.name or line.name,
+ "group_id": group,
+ "origin": line.name,
+ "warehouse_id": warehouse,
+ "date_planned": time.strftime(DT_FORMAT),
+ "product_id": item.product_id,
+ "product_qty": qty,
+ "partner_id": delivery_address_id.id,
+ "product_uom": line.product_id.product_tmpl_id.uom_id.id,
+ "location_id": location,
+ "rma_line_id": line.id,
+ "route_ids": route,
+ }
+ return procurement_data
+
+ @api.model
+ def _create_procurement(self, item, picking_type):
+ errors = []
+ group = self.find_procurement_group(item)
+ if not group:
+ pg_data = self._get_procurement_group_data(item)
+ group = self.env["procurement.group"].create(pg_data)
+ qty = item.product_qty
+ values = self._get_procurement_data(item, group, qty, picking_type)
+ values = dict(values, rma_line_id=item.line_id, rma_id=item.line_id.rma_id)
+ # create picking
+ procurements = []
+ try:
+ procurement = group.Procurement(
+ item.line_id.product_id,
+ qty,
+ item.line_id.product_id.product_tmpl_id.uom_id,
+ values.get("location_id"),
+ values.get("origin"),
+ values.get("origin"),
+ self.env.company,
+ values,
+ )
+
+ procurements.append(procurement)
+ self.env["procurement.group"].run(procurements)
+ except UserError as error:
+ errors.append(error.name)
+ if errors:
+ raise UserError("\n".join(errors))
+ return procurements
+
+ def find_procurement_group(self, item):
+ if item.line_id.rma_id:
+ return self.env["procurement.group"].search(
+ [("rma_id", "=", item.line_id.rma_id.id)]
+ )
+ else:
+ return self.env["procurement.group"].search(
+ [("rma_line_id", "=", item.line_id.id)]
+ )
+
+ def _get_procurement_group_data(self, item):
+ group_data = {
+ "partner_id": item.line_id.partner_id.id,
+ "name": item.line_id.rma_id.name or item.line_id.name,
+ "rma_id": item.line_id.rma_id and item.line_id.rma_id.id or False,
+ "rma_line_id": item.line_id.id if not item.line_id.rma_id else False,
+ }
+ return group_data
+