[freeside-commits] branch master updated. 770d8237cda1b6459962d31f6ae86997ad5843be

Mark Wells mark at 420.am
Sat Apr 30 18:08:18 PDT 2016


The branch, master has been updated
       via  770d8237cda1b6459962d31f6ae86997ad5843be (commit)
      from  8ff31f04ed1e2da3e09c56e72ab0f879d0b7ab75 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 770d8237cda1b6459962d31f6ae86997ad5843be
Author: Mark Wells <mark at freeside.biz>
Date:   Sat Apr 30 18:07:50 2016 -0700

    allow sending email to specific contact classes, #33316

diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 38edd04..4ec5a02 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2877,6 +2877,73 @@ sub invoicing_list_emailonly_scalar {
   join(', ', $self->invoicing_list_emailonly);
 }
 
+=item contact_list [ CLASSNUM, ... ]
+
+Returns a list of contacts (L<FS::contact> objects) for the customer. If
+a list of contact classnums is given, returns only contacts in those
+classes. If the pseudo-classnum 'invoice' is given, returns contacts that
+are marked as invoice destinations. If '0' is given, also returns contacts
+with no class.
+
+If no arguments are given, returns all contacts for the customer.
+
+=cut
+
+sub contact_list {
+  my $self = shift;
+  my $search = {
+    table       => 'contact',
+    select      => 'contact.*, cust_contact.invoice_dest',
+    addl_from   => ' JOIN cust_contact USING (contactnum)',
+    extra_sql   => ' WHERE cust_contact.custnum = '.$self->custnum,
+  };
+
+  my @orwhere;
+  my @classnums;
+  foreach (@_) {
+    if ( $_ eq 'invoice' ) {
+      push @orwhere, 'cust_contact.invoice_dest = \'Y\'';
+    } elsif ( $_ eq '0' ) {
+      push @orwhere, 'cust_contact.classnum is null';
+    } elsif ( /^\d+$/ ) {
+      push @classnums, $_;
+    } else {
+      die "bad classnum argument '$_'";
+    }
+  }
+
+  if (@classnums) {
+    push @orwhere, 'cust_contact.classnum IN ('.join(',', @classnums).')';
+  }
+  if (@orwhere) {
+    $search->{extra_sql} .= ' AND (' .
+                            join(' OR ', map "( $_ )", @orwhere) .
+                            ')';
+  }
+
+  qsearch($search);
+}
+
+=item contact_list_email [ CLASSNUM, ... ]
+
+Same as L</contact_list>, but returns email destinations instead of contact
+objects.
+
+=cut
+
+sub contact_list_email {
+  my $self = shift;
+  my @contacts = $self->contact_list(@_);
+  my @emails;
+  foreach my $contact (@contacts) {
+    foreach my $contact_email ($contact->contact_email) {
+      push @emails,
+        $contact->firstlast . ' <' . $contact_email->emailaddress . '>';
+    }
+  }
+  @emails;
+}
+
 =item referral_custnum_cust_main
 
 Returns the customer who referred this customer (or the empty string, if
diff --git a/FS/FS/cust_main_Mixin.pm b/FS/FS/cust_main_Mixin.pm
index bbba8c5..9fc66e0 100644
--- a/FS/FS/cust_main_Mixin.pm
+++ b/FS/FS/cust_main_Mixin.pm
@@ -383,6 +383,12 @@ HTML body
 
 Text body
 
+=item to_contact_classnum
+
+The customer contact class (or classes, as a comma-separated list) to send
+the message to. If unspecified, will be sent to any contacts that are marked
+as invoice destinations (the equivalent of specifying 'invoice').
+
 =back
 
 Returns an error message, or false for success.
@@ -406,6 +412,7 @@ sub email_search_result {
   my $subject = delete $param->{subject};
   my $html_body = delete $param->{html_body};
   my $text_body = delete $param->{text_body};
+  my $to_contact_classnum = delete $param->{to_contact_classnum};
   my $error = '';
 
   my $job = delete $param->{'job'}
@@ -471,6 +478,7 @@ sub email_search_result {
     my $cust_msg = $msg_template->prepare(
       'cust_main' => $cust_main,
       'object'    => $obj,
+      'to_contact_classnum' => $to_contact_classnum,
     );
 
     # For non-cust_main searches, we avoid duplicates based on message
diff --git a/FS/FS/msg_template/email.pm b/FS/FS/msg_template/email.pm
index 07a1fa0..d1df5d6 100644
--- a/FS/FS/msg_template/email.pm
+++ b/FS/FS/msg_template/email.pm
@@ -289,9 +289,25 @@ sub prepare {
 
   my @to;
   if ( exists($opt{'to'}) ) {
+
     @to = split(/\s*,\s*/, $opt{'to'});
+
   } elsif ( $cust_main ) {
-    @to = $cust_main->invoicing_list_emailonly;
+
+    if ( $opt{'to_contact_classnum'} ) {
+
+      my $classnum = $opt{'to_contact_classnum'};
+      my @classes = ref($classnum) ? @$classnum : split(',', $classnum);
+      if ( !@classes ) {
+        # traditional behavior: send to invoice email destinations (only)
+        @classes = ( 'invoice' );
+      }
+      @to = $cust_main->contact_list_email(@classes);
+      # not guaranteed to produce contacts, but then customers aren't
+      # guaranteed to have email addresses on file. in that case, env_to
+      # will be null and sending this message will fail.
+    }
+
   } else {
     die 'no To: address or cust_main object specified';
   }
@@ -324,13 +340,16 @@ sub prepare {
   );
 
   warn "$me creating message headers\n" if $DEBUG;
+  # strip display-name from envelope addresses
+  # (use Email::Address for this? it chokes on non-ASCII characters in
+  # the display-name, which is not great for us)
   my $env_from = $from_addr;
-  $env_from =~ s/^\s*//; $env_from =~ s/\s*$//;
-  if ( $env_from =~ /^(.*)\s*<(.*@.*)>$/ ) {
-    # a common idiom
-    $env_from = $2;
-  } 
-  
+  foreach ($env_from, @to) {
+    s/^\s*//;
+    s/\s*$//;
+    s/^(.*)\s*<(.*@.*)>$/$2/;
+  }
+
   my $domain;
   if ( $env_from =~ /\@([\w\.\-]+)/ ) {
     $domain = $1;
diff --git a/httemplate/elements/checkboxes.html b/httemplate/elements/checkboxes.html
index ad9d691..b07b654 100644
--- a/httemplate/elements/checkboxes.html
+++ b/httemplate/elements/checkboxes.html
@@ -27,7 +27,7 @@ Example:
 
 </%doc>
 
-<TABLE CELLSPACING=0 CELLPADDING=0>
+<TABLE CELLSPACING=0 CELLPADDING=0 <% $style %>>
 
 % unless ( $opt{'disable_links'} ) {
 
@@ -108,4 +108,8 @@ $opt{'error_checked_callback'} ||= sub {
   $cgi->param($opt{'element_name_prefix'}. $name );
 };
 
+my $style = '';
+if ($opt{'style'}) {
+  $style = 'STYLE="' . $opt{'style'} . '"';
+}
 </%init>
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
index 8e28634..d086c67 100644
--- a/httemplate/misc/email-customers.html
+++ b/httemplate/misc/email-customers.html
@@ -7,8 +7,8 @@ selecting an existing msg_template, or creating a custom message, and shows a
 preview of the message before sending.  If linked to as a popup, include the
 cgi parameter 'popup' for proper header handling.
 
-This may also be used as an element in other pages, enabling you to provide an
-alternate initial form while using this for search freezing/thawing and 
+This may also be used as an element in other pages, enabling you to provide
+an alternate initial form while using this for search freezing/thawing and 
 preview/send actions, with the following options:
 
 acl - the access right to use (defaults to 'Bulk send customer notices')
@@ -48,6 +48,7 @@ from/subject/body cgi params
 <INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
 <INPUT TYPE="hidden" NAME="popup" VALUE="<% $popup %>">
 <INPUT TYPE="hidden" NAME="url" VALUE="<% $url | h %>">
+<INPUT TYPE="hidden" NAME="to_contact_classnum" VALUE="<% join(',', @contact_classnum) %>">
 
 % if ( $cgi->param('preview') ) {
 %   # preview mode: at this point we have a msg_template (either "real" or
@@ -57,7 +58,7 @@ from/subject/body cgi params
     <FONT SIZE="+2">Preview notice</FONT>
     <& /elements/progress-init.html,
                  'OneTrueForm',
-                 [ qw( search table msgnum ) ],
+                 [ qw( search table msgnum to_contact_classnum ) ],
                  $process_url,
                  $pdest,
     &>
@@ -79,6 +80,10 @@ from/subject/body cgi params
         <td><% $from |h %></td>
       </tr>
 
+      <& /elements/tr-td-label.html, 'label' => 'To contacts:' &>
+        <td><% join('<BR>', @contact_classname) %></td>
+      </tr>
+
       <& /elements/tr-td-label.html, 'label' => 'Subject:' &>
         <td><% $subject |h %></td>
       </tr>
@@ -158,6 +163,20 @@ Template:
     &>
     <BR>
 % }
+% # select destination contact classes
+Send to contacts:
+  <& /elements/checkboxes.html,
+    'style'               => 'display: inline; vertical-align: top',
+    'disable_links'       => 1,
+    'names_list'          => \@contact_checkboxes,
+    'element_name_prefix' => 'contact_class_',
+    'checked_callback'    => sub {
+      my($cgi, $name) = @_;
+      $name eq 'invoice' #others default to unchecked
+    },
+  &>
+<BR>
+% # if sending a one-off message, show a form to edit it
   <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
     <& /elements/tr-td-label.html, 'label' => 'From:' &>
       <TD><& /elements/input-text.html,
@@ -262,6 +281,9 @@ if ( $cgi->param('msgnum') =~ /^(\d+)$/ ) {
     or die "template not found: ".$cgi->param('msgnum');
 }
 
+my @contact_classnum;
+my @contact_classname;
+
 my $subject = $cgi->param('subject');
 my $body = $cgi->param('body');
 my ($html_body, $text_body);
@@ -337,6 +359,28 @@ if ( !$cgi->param('preview') ) {
       $subject = $1;
     }
   }
+
+  # contact_class_X params
+  foreach my $param ( $cgi->multi_param ) {
+    if ( $param =~ /^contact_class_(\w+)$/ ) {
+      push @contact_classnum, $1;
+      if ( $1 eq 'invoice' ) {
+        push @contact_classname, 'Invoice recipients';
+      } else {
+        my $contact_class = FS::contact_class->by_key($1);
+        push @contact_classname, encode_entities($contact_class->classname);
+      }
+    }
+  }
 }
 
+my @contact_checkboxes = (
+  [ 'invoice' => { label => 'Invoice recipients' } ]
+);
+foreach my $class (qsearch('contact_class', { disabled => '' })) {
+  push @contact_checkboxes, [
+    $class->classnum,
+    { label => $class->classname }
+  ];
+}
 </%init>

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/cust_main.pm                   |   67 ++++++++++++++++++++++++++++++++++
 FS/FS/cust_main_Mixin.pm             |    8 ++++
 FS/FS/msg_template/email.pm          |   33 +++++++++++++----
 httemplate/elements/checkboxes.html  |    6 ++-
 httemplate/misc/email-customers.html |   50 +++++++++++++++++++++++--
 5 files changed, 153 insertions(+), 11 deletions(-)




More information about the freeside-commits mailing list