diff --git a/addons/rma/__init__.py b/addons/rma/__init__.py new file mode 100644 index 00000000..a31f3c40 --- /dev/null +++ b/addons/rma/__init__.py @@ -0,0 +1,4 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import models +from . import wizard diff --git a/addons/rma/__manifest__.py b/addons/rma/__manifest__.py new file mode 100644 index 00000000..32e9e729 --- /dev/null +++ b/addons/rma/__manifest__.py @@ -0,0 +1,39 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Return Merchandise Authorizationing', + 'version': '1.0', + 'author': 'Flectra', + 'website': 'https://flectrahq.com', + 'description': """ + Return Merchandise Authorizationing is part of the process of a + consumer returning a product to receive a refund, replacement or repair + during the product's warranty period. + """, + 'category': 'Sales', + 'depends': ['sale_management', 'stock', 'purchase'], + 'data': [ + 'security/ir.model.access.csv', + 'data/mail_message_subtype_data.xml', + 'data/mail_template_data.xml', + 'data/replacement_reason_data.xml', + 'sequences/data_rma_order_sequence.xml', + 'views/rma_request_view.xml', + 'views/replacement_reason_view.xml', + 'views/rma_line_view.xml', + 'views/stock_production_lot_view.xml', + 'views/stock_picking_view.xml', + 'views/warranty_expire_line_view.xml', + 'views/sales_team_view.xml', + 'views/menuitems_view.xml', + + ], + + 'demo': [ + 'demo/product_demo.xml', + 'demo/stock_demo.xml', + 'demo/sale_order_demo.xml', + ], + + 'application': True, +} diff --git a/addons/rma/data/mail_message_subtype_data.xml b/addons/rma/data/mail_message_subtype_data.xml new file mode 100644 index 00000000..f93b5fcc --- /dev/null +++ b/addons/rma/data/mail_message_subtype_data.xml @@ -0,0 +1,52 @@ + + + + + Request Created + + rma.request + + Replacement Request is Created + + + Request Created + 10 + rma.request + + + team_id + + + + Request Confirmed + + rma.request + + Replacement Request has been + Confirmed + + + Request Confirmed + 11 + rma.request + + + team_id + + + + Replacement created + + rma.request + + Products have been replaced + + + Replacement created + 12 + rma.request + + + team_id + + \ No newline at end of file diff --git a/addons/rma/data/mail_template_data.xml b/addons/rma/data/mail_template_data.xml new file mode 100644 index 00000000..27f8bd20 --- /dev/null +++ b/addons/rma/data/mail_template_data.xml @@ -0,0 +1,63 @@ + + + + + Warranty: Product Warranty email + + ${(object.user_id.email and '%s <%s>' % (object.user_id.name, object.user_id.email) or '')|safe} + + ${object.name or 'n/a'} + ${object.partner_id.id} + + + ${object.partner_id.lang} + +

Dear ${object.partner_id.name}

+

We are sorry to hear about the problem you have had + with your product. +

+

We would like to be able to make the necessary + adjustment at no charge to you, + but unfortunately the warranty is expired of + following products: +

+ + + + + + + + + + + + + % for line in object.warranty_expire_line: + + + + + + + % endfor + +
ProductLotQuantityWarranty Date
${line.product_id.name}${line.lot_id.name}${line.qty_expired}${line.warranty_date}
+ % else: +
+

Dear ${object.partner_id.name}

+

We are sorry to hear about the problem you have had + with your product.

+

We would like to be able to make the necessary + adjustment at no charge to you, We are going to + replace your product. We will deliver it to + you as possible. +

+
+ % endif +]]> +
+
+
diff --git a/addons/rma/data/replacement_reason_data.xml b/addons/rma/data/replacement_reason_data.xml new file mode 100644 index 00000000..c1493eb0 --- /dev/null +++ b/addons/rma/data/replacement_reason_data.xml @@ -0,0 +1,50 @@ + + + + Better price available + + + + Incompatible or not useful + + + + Product damaged, but shipping box OK + + + + Item arrived too late + + + + Missing parts or accessories + + + + Both product and shipping box damaged + + + + Wrong item was sent + + + + No longer needed + + + + Item defective or doesn't work + + + + Didn't approve purchase + + + + Performace or quality not adequate + + + + Inaccurate website description + + diff --git a/addons/rma/demo/product_demo.xml b/addons/rma/demo/product_demo.xml new file mode 100644 index 00000000..396eb64b --- /dev/null +++ b/addons/rma/demo/product_demo.xml @@ -0,0 +1,30 @@ + + + + + Demo Product1 + + 100.0 + 200.0 + product + + + Demo product1 + + DEMO-PROD1 + lot + + + Demo Product2 + + 200.0 + 250.0 + product + + + Demo product2 + + DEMO-PROD2 + lot + + diff --git a/addons/rma/demo/sale_order_demo.xml b/addons/rma/demo/sale_order_demo.xml new file mode 100644 index 00000000..70a72a7d --- /dev/null +++ b/addons/rma/demo/sale_order_demo.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + Demo Product1 + + 5 + + 150.00 + + + + + + + + + + + + + + + Demo Product1 + + 5 + + 150.00 + + + + + Demo Product2 + + 10 + + 200.00 + + diff --git a/addons/rma/demo/stock_demo.xml b/addons/rma/demo/stock_demo.xml new file mode 100644 index 00000000..ae3c1e8e --- /dev/null +++ b/addons/rma/demo/stock_demo.xml @@ -0,0 +1,19 @@ + + + + + + + + 20.0 + + + + + + + + 30.0 + + + diff --git a/addons/rma/i18n/rma.pot b/addons/rma/i18n/rma.pot new file mode 100644 index 00000000..abc0550c --- /dev/null +++ b/addons/rma/i18n/rma.pot @@ -0,0 +1,538 @@ +# Translation of Flectra Server. +# This file contains the translation of the following modules: +# * rma +# +msgid "" +msgstr "" +"Project-Id-Version: Flectra Server 1.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-12-25 11:22+0000\n" +"PO-Revision-Date: 2017-12-25 11:22+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: rma +#: model:mail.template,body_html:rma.email_template_notify_warranty_new +msgid "\n" +" % if object.warranty_expire_line:\n" +"
\n" +"

