ERPNext - How-to add billing contacts to your Sales Invoice CC field in ERPNext

Here's a quick tips and tricks guide on how to add your billing contacts to the CC field of a Sales Invoice using `get_email_recipients`

 · 3 min read

Image attribution: upklyak from freepik.com


Often a customer will ask:

hey, can you make sure you CC invoices@megacorp.zzz if you want to get paid?


At which point you make a mental note which goes out of the window!


By default ERPNext will add your primary customer contact email address to the To field of your Sales document emails. But this is probably different to the billing contact.


ERPNext also has a tick box on its Contact form called Is Billing Contact, but unfortunately this information isn't used in many places as of Version 15.


So here's a quick Client Script that will add your billing contact email addresses to the CC field of any Sales Invoices.


TL;DR

Either add a new or modify an existing Client Script for Doctype: Sales Invoice, Apply To: Form with the following code, and tick the Enabled box before saving:

frappe.ui.form.on('Sales Invoice', {
   // must retrieve billing contacts in advance because email dialog constructor won't await
   refresh: function(frm) {
       erpnext.utils.get_billing_contacts(frm);
   },

   // add billing emails to CC field
   get_email_recipients: function(frm, fieldname) {
       // fieldname can be recipients (the to field), cc or bcc
       // return an array of email addresses
       const emails = [];
       if (frm.doc.billing_contacts) {
           if (fieldname === "cc") {
               for (const c of frm.doc.billing_contacts) {
                   if (c.email_id?.length) {
                       emails.push(c.email_id);
                   }
               }
           }
       }
       return emails;
   },
});

erpnext.utils.get_billing_contacts = function(frm) {
   if (frm.doc.customer) {
       frappe.db.get_list("Contact", {
           fields: ["first_name", "last_name", "email_id"],
           filters: [
               ["Dynamic Link", "link_doctype", "=", "Customer"],
               ["Dynamic Link", "link_name", "=", frm.doc.customer],
               ["is_billing_contact", "=", 1],
           ],
       }).then(r => {
           // assign our billing contacts into the doc for use later
           frm.doc.billing_contacts = r;
       });
   }
};


Explanation

There is a documented hook called get_email_recipients which is called three times when an email is created on any document. It is called with the following fieldnames as the second parameter:

  1. recipients (the to field)
  2. cc
  3. bcc

The code expects an array of email addresses to be returned.


The snag is it won't await for a Promise to resolve.

So the script does three things:

  1. Defines a get_billing_contacts function to do a server request to retrieve the billing contacts, and stores them at frm.doc.billing_contacts for use later;
  2. This function is run in the form refresh, so there's always an up-to-date copy;
  3. When get_email_recipients is called with "cc", it returns any billing email addresses from the retrieved contacts as an array.


That's it!

Let's hope we all get paid on time.


No comments yet.

Add a comment
Ctrl+Enter to add comment