+ You've succesfully placed your RMA ${object.name}
+ on ${object.company_id.name}. Our team will check it and will validate
+ it as soon as possible.
+
+ Do not hesitate to contact us if you have any question.
+
+
+
+
diff --git a/rma/models/res_company.py b/rma/models/res_company.py
index 0a270769..479e7e7c 100644
--- a/rma/models/res_company.py
+++ b/rma/models/res_company.py
@@ -13,11 +13,32 @@ class Company(models.Model):
except ValueError:
return False
+ def _default_rma_mail_receipt_template(self):
+ try:
+ return self.env.ref("rma.mail_template_rma_receipt_notification").id
+ except ValueError:
+ return False
+
+ def _default_rma_mail_draft_template(self):
+ try:
+ return self.env.ref("rma.mail_template_rma_draft_notification").id
+ except ValueError:
+ return False
+
send_rma_confirmation = fields.Boolean(
string="Send RMA Confirmation",
help="When the delivery is confirmed, send a confirmation email "
"to the customer.",
)
+ send_rma_receipt_confirmation = fields.Boolean(
+ string="Send RMA Receipt Confirmation",
+ help="When the RMA receipt is confirmed, send a confirmation email "
+ "to the customer.",
+ )
+ send_rma_draft_confirmation = fields.Boolean(
+ string="Send RMA draft Confirmation",
+ help="When a customer places an RMA, send a notification with it",
+ )
rma_mail_confirmation_template_id = fields.Many2one(
comodel_name="mail.template",
string="Email Template confirmation for RMA",
@@ -25,6 +46,20 @@ class Company(models.Model):
default=_default_rma_mail_confirmation_template,
help="Email sent to the customer once the RMA is confirmed.",
)
+ rma_mail_receipt_confirmation_template_id = fields.Many2one(
+ comodel_name="mail.template",
+ string="Email Template receipt confirmation for RMA",
+ domain="[('model', '=', 'rma')]",
+ default=_default_rma_mail_receipt_template,
+ help="Email sent to the customer once the RMA products are received.",
+ )
+ rma_mail_draft_confirmation_template_id = fields.Many2one(
+ comodel_name="mail.template",
+ string="Email Template draft notification for RMA",
+ domain="[('model', '=', 'rma')]",
+ default=_default_rma_mail_draft_template,
+ help="Email sent to the customer when they place " "an RMA from the portal",
+ )
@api.model
def create(self, vals):
diff --git a/rma/models/res_config_settings.py b/rma/models/res_config_settings.py
index d18ce044..751656bb 100644
--- a/rma/models/res_config_settings.py
+++ b/rma/models/res_config_settings.py
@@ -12,3 +12,15 @@ class ResConfigSettings(models.TransientModel):
rma_mail_confirmation_template_id = fields.Many2one(
related="company_id.rma_mail_confirmation_template_id", readonly=False,
)
+ send_rma_receipt_confirmation = fields.Boolean(
+ related="company_id.send_rma_receipt_confirmation", readonly=False,
+ )
+ rma_mail_receipt_confirmation_template_id = fields.Many2one(
+ related="company_id.rma_mail_receipt_confirmation_template_id", readonly=False,
+ )
+ send_rma_draft_confirmation = fields.Boolean(
+ related="company_id.send_rma_draft_confirmation", readonly=False,
+ )
+ rma_mail_draft_confirmation_template_id = fields.Many2one(
+ related="company_id.rma_mail_draft_confirmation_template_id", readonly=False,
+ )
diff --git a/rma/models/rma.py b/rma/models/rma.py
index c125f93d..4e8068a9 100644
--- a/rma/models/rma.py
+++ b/rma/models/rma.py
@@ -493,7 +493,13 @@ class Rma(models.Model):
# Assign a default team_id which will be the first in the sequence
if "team_id" not in vals:
vals["team_id"] = self.env["rma.team"].search([], limit=1).id
- return super().create(vals_list)
+ rmas = super().create(vals_list)
+ # Send acknowledge when the RMA is created from the portal and the
+ # company has the proper setting active. This context is set by the
+ # `rma_sale` module.
+ if self.env.context.get("from_portal"):
+ rmas._send_draft_email()
+ return rmas
def copy(self, default=None):
team = super().copy(default)
@@ -511,6 +517,16 @@ class Rma(models.Model):
)
return super().unlink()
+ def _send_draft_email(self):
+ """Send customer notifications they place the RMA from the portal"""
+ for rma in self.filtered("company_id.send_rma_draft_confirmation"):
+ rma_template_id = rma.company_id.rma_mail_draft_confirmation_template_id.id
+ rma.with_context(
+ force_send=True,
+ mark_rma_as_sent=True,
+ default_subtype_id=self.env.ref("rma.mt_rma_notification").id,
+ ).message_post_with_template(rma_template_id)
+
def _send_confirmation_email(self):
"""Auto send notifications"""
for rma in self.filtered(lambda p: p.company_id.send_rma_confirmation):
@@ -521,6 +537,18 @@ class Rma(models.Model):
default_subtype_id=self.env.ref("rma.mt_rma_notification").id,
).message_post_with_template(rma_template_id)
+ def _send_receipt_confirmation_email(self):
+ """Send customer notifications when the products are received"""
+ for rma in self.filtered("company_id.send_rma_receipt_confirmation"):
+ rma_template_id = (
+ rma.company_id.rma_mail_receipt_confirmation_template_id.id
+ )
+ rma.with_context(
+ force_send=True,
+ mark_rma_as_sent=True,
+ default_subtype_id=self.env.ref("rma.mt_rma_notification").id,
+ ).message_post_with_template(rma_template_id)
+
# Action methods
def action_rma_send(self):
self.ensure_one()
@@ -1217,6 +1245,16 @@ class Rma(models.Model):
return "RMA Report - %s" % self.name
# Other business methods
+
+ def update_received_state_on_reception(self):
+ """ Invoked by:
+ [stock.move]._action_done
+ Here we can attach methods to trigger when the customer products
+ are received on the RMA location, such as automatic notifications
+ """
+ self.write({"state": "received"})
+ self._send_receipt_confirmation_email()
+
def update_received_state(self):
""" Invoked by:
[stock.move].unlink
diff --git a/rma/models/rma_tag.py b/rma/models/rma_tag.py
index 9f96059e..db7d337b 100644
--- a/rma/models/rma_tag.py
+++ b/rma/models/rma_tag.py
@@ -13,6 +13,9 @@ class RmaTag(models.Model):
help="The active field allows you to hide the category without " "removing it.",
)
name = fields.Char(string="Tag Name", required=True, translate=True, copy=False,)
+ is_public = fields.Boolean(
+ string="Public Tag", help="The tag is visible in the portal view",
+ )
color = fields.Integer(string="Color Index")
rma_ids = fields.Many2many(comodel_name="rma")
diff --git a/rma/models/stock_move.py b/rma/models/stock_move.py
index ade29234..bb4653ea 100644
--- a/rma/models/stock_move.py
+++ b/rma/models/stock_move.py
@@ -68,7 +68,7 @@ class StockMove(models.Model):
.mapped("rma_receiver_ids")
.filtered(lambda r: r.state == "confirmed")
)
- to_be_received.write({"state": "received"})
+ to_be_received.update_received_state_on_reception()
# Set RMAs as delivered
move_done.mapped("rma_id").update_replaced_state()
move_done.mapped("rma_id").update_returned_state()
diff --git a/rma/tests/test_rma.py b/rma/tests/test_rma.py
index 1f07b7be..e2f1d0e5 100644
--- a/rma/tests/test_rma.py
+++ b/rma/tests/test_rma.py
@@ -665,19 +665,50 @@ class TestRma(SavepointCase):
self.assertEqual(rma.product_id.qty_available, 0)
def test_autoconfirm_email(self):
- rma = self._create_rma(self.partner, self.product, 10, self.rma_loc)
- rma.company_id.send_rma_confirmation = True
- rma.company_id.rma_mail_confirmation_template_id = self.env.ref(
+ self.company.send_rma_confirmation = True
+ self.company.send_rma_receipt_confirmation = True
+ self.company.send_rma_draft_confirmation = True
+ self.company.rma_mail_confirmation_template_id = self.env.ref(
"rma.mail_template_rma_notification"
)
+ self.company.rma_mail_receipt_confirmation_template_id = self.env.ref(
+ "rma.mail_template_rma_receipt_notification"
+ )
+ self.company.rma_mail_draft_confirmation_template_id = self.env.ref(
+ "rma.mail_template_rma_draft_notification"
+ )
previous_mails = self.env["mail.mail"].search(
[("partner_ids", "in", self.partner.ids)]
)
self.assertFalse(previous_mails)
- rma.action_confirm()
- mail = self.env["mail.message"].search(
+ # Force the context to mock an RMA created from the portal, which is
+ # feature that we get on `rma_sale`. We drop it after the RMA creation
+ # to avoid uncontrolled side effects
+ ctx = self.env.context
+ self.env.context = dict(ctx, from_portal=True)
+ rma = self._create_rma(self.partner, self.product, 10, self.rma_loc)
+ self.env.context = ctx
+ mail_draft = self.env["mail.message"].search(
[("partner_ids", "in", self.partner.ids)]
)
- self.assertTrue(rma.name in mail.subject)
- self.assertTrue(rma.name in mail.body)
- self.assertEqual(self.env.ref("rma.mt_rma_notification"), mail.subtype_id)
+ rma.action_confirm()
+ mail_confirm = (
+ self.env["mail.message"].search([("partner_ids", "in", self.partner.ids)])
+ - mail_draft
+ )
+ self.assertTrue(rma.name in mail_confirm.subject)
+ self.assertTrue(rma.name in mail_confirm.body)
+ self.assertEqual(
+ self.env.ref("rma.mt_rma_notification"), mail_confirm.subtype_id
+ )
+ # Now we'll confirm the incoming goods picking and the automatic
+ # reception notification should be sent
+ rma.reception_move_id.quantity_done = rma.product_uom_qty
+ rma.reception_move_id.picking_id.button_validate()
+ mail_receipt = (
+ self.env["mail.message"].search([("partner_ids", "in", self.partner.ids)])
+ - mail_draft
+ - mail_confirm
+ )
+ self.assertTrue(rma.name in mail_receipt.subject)
+ self.assertTrue("products received" in mail_receipt.subject)
diff --git a/rma/views/res_config_settings_views.xml b/rma/views/res_config_settings_views.xml
index 744da02b..423dff2d 100644
--- a/rma/views/res_config_settings_views.xml
+++ b/rma/views/res_config_settings_views.xml
@@ -46,6 +46,82 @@
+
+
+
+
+
+
+
+
+ When the RMA products are received, send an automatic information email.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When customers themselves place an RMA from the portal, send an automatic notification acknowleging it.
+
+
+
+
+
+
+
diff --git a/rma/views/rma_portal_templates.xml b/rma/views/rma_portal_templates.xml
index 002a5c80..a102f80d 100644
--- a/rma/views/rma_portal_templates.xml
+++ b/rma/views/rma_portal_templates.xml
@@ -115,6 +115,18 @@
+
+
+
+
+
+
+
@@ -51,6 +52,7 @@
+
diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py
index 75752cc6..fb3af4cf 100644
--- a/rma_sale/__manifest__.py
+++ b/rma_sale/__manifest__.py
@@ -17,6 +17,7 @@
"views/rma_views.xml",
"views/sale_views.xml",
"views/sale_portal_template.xml",
+ "views/res_config_settings_views.xml",
"wizard/sale_order_rma_wizard_views.xml",
],
}
diff --git a/rma_sale/controllers/sale_portal.py b/rma_sale/controllers/sale_portal.py
index e8eec8e6..eb5ab385 100644
--- a/rma_sale/controllers/sale_portal.py
+++ b/rma_sale/controllers/sale_portal.py
@@ -30,12 +30,18 @@ class CustomerPortal(CustomerPortal):
}
# Set wizard line vals
mapped_vals = {}
+ custom_vals = {}
partner_shipping_id = post.pop("partner_shipping_id", False)
for name, value in post.items():
- row, field_name = name.split("-", 1)
- if wizard_line_field_types.get(field_name) == "many2one":
- value = int(value) if value else False
- mapped_vals.setdefault(row, {}).update({field_name: value})
+ try:
+ row, field_name = name.split("-", 1)
+ if wizard_line_field_types.get(field_name) == "many2one":
+ value = int(value) if value else False
+ mapped_vals.setdefault(row, {}).update({field_name: value})
+ # Catch possible form custom fields to add them to the RMA
+ # description values
+ except ValueError:
+ custom_vals.update({name: value})
# If no operation is filled, no RMA will be created
line_vals = [
(0, 0, vals) for vals in mapped_vals.values() if vals.get("operation_id")
@@ -43,11 +49,19 @@ class CustomerPortal(CustomerPortal):
# Create wizard an generate rmas
order = order_obj.browse(order_id).sudo()
location_id = order.warehouse_id.rma_loc_id.id
+ # Add custom fields text
+ custom_description = ""
+ if custom_vals:
+ custom_description = r" --- "
+ custom_description += r" ".join(
+ ["{}: {}".format(x, y) for x, y in custom_vals.items()]
+ )
wizard = wizard_obj.with_context(active_id=order_id).create(
{
"line_ids": line_vals,
"location_id": location_id,
"partner_shipping_id": partner_shipping_id,
+ "custom_description": custom_description,
}
)
rma = wizard.sudo().create_rma(from_portal=True)
@@ -61,3 +75,27 @@ class CustomerPortal(CustomerPortal):
else:
route = "/my/rmas?sale_id=%d" % order_id
return request.redirect(route)
+
+ @http.route(
+ ["/my/requestrma/"], type="http", auth="public", website=True
+ )
+ def request_sale_rma(self, order_id, access_token=None, **kw):
+ """Request RMA on a single page"""
+ try:
+ order_sudo = self._document_check_access(
+ "sale.order", order_id, access_token=access_token
+ )
+ except (AccessError, MissingError):
+ return request.redirect("/my")
+ if order_sudo.state in ("draft", "sent", "cancel"):
+ return request.redirect("/my")
+ values = {
+ "sale_order": order_sudo,
+ "page_name": "request_rma",
+ "default_url": order_sudo.get_portal_url(),
+ "token": access_token,
+ "partner_id": order_sudo.partner_id.id,
+ }
+ if order_sudo.company_id:
+ values["res_company"] = order_sudo.company_id
+ return request.render("rma_sale.request_rma_single_page", values)
diff --git a/rma_sale/models/__init__.py b/rma_sale/models/__init__.py
index 0090dd37..16972d9b 100644
--- a/rma_sale/models/__init__.py
+++ b/rma_sale/models/__init__.py
@@ -1,5 +1,6 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-
+from . import res_company
+from . import res_config_settings
from . import rma
from . import sale
from . import stock_move
diff --git a/rma_sale/models/res_company.py b/rma_sale/models/res_company.py
new file mode 100644
index 00000000..021eea2c
--- /dev/null
+++ b/rma_sale/models/res_company.py
@@ -0,0 +1,13 @@
+# Copyright 2021 Tecnativa - David Vidal
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import fields, models
+
+
+class ResCompany(models.Model):
+ _inherit = "res.company"
+
+ show_full_page_sale_rma = fields.Boolean(
+ string="Full page RMA creation",
+ help="From the frontend sale order page go to a single RMA page "
+ "creation instead of the usual popup",
+ )
diff --git a/rma_sale/models/res_config_settings.py b/rma_sale/models/res_config_settings.py
new file mode 100644
index 00000000..dd42c753
--- /dev/null
+++ b/rma_sale/models/res_config_settings.py
@@ -0,0 +1,11 @@
+# Copyright 2021 Tecnativa - David Vidal
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+
+ show_full_page_sale_rma = fields.Boolean(
+ related="company_id.show_full_page_sale_rma", readonly=False,
+ )
diff --git a/rma_sale/views/res_config_settings_views.xml b/rma_sale/views/res_config_settings_views.xml
new file mode 100644
index 00000000..61618781
--- /dev/null
+++ b/rma_sale/views/res_config_settings_views.xml
@@ -0,0 +1,36 @@
+
+
+
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
+
+ When we hit the RMA request button from the portal sale page, open in a single page instead of a popup.
+