Merge pull request #24725 from blaggacao/chore/generalize-notification-receiver-fields

feat: generalize receiver logic in notifications
This commit is contained in:
Akhil Narang 2024-07-18 15:30:52 +05:30 committed by GitHub
commit 53b054e72e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 34 deletions

View file

@ -14,10 +14,11 @@ frappe.notification = {
let get_select_options = function (df, parent_field) {
// Append parent_field name along with fieldname for child table fields
let select_value = parent_field ? df.fieldname + "," + parent_field : df.fieldname;
let path = parent_field ? parent_field + " > " + df.fieldname : df.fieldname;
return {
value: select_value,
label: df.fieldname + " (" + __(df.label, null, df.parent) + ")",
label: path + " (" + __(df.label, null, df.parent) + ")",
};
};
@ -33,6 +34,38 @@ frappe.notification = {
{ value: "modified", label: `modified (${__("Last Modified Date")})` },
]);
};
let get_receiver_fields = function (
fields,
is_extra_receiver_field = (_) => {
return false;
}
) {
// finds receiver fields from the fields or any child table
// by default finds any link to the User doctype
// however an additional optional predicate can be passed as argument
// to find additional fields
let is_receiver_field = function (df) {
return (
is_extra_receiver_field(df) ||
(df.options == "User" && df.fieldtype == "Link") ||
(df.options == "Customer" && df.fieldtype == "Link")
);
};
let extract_receiver_field = function (df) {
// Add recipients from child doctypes into select dropdown
if (frappe.model.table_fields.includes(df.fieldtype)) {
let child_fields = frappe.get_doc("DocType", df.options).fields;
return $.map(child_fields, function (cdf) {
return is_receiver_field(cdf)
? get_select_options(cdf, df.fieldname)
: null;
});
} else {
return is_receiver_field(df) ? get_select_options(df) : null;
}
};
return $.map(fields, extract_receiver_field);
};
let fields = frappe.get_doc("DocType", frm.doc.document_type).fields;
let options = $.map(fields, function (d) {
@ -50,27 +83,12 @@ frappe.notification = {
let receiver_fields = [];
if (frm.doc.channel === "Email") {
receiver_fields = $.map(fields, function (d) {
// Add User and Email fields from child into select dropdown
if (frappe.model.table_fields.includes(d.fieldtype)) {
let child_fields = frappe.get_doc("DocType", d.options).fields;
return $.map(child_fields, function (df) {
return df.options == "Email" ||
(df.options == "User" && df.fieldtype == "Link")
? get_select_options(df, d.fieldname)
: null;
});
// Add User and Email fields from parent into select dropdown
} else {
return d.options == "Email" ||
(d.options == "User" && d.fieldtype == "Link")
? get_select_options(d)
: null;
}
receiver_fields = get_receiver_fields(fields, function (df) {
return df.options == "Email";
});
} else if (["WhatsApp", "SMS"].includes(frm.doc.channel)) {
receiver_fields = $.map(fields, function (d) {
return d.options == "Phone" ? get_select_options(d) : null;
receiver_fields = get_receiver_fields(fields, function (df) {
df.options == "Phone" || df.options == "Mobile";
});
}

View file

@ -176,7 +176,7 @@ def get_context(context):
"""Build recipients and send Notification"""
context = get_context(doc)
context = {"doc": doc, "alert": self, "comments": None}
context.update({"alert": self, "comments": None})
if doc.get("_comments"):
context["comments"] = json.loads(doc.get("_comments"))
@ -316,10 +316,41 @@ def get_context(context):
def send_sms(self, doc, context):
send_sms(
receiver_list=self.get_receiver_list(doc, context),
receiver_list=self.get_receiver_list(doc, context, "mobile_no", self.get_mobile_no),
msg=frappe.utils.strip_html_tags(frappe.render_template(self.message, context)),
)
@staticmethod
def get_mobile_no(doc, field):
option = doc.meta.get_field(field).options.strip()
# users may sometimes register mobile numbers under Phone type fields
if option == "Phone" or option == "Mobile":
mobile_no = doc.get(field)
if not mobile_no:
doc.log_error(
_("Notification: document {0} has no {1} number set (field: {2})").format(
field, doc.name, option, field
)
)
# but on user & customer it's expected to be set on the proper field
elif option == "User":
user = doc.get(field)
mobile_no = frappe.get_value("User", user, "mobile_no")
if not mobile_no:
doc.log_error(_("Notification: user {0} has no Mobile number set").format(user))
elif option == "Customer":
customer = doc.get(field)
mobile_no = frappe.get_value("Customer", customer, "mobile_no")
if not mobile_no:
doc.log_error(_("Notification: customer {0} has no Mobile number set").format(customer))
else:
frappe.throw(
_(
"Field {0} on document {1} is neither a Mobile number field nor a Customer or User link"
).format(field, doc.name)
)
return mobile_no
def get_list_of_recipients(self, doc, context):
recipients = []
cc = []
@ -329,16 +360,17 @@ def get_context(context):
if not frappe.safe_eval(recipient.condition, None, context):
continue
if recipient.receiver_by_document_field:
fields = recipient.receiver_by_document_field.split(",")
# fields from child table
if len(fields) > 1:
for d in doc.get(fields[1]):
email_id = d.get(fields[0])
data_field, child_field = _parse_receiver_by_document_field(
recipient.receiver_by_document_field
)
if child_field:
for d in doc.get(child_field):
email_id = d.get(data_field)
if validate_email_address(email_id):
recipients.append(email_id)
# field from parent doc
# field from current doc
else:
email_ids_value = doc.get(fields[0])
email_ids_value = doc.get(data_field)
if validate_email_address(email_ids_value):
email_ids = email_ids_value.replace(",", "\n")
recipients = recipients + email_ids.split("\n")
@ -358,8 +390,10 @@ def get_context(context):
return list(set(recipients)), list(set(cc)), list(set(bcc))
def get_receiver_list(self, doc, context):
def get_receiver_list(self, doc, context, field_on_user="mobile_no", recipient_extractor_func=None):
"""return receiver list based on the doc field and role specified"""
if not recipient_extractor_func:
recipient_extractor_func = self.get_mobile_no
receiver_list = []
for recipient in self.recipients:
if recipient.condition:
@ -368,18 +402,28 @@ def get_context(context):
# For sending messages to the owner's mobile phone number
if recipient.receiver_by_document_field == "owner":
receiver_list += get_user_info([dict(user_name=doc.get("owner"))], "mobile_no")
receiver_list += get_user_info([dict(user_name=doc.get("owner"))], field_on_user)
# For sending messages to the number specified in the receiver field
elif recipient.receiver_by_document_field:
receiver_list.append(doc.get(recipient.receiver_by_document_field))
data_field, child_field = _parse_receiver_by_document_field(
recipient.receiver_by_document_field
)
if child_field:
for d in doc.get(child_field):
if recv := recipient_extractor_func(d, data_field):
receiver_list.append(recv)
# field from current doc
else:
if recv := recipient_extractor_func(doc, data_field):
receiver_list.append(recv)
# For sending messages to specified role
if recipient.receiver_by_role:
receiver_list += get_info_based_on_role(
recipient.receiver_by_role, "mobile_no", ignore_permissions=True
recipient.receiver_by_role, field_on_user, ignore_permissions=True
)
return receiver_list
return list(set(receiver_list))
def get_attachment(self, doc):
"""check print settings are attach the pdf"""
@ -555,3 +599,13 @@ def get_reference_doctype(doc):
def get_reference_name(doc):
return doc.parent if doc.meta.istable else doc.name
def _parse_receiver_by_document_field(s):
fragments = s.split(",")
# fields from child table or linked doctype
if len(fragments) > 1:
data_field, child_field = fragments
else:
data_field, child_field = fragments[0], None
return data_field, child_field