Dear ${object.partner_id.name}

\n" +"

We are sorry to hear about the problem you have had\n" +" with your product.\n" +" We would like to be able to make the necessary\n" +" adjustment at no charge to you,\n" +" but unfortunately the warranty is expired of\n" +" following products:\n" +"

\n" +"
\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" % for line in object.warranty_expire_line:\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" % endfor\n" +" \n" +"
ProductLotQuantityWarranty Date
${line.product_id.name}${line.lot_id.name}${line.qty_expired}${line.warranty_date}
\n" +" % else:\n" +"
\n" +"

Dear ${object.partner_id.name}

\n" +"

We are sorry to hear about the problem you have had\n" +" with your product.\n" +" We would like to be able to make the necessary\n" +" adjustment at no charge to you, We are going to\n" +" replace your product. We will deliver it to\n" +" you as possible.\n" +"

\n" +"
\n" +" % endif\n" +"\n" +" " +msgstr "" + +#. module: rma +#: model:mail.template,subject:rma.email_template_notify_warranty_new +msgid "${object.name or 'n/a'}" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_request.py:147 +#, python-format +msgid "Compose Email" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "Confirm" +msgstr "" + +#. module: rma +#: selection:rma.request,state:0 +msgid "Confirmed" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "Create Replacement" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_create_uid +#: model:ir.model.fields,field_description:rma.field_rma_line_create_uid +#: model:ir.model.fields,field_description:rma.field_rma_request_create_uid +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_create_uid +msgid "Created by" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_create_date +#: model:ir.model.fields,field_description:rma.field_rma_line_create_date +#: model:ir.model.fields,field_description:rma.field_rma_request_create_date +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_create_date +msgid "Created on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_partner_id +msgid "Customer" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_qty_delivered +msgid "Delivered Quantity" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_picking_count +#: model:ir.model.fields,field_description:rma.field_rma_request_picking_ids +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "Delivery" +msgstr "" + +#. module: rma +#: model:product.product,name:rma.demo_product_1 +#: model:product.template,name:rma.demo_product_1_product_template +#: model:stock.inventory.line,product_name:rma.demo_stock_inventory_line_1 +msgid "Demo Product1" +msgstr "" + +#. module: rma +#: model:product.product,name:rma.demo_product_2 +#: model:product.template,name:rma.demo_product_2_product_template +#: model:stock.inventory.line,product_name:rma.demo_stock_inventory_line_2 +msgid "Demo Product2" +msgstr "" + +#. module: rma +#: model:product.product,description:rma.demo_product_1 +#: model:product.template,description:rma.demo_product_1_product_template +msgid "Demo product1\n" +" " +msgstr "" + +#. module: rma +#: model:product.product,description:rma.demo_product_2 +#: model:product.template,description:rma.demo_product_2_product_template +msgid "Demo product2\n" +" " +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_display_name +#: model:ir.model.fields,field_description:rma.field_rma_line_display_name +#: model:ir.model.fields,field_description:rma.field_rma_request_display_name +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_display_name +msgid "Display Name" +msgstr "" + +#. module: rma +#: selection:rma.request,state:0 +msgid "Draft" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_qty_expired +msgid "Expired Quantity" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Group By" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_id +#: model:ir.model.fields,field_description:rma.field_rma_line_id +#: model:ir.model.fields,field_description:rma.field_rma_request_id +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_id +msgid "ID" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason___last_update +#: model:ir.model.fields,field_description:rma.field_rma_line___last_update +#: model:ir.model.fields,field_description:rma.field_rma_request___last_update +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line___last_update +msgid "Last Modified on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_write_uid +#: model:ir.model.fields,field_description:rma.field_rma_line_write_uid +#: model:ir.model.fields,field_description:rma.field_rma_request_write_uid +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_write_date +#: model:ir.model.fields,field_description:rma.field_rma_line_write_date +#: model:ir.model.fields,field_description:rma.field_rma_request_write_date +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_write_date +msgid "Last Updated on" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_warranty_date +msgid "Lot Warranty Date" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_production_lot +msgid "Lot/Serial" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "My Requests" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/stock_return_picking.py:62 +#, python-format +msgid "No products to replace (only lines in Done state andnot fully replaced yet can be replaced)!" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,help:rma.replacement_request_action_reports +msgid "No replacement request." +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_crm_team_replacements_count +msgid "Number of replacements" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Partner" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_picking_id +msgid "Picking Number" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_product_id +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_product_id +msgid "Product" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_request_replaced +msgid "Products have been replaced" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_line +msgid "RMA Line" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.act_open_rma_line_view +#: model:ir.model.fields,field_description:rma.field_rma_request_rma_line +#: model:ir.ui.view,arch_db:rma.view_rma_line_form +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "RMA Lines" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_name +#: model:ir.model.fields,field_description:rma.field_stock_picking_rma_id +msgid "RMA Order Number" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.act_open_rma_request_view +#: model:ir.actions.act_window,name:rma.action_open_rma_request_replacementteam +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "RMA Request" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_rma_id +msgid "RMA Request Number" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_rma_request +msgid "RMA Reuqest" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_rma_id +msgid "RMA Reuqest Number" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_replacement_reason_name +#: model:ir.model.fields,field_description:rma.field_rma_line_reason_id +#: model:ir.ui.view,arch_db:rma.view_replacement_reason_form +msgid "Reason" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_remark +msgid "Remark" +msgstr "" + +#. module: rma +#: model:ir.ui.menu,name:rma.menu_request_rma +msgid "Replace Request" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_crm_team_use_replacement +#: model:ir.ui.menu,name:rma.rma_request_menu +#: model:ir.ui.view,arch_db:rma.crm_team_replacement_teams_view_kanban +#: model:ir.ui.view,arch_db:rma.replacement_view_kanban +#: selection:rma.request,type:0 +msgid "Replacement" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +#: selection:rma.request,state:0 +msgid "Replacement Created" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_qty_replaced +msgid "Replacement Quantity" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.act_open_replacement_reason_view +#: model:ir.model,name:rma.model_replacement_reason +#: model:ir.ui.menu,name:rma.menu_replacement_reason +msgid "Replacement Reason" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_line_graph +msgid "Replacement Request" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_request_confirm +msgid "Replacement Request has been\n" +" Confirmed" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,description:rma.mt_request_create +msgid "Replacement Request is Created" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.replacement_request_action_reports +msgid "Replacement Requests" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_request_replaced +#: model:mail.message.subtype,name:rma.mt_salesteam_request_replaced +msgid "Replacement created" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.crm_team_replacement_teams_view_kanban +msgid "Replacements" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_line.py:35 +#, python-format +msgid "Replacemet quantity of %s should not be greater than ordered quantity." +msgstr "" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_request_confirm +#: model:mail.message.subtype,name:rma.mt_salesteam_request_confirm +msgid "Request Confirmed" +msgstr "" + +#. module: rma +#: model:mail.message.subtype,name:rma.mt_request_create +#: model:mail.message.subtype,name:rma.mt_salesteam_request +msgid "Request Created" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_date +msgid "Request Date" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Request Month" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_state +msgid "Request Status" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_type +msgid "Request Type" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "Requests for RMA" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_return_picking +msgid "Return Picking" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_sale_order_id +msgid "SO Number" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Sale Order" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_crm_team +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Sales Channel" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_line_filter +msgid "Search RMA Line" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "Search RMA Request" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +msgid "Send By Email" +msgstr "" + +#. module: rma +#: model:ir.ui.view,arch_db:rma.view_rma_request_filter +msgid "State" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_move_line_id +msgid "Stock Move" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_warranty_expire_line_lot_id +msgid "Stock production lot" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_team_id +#: model:ir.model.fields,field_description:rma.field_rma_request_team_id +msgid "Team" +msgstr "" + +#. module: rma +#: model:ir.model,name:rma.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_line_uom_id +msgid "UOM" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_rma_request_user_id +msgid "User" +msgstr "" + +#. module: rma +#: model:ir.actions.act_window,name:rma.act_open_warranty_expire_line_view +#: model:ir.model,name:rma.model_warranty_expire_line +#: model:ir.model.fields,field_description:rma.field_rma_request_warranty_expire_line +#: model:ir.ui.view,arch_db:rma.view_rma_request_form +#: model:ir.ui.view,arch_db:rma.view_warranty_expire_line_form +msgid "Warranty Expire Lines" +msgstr "" + +#. module: rma +#: model:ir.model.fields,field_description:rma.field_stock_production_lot_warranty_date +msgid "Warranty Expiry Date" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_line.py:30 +#, python-format +msgid "You can only replace %s quantity for product %s as its warranty has been expired." +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_request.py:183 +#, python-format +msgid "You cannot delete a request which is not in draft state." +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/stock_return_picking.py:16 +#, python-format +msgid "You may only replace single picking at a time!" +msgstr "" + +#. module: rma +#: code:addons/rma/wizard/stock_return_picking.py:26 +#, python-format +msgid "You may only return Done pickings" +msgstr "" + +#. module: rma +#: code:addons/rma/models/rma_request.py:174 +#, python-format +msgid "You must select rma lines!" +msgstr "" + diff --git a/addons/rma/models/__init__.py b/addons/rma/models/__init__.py new file mode 100644 index 00000000..b65c16e5 --- /dev/null +++ b/addons/rma/models/__init__.py @@ -0,0 +1,9 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import rma_line +from . import warranty_expire_line +from . import rma_request +from . import stock_production_lot +from . import stock_picking +from . import sales_team +from . import replacement_reason diff --git a/addons/rma/models/replacement_reason.py b/addons/rma/models/replacement_reason.py new file mode 100644 index 00000000..5790576b --- /dev/null +++ b/addons/rma/models/replacement_reason.py @@ -0,0 +1,10 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import fields, models + + +class ReplacementReason(models.Model): + _name = "replacement.reason" + _description = "Replacement Reason" + + name = fields.Char(string='Reason') diff --git a/addons/rma/models/rma_line.py b/addons/rma/models/rma_line.py new file mode 100644 index 00000000..3f775ca4 --- /dev/null +++ b/addons/rma/models/rma_line.py @@ -0,0 +1,40 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models, _ +from flectra.exceptions import UserError + + +class RMALine(models.Model): + _name = "rma.line" + _description = "RMA Line" + + product_id = fields.Many2one('product.product', string='Product') + uom_id = fields.Many2one('product.uom', string='UOM') + qty_delivered = fields.Float(string='Delivered Quantity') + qty_replaced = fields.Float(string='Replacement Quantity') + rma_id = fields.Many2one('rma.request', string='RMA Request Number') + move_line_id = fields.Many2one('stock.move', string='Stock Move') + reason_id = fields.Many2one("replacement.reason", sting='Reason for RMA') + remark = fields.Text(sting='Remark') + team_id = fields.Many2one('crm.team', 'Team', related='rma_id.team_id') + + @api.onchange('qty_replaced') + def _onchange_qty_replaced(self): + if self.product_id.tracking != 'none': + replaceable_qty = sum(line.qty_done for line in + self.move_line_id.move_line_ids if + line.lot_id.warranty_date and + line.lot_id.warranty_date >= + self.rma_id.date) + if self.qty_replaced > replaceable_qty: + raise UserError(_('You can only replace %s quantity for ' + 'product %s as its warranty has been ' + 'expired.') % (replaceable_qty, + self.product_id.name)) + elif self.qty_replaced > self.qty_delivered: + raise UserError(_('Replacemet quantity of %s should not be ' + 'greater than ordered quantity.') % + (self.product_id.name)) + + if self.qty_replaced == 0: + raise UserError(_('Replacement quantity should not be 0.')) diff --git a/addons/rma/models/rma_request.py b/addons/rma/models/rma_request.py new file mode 100644 index 00000000..5a855172 --- /dev/null +++ b/addons/rma/models/rma_request.py @@ -0,0 +1,186 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models, _ +from flectra.exceptions import UserError + + +class RmaRequest(models.Model): + _name = "rma.request" + _inherit = ['mail.thread', 'mail.activity.mixin'] + _description = "RMA Request" + + name = fields.Char(string='RMA Order Number') + sale_order_id = fields.Many2one('sale.order', string='SO Number') + picking_id = fields.Many2one('stock.picking', string='Picking Number') + date = fields.Date(string='Request Date', + default=fields.Date.context_today) + partner_id = fields.Many2one('res.partner', string='Customer') + type = fields.Selection([ + ('replacement', 'Replacement') + ], string='Request Type') + rma_line = fields.One2many('rma.line', 'rma_id', string='RMA Lines') + warranty_expire_line = fields.One2many('warranty.expire.line', 'rma_id', + string='Warranty Expire Lines') + state = fields.Selection([ + ('draft', 'Draft'), + ('confirmed', 'Confirmed'), + ('replacement_created', 'Replacement Created'), + ], string='Request Status', track_visibility='onchange', readonly=True, + copy=False, default='draft') + picking_count = fields.Integer(string='Delivery', + compute="_compute_picking") + picking_ids = fields.Many2many('stock.picking', + string='Delivery', + compute="_compute_picking") + user_id = fields.Many2one('res.users', string='User', + default=lambda self: self.env.user) + team_id = fields.Many2one('crm.team', string='Team') + + @api.multi + def _compute_picking(self): + for request in self: + picking_ids = self.env['stock.picking'].search([( + 'rma_id', '=', request.id)]) + request.picking_ids = picking_ids and picking_ids.ids or False + request.picking_count = len(picking_ids) + + @api.onchange('sale_order_id') + def _get_partner(self): + if self.sale_order_id: + self.partner_id = self.sale_order_id.partner_id and \ + self.sale_order_id.partner_id.id or False + self.team_id = self.sale_order_id.team_id and \ + self.sale_order_id.team_id.id or False + + @api.onchange('picking_id') + def _get_rma_lines(self): + if self.picking_id: + move_line_ids = self.env['stock.move'].search([( + 'picking_id', '=', self.picking_id.id)]) + move_lines = [(5, 0, 0)] + for line in move_line_ids: + move_lines.append((0, 0, { + 'product_id': line.product_id.id, + 'uom_id': line.product_uom.id, + 'qty_delivered': line.quantity_done, + 'qty_replaced': sum(line.qty_done for line in + line.move_line_ids if + line.lot_id.warranty_date and + line.lot_id.warranty_date >= self.date + ), + 'rma_id': self.id, + 'move_line_id': line.id + })) + self.rma_line = move_lines + + @api.onchange('rma_line', 'date') + def _get_warranty_lines(self): + warranty_lines = [(5, 0, 0)] + for line in self.rma_line: + if line.move_line_id and \ + line.move_line_id.product_id.tracking != 'none': + for move_line in line.move_line_id.move_line_ids: + if move_line.lot_id.warranty_date and \ + move_line.lot_id.warranty_date < self.date: + warranty_lines.append((0, 0, { + 'product_id': move_line.product_id.id, + 'lot_id': move_line.lot_id.id, + 'warranty_date': + move_line.lot_id.warranty_date, + 'qty_expired': sum(line.qty_done for line in + line.move_line_id.move_line_ids + if line.lot_id.warranty_date and + line.lot_id. + warranty_date < self.date), + 'rma_id': self.id, + })) + self.warranty_expire_line = warranty_lines + + @api.model + def create(self, vals): + vals.update({ + 'name': self.env['ir.sequence'].next_by_code( + 'rma_order') + }) + return super(RmaRequest, self).create(vals) + + @api.multi + def action_create_delivery(self): + self.ensure_one() + action = self.env.ref('stock.action_picking_tree_all') + result = action.read()[0] + if len(self.picking_ids) != 1: + result.update({ + 'domain': "[('id', 'in', " + str(self.picking_ids.ids) + ")]" + }) + elif len(self.picking_ids) == 1: + res = self.env.ref('rma.view_picking_form', False) + result.update({ + 'views': [(res and res.id or False, 'form')], + 'res_id': self.picking_ids.id + }) + return result + + @api.multi + def action_notify_warranty(self): + self.ensure_one() + ir_model_data = self.env['ir.model.data'] + try: + template_id = ir_model_data.get_object_reference( + 'rma', 'email_template_notify_warranty_new')[1] + except ValueError: + template_id = False + try: + compose_form_id = ir_model_data.get_object_reference( + 'mail', 'email_compose_message_wizard_form')[1] + except ValueError: + compose_form_id = False + ctx = dict( + default_model='rma.request', + default_res_id=self.id, + default_use_template=bool(template_id), + default_template_id=template_id, + default_composition_mode='comment', + force_email=True + ) + return { + 'name': _('Compose Email'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'mail.compose.message', + 'views': [(compose_form_id, 'form')], + 'view_id': compose_form_id, + 'target': 'new', + 'context': ctx, + } + + @api.multi + def _track_subtype(self, init_values): + self.ensure_one() + if 'state' in init_values and self.state == 'draft' and self.rma_line: + return 'rma.mt_request_create' + elif 'state' in init_values and self.state == 'confirmed' and \ + self.rma_line: + return 'rma.mt_request_confirm' + elif 'state' in init_values and self.state == 'replacement_created': + return 'rma.mt_request_replaced' + return super(RmaRequest, self)._track_subtype(init_values) + + @api.multi + def action_confirm_request(self): + self.ensure_one() + if not self.rma_line: + raise UserError(_('You must select rma lines!')) + for line in self.rma_line: + line._onchange_qty_replaced() + self.state = 'confirmed' + + @api.multi + def unlink(self): + for request in self: + if request.state != 'draft': + raise UserError(_( + 'You cannot delete a request which is not in draft ' + 'state.')) + return super(RmaRequest, self).unlink() diff --git a/addons/rma/models/sales_team.py b/addons/rma/models/sales_team.py new file mode 100644 index 00000000..42b30d23 --- /dev/null +++ b/addons/rma/models/sales_team.py @@ -0,0 +1,19 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import fields, models + + +class CrmTeam(models.Model): + _inherit = 'crm.team' + + use_replacement = fields.Boolean(string='Replacement') + replacements_count = fields.Integer( + compute='_compute_replacements', + string='Number of replacements') + + def _compute_replacements(self): + for replace in self: + if replace.use_replacement: + rma_ids = self.env['rma.request'].search([ + ('team_id.id', '=', replace.id)]) + replace.replacements_count = len(rma_ids) diff --git a/addons/rma/models/stock_picking.py b/addons/rma/models/stock_picking.py new file mode 100644 index 00000000..4d672511 --- /dev/null +++ b/addons/rma/models/stock_picking.py @@ -0,0 +1,20 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + rma_id = fields.Many2one('rma.request', string='RMA Order Number') + + @api.model + def create(self, vals): + result = super(StockPicking, self).create(vals) + rma_id = False + if self._context.get('active_model') == 'stock.picking': + rma_id = result.rma_id and result.rma_id.id or False + elif self._context.get('active_model') == 'rma.request': + rma_id = self._context.get('active_id') + result.update({'rma_id': rma_id}) + return result diff --git a/addons/rma/models/stock_production_lot.py b/addons/rma/models/stock_production_lot.py new file mode 100644 index 00000000..a769892e --- /dev/null +++ b/addons/rma/models/stock_production_lot.py @@ -0,0 +1,8 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import fields, models + + +class StockProductionLot(models.Model): + _inherit = 'stock.production.lot' + warranty_date = fields.Date(string='Warranty Expiry Date') diff --git a/addons/rma/models/warranty_expire_line.py b/addons/rma/models/warranty_expire_line.py new file mode 100644 index 00000000..751ce681 --- /dev/null +++ b/addons/rma/models/warranty_expire_line.py @@ -0,0 +1,15 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import fields, models + + +class WarrantyExpireLine(models.Model): + _name = "warranty.expire.line" + _description = "Warranty Expire Lines" + + rma_id = fields.Many2one('rma.request', string='RMA Request Number') + product_id = fields.Many2one('product.product', string='Product') + lot_id = fields.Many2one('stock.production.lot', + string='Stock production lot') + qty_expired = fields.Float(string="Expired Quantity") + warranty_date = fields.Date('Lot Warranty Date') diff --git a/addons/rma/security/ir.model.access.csv b/addons/rma/security/ir.model.access.csv new file mode 100644 index 00000000..3df44237 --- /dev/null +++ b/addons/rma/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_request,access_rma_request,model_rma_request,,1,1,1,0 +access_rma_line,access_rma_line,model_rma_line,,1,1,1,0 +access_warranty_expire_line,access_warranty_expire_line,model_warranty_expire_line,,1,1,1,0 +access_replacement_reason,access_replacement_reason,model_replacement_reason,,1,1,1,0 diff --git a/addons/rma/sequences/data_rma_order_sequence.xml b/addons/rma/sequences/data_rma_order_sequence.xml new file mode 100644 index 00000000..377b7fdb --- /dev/null +++ b/addons/rma/sequences/data_rma_order_sequence.xml @@ -0,0 +1,10 @@ + + + + + RMA Order Sequence + rma_order + RMA + 4 + + diff --git a/addons/rma/static/description/icon.png b/addons/rma/static/description/icon.png new file mode 100644 index 00000000..e4097b19 Binary files /dev/null and b/addons/rma/static/description/icon.png differ diff --git a/addons/rma/tests/__init__.py b/addons/rma/tests/__init__.py new file mode 100644 index 00000000..d353a324 --- /dev/null +++ b/addons/rma/tests/__init__.py @@ -0,0 +1,3 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import test_rma_request diff --git a/addons/rma/tests/test_rma_request.py b/addons/rma/tests/test_rma_request.py new file mode 100644 index 00000000..037c0ee9 --- /dev/null +++ b/addons/rma/tests/test_rma_request.py @@ -0,0 +1,172 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra.tests.common import TransactionCase +from datetime import datetime +from dateutil.relativedelta import relativedelta + + +class TestSaleOrder(TransactionCase): + def setUp(self): + super(TestSaleOrder, self).setUp() + + self.demo_product1_id = self.env.ref('rma.demo_product_1') + self.demo_product2_id = self.env.ref('rma.demo_product_2') + + self.product1_lot_id = self.env['stock.production.lot'].create({ + 'name': 'L001', + 'product_id': self.demo_product1_id.id, + 'warranty_date': datetime.now() + relativedelta(years=1) + }) + self.change_prod1_qty_id = self.env[ + 'stock.change.product.qty'].create({ + 'product_id': self.demo_product1_id.id, + 'new_quantity': 10, + 'lot_id': self.product1_lot_id.id + }) + self.change_prod1_qty_id.change_product_qty() + self.sale_order_id_1 = self.env.ref( + 'rma.demo_sale_order_rma_request1') + + self.product2_lot_id = self.env['stock.production.lot'].create({ + 'name': 'L002', + 'product_id': self.demo_product2_id.id, + 'warranty_date': datetime.now() - relativedelta(years=1) + }) + self.change_prod2_qty_id = self.env[ + 'stock.change.product.qty'].create({ + 'product_id': self.demo_product2_id.id, + 'new_quantity': 10, + 'lot_id': self.product2_lot_id.id + }) + self.change_prod2_qty_id.change_product_qty() + self.sale_order_id_2 = self.env.ref( + 'rma.demo_sale_order_rma_request2') + + def test_00_rma_request(self): + self.sale_order_id_1.action_confirm() + picking_ids = self.sale_order_id_1.picking_ids[0] + for move_line in picking_ids.move_lines[0].move_line_ids: + move_line.lot_id = self.product1_lot_id.id + move_line.qty_done = 5 + picking_ids.button_validate() + picking_ids.action_done() + + self.rma_id_1 = self.env['rma.request'].create({ + 'sale_order_id': self.sale_order_id_1.id, + 'picking_id': picking_ids[0].id, + 'date': datetime.now() - relativedelta(days=15), + 'partner_id': self.sale_order_id_1.partner_id.id, + 'type': 'replacement' + }) + self.assertEquals(self.rma_id_1.state, 'draft') + + self.rma_id_1._get_rma_lines() + self.assertTrue((len(self.rma_id_1.rma_line.ids)) != 0, + 'You can not create replacement request!') + + self.rma_id_1._get_warranty_lines() + self.assertEquals((len(self.rma_id_1.warranty_expire_line.ids)), 0, + 'This Replacement request should not have expiry ' + 'product!') + + for rma_line in self.rma_id_1.rma_line: + replaceable_qty = sum(line.qty_done for line in + rma_line.move_line_id.move_line_ids if + line.lot_id.warranty_date and + line.lot_id.warranty_date >= + self.rma_id_1.date) + self.assertTrue(rma_line.qty_replaced <= replaceable_qty, + "You can only replace %d quantity for %s" % + (replaceable_qty, rma_line.product_id.name)) + + self.rma_id_1.action_confirm_request() + self.assertEquals(self.rma_id_1.state, 'confirmed') + + context = {"active_model": 'rma.request', + "active_ids": [self.rma_id_1.id], "active_id": + self.rma_id_1.id, "rma": True} + self.return_picking_id_1 = self.env[ + 'stock.return.picking'].with_context(context).create(dict( + picking_id=self.rma_id_1.picking_id.id, + )) + self.return_picking_id_1.create_returns() + self.assertEquals(self.rma_id_1.state, 'replacement_created') + self.assertTrue(len(self.sale_order_id_1.picking_ids.ids) > 1, + 'Product has not been replaced yet') + + incoming_shipment = False + for pick in self.sale_order_id_1.picking_ids: + if pick.picking_type_code == 'incoming': + incoming_shipment = True + for move_line in pick.move_lines[0].move_line_ids: + move_line.lot_id = self.product1_lot_id.id + move_line.qty_done = 5 + pick.button_validate() + pick.action_done() + self.assertTrue(incoming_shipment, 'Incoming shipment is not created') + + def test_01_rma_request(self): + self.sale_order_id_2.action_confirm() + picking_ids = self.sale_order_id_2.picking_ids[0] + for move_line in picking_ids.move_lines[0].move_line_ids: + move_line.lot_id = self.product2_lot_id.id + move_line.qty_done = 10 + for move_line in picking_ids.move_lines[1].move_line_ids: + move_line.lot_id = self.product1_lot_id.id + move_line.qty_done = 5 + picking_ids.button_validate() + picking_ids.action_done() + + self.rma_id_2 = self.env['rma.request'].create({ + 'sale_order_id': self.sale_order_id_2.id, + 'picking_id': picking_ids[0].id, + 'date': datetime.now() - relativedelta(days=10), + 'partner_id': self.sale_order_id_2.partner_id.id, + 'type': 'replacement' + }) + self.assertEquals(self.rma_id_2.state, 'draft') + self.rma_id_2._get_rma_lines() + self.assertTrue((len(self.rma_id_2.rma_line.ids)) != 0, + 'You can not create replacement request!') + + self.rma_id_2._get_warranty_lines() + self.assertEquals((len(self.rma_id_2.warranty_expire_line.ids)), 1, + 'Replacement request must have expiry product!') + + for rma_line in self.rma_id_2.rma_line: + replaceable_qty = sum(line.qty_done for line in + rma_line.move_line_id.move_line_ids if + line.lot_id.warranty_date and + line.lot_id.warranty_date >= + self.rma_id_2.date) + self.assertTrue(rma_line.qty_replaced <= replaceable_qty, + "You can only return %d quantity for %s" % + (replaceable_qty, rma_line.product_id.name)) + + self.rma_id_2.state = 'confirmed' + self.assertEquals(self.rma_id_2.state, 'confirmed') + + context = {"active_model": 'rma.request', + "active_ids": [self.rma_id_2.id], "active_id": + self.rma_id_2.id, "rma": True} + self.return_picking_id_2 = self.env[ + 'stock.return.picking'].with_context(context).create(dict( + picking_id=self.rma_id_2.picking_id.id, + )) + self.return_picking_id_2.create_returns() + self.assertEquals(self.rma_id_2.state, 'replacement_created') + self.assertTrue(len(self.sale_order_id_2.picking_ids.ids) > 1, + 'Product has not been replaced yet') + + incoming_shipment = False + for pick in self.sale_order_id_2.picking_ids: + if pick.picking_type_code == 'incoming': + incoming_shipment = True + for move_line in pick.move_lines[0].move_line_ids: + move_line.lot_id = self.product1_lot_id.id + move_line.qty_done = 10 + self.assertEquals(len(pick.move_lines[0].move_line_ids.ids), 1, + 'Only one product can be returned') + pick.button_validate() + pick.action_done() + self.assertTrue(incoming_shipment, 'Incoming shipment is not created') diff --git a/addons/rma/views/menuitems_view.xml b/addons/rma/views/menuitems_view.xml new file mode 100644 index 00000000..b3c81367 --- /dev/null +++ b/addons/rma/views/menuitems_view.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/addons/rma/views/replacement_reason_view.xml b/addons/rma/views/replacement_reason_view.xml new file mode 100644 index 00000000..54c93dde --- /dev/null +++ b/addons/rma/views/replacement_reason_view.xml @@ -0,0 +1,41 @@ + + + + replacement.reason.tree + replacement.reason + tree + + + + + + + + + + replacement.reason.form + replacement.reason + form + + +
+ + + + + +
+
+
+ + + Replacement Reason + ir.actions.act_window + replacement.reason + form + tree,form + [] + {} + +
diff --git a/addons/rma/views/rma_line_view.xml b/addons/rma/views/rma_line_view.xml new file mode 100644 index 00000000..762b375a --- /dev/null +++ b/addons/rma/views/rma_line_view.xml @@ -0,0 +1,73 @@ + + + + + rma.line.form + rma.line + form + + +
+ + + + + + + + + + + + + + + +
+
+
+ + + RMA Lines + ir.actions.act_window + rma.line + form + tree,form + [] + {} + + + + rma.line.search.view + rma.line + + + + + + + + + rma.line.graph + rma.line + + + + + + + + + + Replacement Requests + rma.line + graph,tree,form + +

+ No replacement request. +

+
+ {'search_default_team_id': active_id} +
+
diff --git a/addons/rma/views/rma_request_view.xml b/addons/rma/views/rma_request_view.xml new file mode 100644 index 00000000..71de7f05 --- /dev/null +++ b/addons/rma/views/rma_request_view.xml @@ -0,0 +1,186 @@ + + + + rma.request.tree + rma.request + tree + + + + + + + + + + + + + + + rma.request.form + rma.request + form + + +
+
+
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + rma.request.search.view + rma.request + + + + + + + + + + + + + + + + + + + + + + + RMA Request + ir.actions.act_window + rma.request + form + tree,form + [] + {} + + + + RMA Request + ir.actions.act_window + rma.request + form + tree,form + [] + { + 'search_default_team_id': [active_id], + 'default_team_id': active_id, + } + + + + +
diff --git a/addons/rma/views/sales_team_view.xml b/addons/rma/views/sales_team_view.xml new file mode 100644 index 00000000..b03e371b --- /dev/null +++ b/addons/rma/views/sales_team_view.xml @@ -0,0 +1,79 @@ + + + + replacement.team.form + crm.team + + + +
+ +
+
+
+
+ + + replacement.team.kanban + crm.team + + + + +
+ +
+
+ + + + + + +
+
+
+ + + replacement.kanban + crm.team + + + + + + + + + +
diff --git a/addons/rma/views/stock_picking_view.xml b/addons/rma/views/stock_picking_view.xml new file mode 100644 index 00000000..a1dc36b1 --- /dev/null +++ b/addons/rma/views/stock_picking_view.xml @@ -0,0 +1,15 @@ + + + + + stock.picking.form + stock.picking + + + + + + + + + diff --git a/addons/rma/views/stock_production_lot_view.xml b/addons/rma/views/stock_production_lot_view.xml new file mode 100644 index 00000000..ffcf1729 --- /dev/null +++ b/addons/rma/views/stock_production_lot_view.xml @@ -0,0 +1,13 @@ + + + + stock.production.lot.form.inherit + stock.production.lot + + + + + + + + diff --git a/addons/rma/views/warranty_expire_line_view.xml b/addons/rma/views/warranty_expire_line_view.xml new file mode 100644 index 00000000..256109ce --- /dev/null +++ b/addons/rma/views/warranty_expire_line_view.xml @@ -0,0 +1,47 @@ + + + + warranty.expire..line.tree + warranty.expire.line + tree + + + + + + + + + + + + + + warranty.expire.line.form + warranty.expire.line + form + + +
+ + + + + + + + +
+
+
+ + + Warranty Expire Lines + ir.actions.act_window + warranty.expire.line + form + tree,form + [] + {} + +
diff --git a/addons/rma/wizard/__init__.py b/addons/rma/wizard/__init__.py new file mode 100644 index 00000000..031c3e3b --- /dev/null +++ b/addons/rma/wizard/__init__.py @@ -0,0 +1,3 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import stock_return_picking diff --git a/addons/rma/wizard/stock_return_picking.py b/addons/rma/wizard/stock_return_picking.py new file mode 100644 index 00000000..d7fc48e5 --- /dev/null +++ b/addons/rma/wizard/stock_return_picking.py @@ -0,0 +1,98 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, models, _ +from flectra.exceptions import UserError + + +class ReturnPicking(models.TransientModel): + _inherit = 'stock.return.picking' + + @api.model + def default_get(self, fields): + if self._context.get('rma') and self._context.get('active_model') ==\ + 'rma.request': + res = {} + if len(self.env.context.get('active_ids', list())) > 1: + raise UserError(_( + "You may only replace single picking at a time!")) + move_dest_exists = False + product_return_moves = [] + rma_id = self.env['rma.request'].browse( + self.env.context.get('active_id')) + picking = rma_id.picking_id + if picking: + res.update({'picking_id': picking.id}) + if picking.state != 'done': + raise UserError(_("You may only return Done pickings")) + + for move_line in picking.move_lines: + qty = move_line.product_qty or 0.00 + if move_line.move_line_ids: + product_ids = [line.product_id for line in + move_line.move_line_ids] + vals = {} + for rma_line in rma_id.rma_line: + vals.update({ + rma_line.product_id.id: rma_line.qty_replaced, + }) + for prod_id in product_ids: + if vals.get(prod_id.id): + qty = vals.get(prod_id.id) + if move_line.scrapped: + continue + if move_line.move_dest_ids: + move_dest_exists = True + quantity = qty - sum( + move_line.move_dest_ids.filtered( + lambda m: m.state in [ + 'partially_available', 'assigned', 'done'] + ).mapped('move_line_ids').mapped('product_qty')) + product = [line.product_id for line in rma_id.rma_line] + for pid in product: + if move_line.product_id.id == pid.id: + product_return_moves.append( + (0, 0, + { + 'product_id': move_line.product_id.id, + 'quantity': quantity, + 'move_id': move_line.id + })) + + if not product_return_moves: + raise UserError(_( + "No products to replace (only lines in Done state and" + "not fully replaced yet can be replaced)!")) + if 'product_return_moves' in fields: + res.update({'product_return_moves': product_return_moves}) + if 'move_dest_exists' in fields: + res.update({'move_dest_exists': move_dest_exists}) + if 'parent_location_id' in fields and picking.\ + location_id.usage == 'internal': + res.update( + { + 'parent_location_id': + picking.picking_type_id.warehouse_id and + picking.picking_type_id.warehouse_id. + view_location_id.id or picking. + location_id.location_id.id + }) + if 'original_location_id' in fields: + res.update( + {'original_location_id': picking.location_id.id}) + if 'location_id' in fields: + location_id = picking.location_id.id + if picking.picking_type_id.return_picking_type_id.\ + default_location_dest_id.return_location: + location_id = picking.picking_type_id.\ + return_picking_type_id.default_location_dest_id.id + res['location_id'] = location_id + return res + return super(ReturnPicking, self).default_get(fields) + + def create_returns(self): + if self._context.get('rma') and self._context.get('active_model') == \ + 'rma.request': + rma_id = self.env['rma.request'].browse( + self.env.context.get('active_id')) + rma_id.state = 'replacement_created' + return super(ReturnPicking, self).create_returns()