track customer invoice destination emails using contact_email, #25536
authorMark Wells <mark@freeside.biz>
Wed, 18 Nov 2015 21:07:47 +0000 (13:07 -0800)
committerMark Wells <mark@freeside.biz>
Wed, 18 Nov 2015 21:07:47 +0000 (13:07 -0800)
20 files changed:
FS/FS/Schema.pm
FS/FS/contact.pm
FS/FS/cust_main.pm
FS/FS/cust_main/Search.pm
FS/FS/cust_main_invoice.pm
FS/FS/part_event/Condition/nopostal.pm
FS/FS/part_event/Condition/postal.pm
FS/FS/svc_acct.pm
httemplate/REST/1.0/cust_main
httemplate/edit/cust_main.cgi
httemplate/edit/cust_main/basics.html
httemplate/edit/cust_main/billing.html
httemplate/edit/cust_main/name.html
httemplate/edit/process/cust_main-contacts.html
httemplate/edit/process/cust_main.cgi
httemplate/elements/contact.html
httemplate/elements/tr-checkbox.html
httemplate/elements/tr-td-label.html
httemplate/misc/xmlhttp-cust_main-email_search.html
httemplate/view/cust_main/contacts_new.html

index 5a2a9be..c1ed79c 100644 (file)
@@ -1657,6 +1657,7 @@ sub tables_hashref {
         'po_number', 'varchar', 'NULL', $char_d, '', '',
         'invoice_attn', 'varchar', 'NULL', $char_d, '', '',
         'invoice_ship_address', 'char', 'NULL', 1, '', '',
         'po_number', 'varchar', 'NULL', $char_d, '', '',
         'invoice_attn', 'varchar', 'NULL', $char_d, '', '',
         'invoice_ship_address', 'char', 'NULL', 1, '', '',
+        'postal_invoice', 'char', 'NULL', 1, '', '',
       ],
       'primary_key'  => 'custnum',
       'unique'       => [ [ 'agentnum', 'agent_custid' ] ],
       ],
       'primary_key'  => 'custnum',
       'unique'       => [ [ 'agentnum', 'agent_custid' ] ],
@@ -1812,6 +1813,7 @@ sub tables_hashref {
         '_password',          'varchar', 'NULL', $char_d, '', '',
         '_password_encoding', 'varchar', 'NULL', $char_d, '', '',
         'disabled',              'char', 'NULL',       1, '', '', 
         '_password',          'varchar', 'NULL', $char_d, '', '',
         '_password_encoding', 'varchar', 'NULL', $char_d, '', '',
         'disabled',              'char', 'NULL',       1, '', '', 
+        'invoice_dest',          'char', 'NULL',       1, '', '',
       ],
       'primary_key'  => 'contactnum',
       'unique'       => [],
       ],
       'primary_key'  => 'contactnum',
       'unique'       => [],
index 6120480..0428d89 100644 (file)
@@ -6,6 +6,7 @@ use vars qw( $skip_fuzzyfiles );
 use Carp;
 use Scalar::Util qw( blessed );
 use FS::Record qw( qsearch qsearchs dbh );
 use Carp;
 use Scalar::Util qw( blessed );
 use FS::Record qw( qsearch qsearchs dbh );
+use FS::Cursor;
 use FS::contact_phone;
 use FS::contact_email;
 use FS::queue;
 use FS::contact_phone;
 use FS::contact_email;
 use FS::queue;
@@ -88,6 +89,9 @@ empty or bcrypt
 
 disabled
 
 
 disabled
 
+=item invoice_dest
+
+empty, or 'Y' if email invoices should be sent to this contact
 
 =back
 
 
 =back
 
@@ -111,6 +115,25 @@ sub table { 'contact'; }
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
 
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
 
+If the object has an C<emailaddress> field, L<FS::contact_email> records will
+be created for each (comma-separated) email address in that field. If any of
+these coincide with an existing email address, this contact will be merged with
+the contact with that address.
+
+Then, if the object has any fields named C<phonetypenumN> an
+L<FS::contact_phone> record will be created for each of them. Those fields
+should contain phone numbers of the appropriate types (where N is the key of
+an L<FS::phone_type> record identifying the type of number: daytime, night,
+etc.).
+
+After inserting the record, if the object has a 'custnum' or 'prospectnum'
+field, an L<FS::cust_contact> or L<FS::prospect_contact> record will be
+created to link the contact to the customer. The following fields will also
+be included in that record, if they are set on the object:
+- classnum
+- comment
+- selfservice_access
+
 =cut
 
 sub insert {
 =cut
 
 sub insert {
@@ -643,6 +666,7 @@ sub check {
     || $self->ut_textn('_password')
     || $self->ut_enum('_password_encoding', [ '', 'bcrypt'])
     || $self->ut_enum('disabled', [ '', 'Y' ])
     || $self->ut_textn('_password')
     || $self->ut_enum('_password_encoding', [ '', 'bcrypt'])
     || $self->ut_enum('disabled', [ '', 'Y' ])
+    || $self->ut_flag('invoice_dest')
   ;
   return $error if $error;
 
   ;
   return $error if $error;
 
@@ -886,6 +910,7 @@ sub cgi_contact_fields {
 
   my @contact_fields = qw(
     classnum first last title comment emailaddress selfservice_access
 
   my @contact_fields = qw(
     classnum first last title comment emailaddress selfservice_access
+    invoice_dest
   );
 
   push @contact_fields, 'phonetypenum'. $_->phonetypenum
   );
 
   push @contact_fields, 'phonetypenum'. $_->phonetypenum
@@ -899,6 +924,32 @@ use FS::upgrade_journal;
 sub _upgrade_data { #class method
   my ($class, %opts) = @_;
 
 sub _upgrade_data { #class method
   my ($class, %opts) = @_;
 
+  # always migrate cust_main_invoice records over
+  local $FS::cust_main::import = 1; # override require_phone and such
+  my $search = FS::Cursor->new('cust_main_invoice', {});
+  while (my $cust_main_invoice = $search->fetch) {
+    my $custnum = $cust_main_invoice->custnum;
+    my $dest = $cust_main_invoice->dest;
+    my $cust_main = $cust_main_invoice->cust_main;
+
+    if ( $dest =~ /^\d+$/ ) {
+      my $svc_acct = FS::svc_acct->by_key($dest);
+      die "custnum $custnum, invoice destination svcnum $svc_acct does not exist\n"
+        if !$svc_acct;
+      $dest = $svc_acct->email;
+    }
+
+    my $error = $cust_main->replace( [ $dest ] );
+
+    if ( $error ) {
+      die "custnum $custnum, invoice destination $dest, creating contact: $error\n";
+    }
+
+    $error = $cust_main_invoice->delete;
+    die "custnum $custnum, cleaning up cust_main_invoice: $error\n" if $error;
+
+  } # while $search->fetch
+
   unless ( FS::upgrade_journal->is_done('contact__DUPEMAIL') ) {
 
     foreach my $contact (qsearch('contact', {})) {
   unless ( FS::upgrade_journal->is_done('contact__DUPEMAIL') ) {
 
     foreach my $contact (qsearch('contact', {})) {
index 1f64b9e..4c09d8c 100644 (file)
@@ -325,14 +325,7 @@ a better explanation of this, but until then, here's an example:
   );
   $cust_main->insert( \%hash );
 
   );
   $cust_main->insert( \%hash );
 
-INVOICING_LIST_ARYREF: If you pass an arrarref to the insert method, it will
-be set as the invoicing list (see L<"invoicing_list">).  Errors return as
-expected and rollback the entire transaction; it is not necessary to call 
-check_invoicing_list first.  The invoicing_list is set after the records in the
-CUST_PKG_HASHREF above are inserted, so it is now possible to set an
-invoicing_list destination to the newly-created svc_acct.  Here's an example:
-
-  $cust_main->insert( {}, [ $email, 'POST' ] );
+INVOICING_LIST_ARYREF: No longer supported.
 
 Currently available options are: I<depend_jobnum>, I<noexport>,
 I<tax_exemption>, I<prospectnum>, I<contact> and I<contact_params>.
 
 Currently available options are: I<depend_jobnum>, I<noexport>,
 I<tax_exemption>, I<prospectnum>, I<contact> and I<contact_params>.
@@ -352,8 +345,8 @@ created and inserted.
 
 If I<prospectnum> is set, moves contacts and locations from that prospect.
 
 
 If I<prospectnum> is set, moves contacts and locations from that prospect.
 
-If I<contact> is set to an arrayref of FS::contact objects, inserts those
-new contacts with this new customer.
+If I<contact> is set to an arrayref of FS::contact objects, those will be
+inserted.
 
 If I<contact_params> is set to a hashref of CGI parameters (and I<contact> is
 unset), inserts those new contacts with this new customer.  Handles CGI
 
 If I<contact_params> is set to a hashref of CGI parameters (and I<contact> is
 unset), inserts those new contacts with this new customer.  Handles CGI
@@ -368,7 +361,10 @@ for an "m2" multiple entry field as passed by edit/cust_main.cgi
 sub insert {
   my $self = shift;
   my $cust_pkgs = @_ ? shift : {};
 sub insert {
   my $self = shift;
   my $cust_pkgs = @_ ? shift : {};
-  my $invoicing_list = @_ ? shift : '';
+  my $invoicing_list = $_[0];
+  if ( $invoicing_list and ref($invoicing_list) eq 'ARRAY' ) {
+    shift;
+  }
   my %options = @_;
   warn "$me insert called with options ".
        join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
   my %options = @_;
   warn "$me insert called with options ".
        join(', ', map { "$_: $options{$_}" } keys %options ). "\n"
@@ -495,19 +491,6 @@ sub insert {
     }
   }
 
     }
   }
 
-  warn "  setting invoicing list\n"
-    if $DEBUG > 1;
-
-  if ( $invoicing_list ) {
-    $error = $self->check_invoicing_list( $invoicing_list );
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      #return "checking invoicing_list (transaction rolled back): $error";
-      return $error;
-    }
-    $self->invoicing_list( $invoicing_list );
-  }
-
   warn "  setting customer tags\n"
     if $DEBUG > 1;
 
   warn "  setting customer tags\n"
     if $DEBUG > 1;
 
@@ -595,6 +578,27 @@ sub insert {
       return $error;
     }
   }
       return $error;
     }
   }
+  
+  if ( $invoicing_list ) {
+    warn "FS::cust_main::insert setting invoice destinations via invoicing_list\n"
+      if $DEBUG;
+
+    # okay, for now we'll still allow setting the contact this way
+    $invoicing_list = join(',', @$invoicing_list) if ref $invoicing_list;
+    my $contact = FS::contact->new({
+      'custnum'       => $self->get('custnum'),
+      'last'          => $self->get('last'),
+      'first'         => $self->get('first'),
+      'emailaddress'  => $invoicing_list,
+      'invoice_dest'  => 'Y',
+    });
+    my $error = $contact->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+
+  }
 
   warn "  setting cust_payby\n"
     if $DEBUG > 1;
 
   warn "  setting cust_payby\n"
     if $DEBUG > 1;
@@ -1274,12 +1278,9 @@ To change the customer's address, set the pseudo-fields C<bill_location> and
 C<ship_location>.  The address will still only change if at least one of the
 address fields differs from the existing values.
 
 C<ship_location>.  The address will still only change if at least one of the
 address fields differs from the existing values.
 
-INVOICING_LIST_ARYREF: If you pass an arrarref to the insert method, it will
-be set as the invoicing list (see L<"invoicing_list">).  Errors return as
-expected and rollback the entire transaction; it is not necessary to call 
-check_invoicing_list first.  Here's an example:
-
-  $new_cust_main->replace( $old_cust_main, [ $email, 'POST' ] );
+INVOICING_LIST_ARYREF: If you pass an arrayref to this method, it will be
+set as the contact email address for a default contact with the same name as
+the customer.
 
 Currently available options are: I<tax_exemption>.
 
 
 Currently available options are: I<tax_exemption>.
 
@@ -1347,6 +1348,45 @@ sub replace {
     $self->set($l.'num', $new_loc->locationnum);
   } #for $l
 
     $self->set($l.'num', $new_loc->locationnum);
   } #for $l
 
+  if ( @param && ref($param[0]) eq 'ARRAY' ) { # INVOICING_LIST_ARYREF
+    my $invoicing_list = shift @param;
+    my $email = '';
+    foreach (@$invoicing_list) {
+      if ($_ eq 'POST') {
+        $self->set('postal_invoice', 'Y');
+      } else {
+        $email .= ',' if length($email);
+        $email .= $_;
+      }
+    }
+    my @contacts = map { $_->contact } $self->cust_contact;
+    # if possible, use a contact that matches the customer's name
+    my ($contact) = grep { $_->first eq $old->get('first') and
+                           $_->last  eq $old->get('last') }
+                    @contacts;
+    $contact ||= FS::contact->new({
+        'custnum'       => $self->custnum,
+        'locationnum'   => $self->get('bill_locationnum'),
+    });
+    $contact->set('last', $self->get('last'));
+    $contact->set('first', $self->get('first'));
+    $contact->set('emailaddress', $email);
+    $contact->set('invoice_dest', 'Y');
+
+    my $error;
+    if ( $contact->contactnum ) {
+      $error = $contact->replace;
+    } elsif ( length($email) ) { # don't create a new contact if email is empty
+      $error = $contact->insert;
+    }
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+
+  }
+
   # replace the customer record
   my $error = $self->SUPER::replace($old);
 
   # replace the customer record
   my $error = $self->SUPER::replace($old);
 
@@ -1376,16 +1416,6 @@ sub replace {
     }
   }
 
     }
   }
 
-  if ( @param && ref($param[0]) eq 'ARRAY' ) { # INVOICING_LIST_ARYREF
-    my $invoicing_list = shift @param;
-    $error = $self->check_invoicing_list( $invoicing_list );
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-    $self->invoicing_list( $invoicing_list );
-  }
-
   if ( $self->exists('tagnum') ) { #so we don't delete these on edit by accident
 
     #this could be more efficient than deleting and re-inserting, if it matters
   if ( $self->exists('tagnum') ) { #so we don't delete these on edit by accident
 
     #this could be more efficient than deleting and re-inserting, if it matters
@@ -1605,6 +1635,7 @@ sub check {
     || $self->ut_alphan('po_number')
     || $self->ut_enum('complimentary', [ '', 'Y' ])
     || $self->ut_flag('invoice_ship_address')
     || $self->ut_alphan('po_number')
     || $self->ut_enum('complimentary', [ '', 'Y' ])
     || $self->ut_flag('invoice_ship_address')
+    || $self->ut_flag('invoice_dest')
   ;
 
   foreach (qw(company ship_company)) {
   ;
 
   foreach (qw(company ship_company)) {
@@ -2814,18 +2845,10 @@ sub tax_exemption {
 
 =item cust_main_exemption
 
 
 =item cust_main_exemption
 
-=item invoicing_list [ ARRAYREF ]
-
-If an arguement is given, sets these email addresses as invoice recipients
-(see L<FS::cust_main_invoice>).  Errors are not fatal and are not reported
-(except as warnings), so use check_invoicing_list first.
-
-Returns a list of email addresses (with svcnum entries expanded).
+=item invoicing_list
 
 
-Note: You can clear the invoicing list by passing an empty ARRAYREF.  You can
-check it without disturbing anything by passing nothing.
-
-This interface may change in the future.
+Returns a list of email addresses (with svcnum entries expanded), and the word
+'POST' if the customer receives postal invoices.
 
 =cut
 
 
 =cut
 
@@ -2833,47 +2856,13 @@ sub invoicing_list {
   my( $self, $arrayref ) = @_;
 
   if ( $arrayref ) {
   my( $self, $arrayref ) = @_;
 
   if ( $arrayref ) {
-    my @cust_main_invoice;
-    if ( $self->custnum ) {
-      @cust_main_invoice = 
-        qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
-    } else {
-      @cust_main_invoice = ();
-    }
-    foreach my $cust_main_invoice ( @cust_main_invoice ) {
-      #warn $cust_main_invoice->destnum;
-      unless ( grep { $cust_main_invoice->address eq $_ } @{$arrayref} ) {
-        #warn $cust_main_invoice->destnum;
-        my $error = $cust_main_invoice->delete;
-        warn $error if $error;
-      }
-    }
-    if ( $self->custnum ) {
-      @cust_main_invoice = 
-        qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
-    } else {
-      @cust_main_invoice = ();
-    }
-    my %seen = map { $_->address => 1 } @cust_main_invoice;
-    foreach my $address ( @{$arrayref} ) {
-      next if exists $seen{$address} && $seen{$address};
-      $seen{$address} = 1;
-      my $cust_main_invoice = new FS::cust_main_invoice ( {
-        'custnum' => $self->custnum,
-        'dest'    => $address,
-      } );
-      my $error = $cust_main_invoice->insert;
-      warn $error if $error;
-    }
+    warn "FS::cust_main::invoicing_list(ARRAY) is no longer supported.";
   }
   
   }
   
-  if ( $self->custnum ) {
-    map { $_->address }
-      qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
-  } else {
-    ();
-  }
+  my @emails = $self->invoicing_list_emailonly;
+  push @emails, 'POST' if $self->get('postal_invoice');
 
 
+  @emails;
 }
 
 =item check_invoicing_list ARRAYREF
 }
 
 =item check_invoicing_list ARRAYREF
@@ -2911,18 +2900,6 @@ sub check_invoicing_list {
   '';
 }
 
   '';
 }
 
-=item set_default_invoicing_list
-
-Sets the invoicing list to all accounts associated with this customer,
-overwriting any previous invoicing list.
-
-=cut
-
-sub set_default_invoicing_list {
-  my $self = shift;
-  $self->invoicing_list($self->all_emails);
-}
-
 =item all_emails
 
 Returns the email addresses of all accounts provisioned for this customer.
 =item all_emails
 
 Returns the email addresses of all accounts provisioned for this customer.
@@ -2952,10 +2929,11 @@ to receive postal invoices, does nothing.
 
 sub invoicing_list_addpost {
   my $self = shift;
 
 sub invoicing_list_addpost {
   my $self = shift;
-  return if grep { $_ eq 'POST' } $self->invoicing_list;
-  my @invoicing_list = $self->invoicing_list;
-  push @invoicing_list, 'POST';
-  $self->invoicing_list(\@invoicing_list);
+  if ( $self->get('postal_invoice') eq '' ) {
+    $self->set('postal_invoice', 'Y');
+    my $error = $self->replace;
+    warn $error if $error; # should fail harder, but this is traditional
+  }
 }
 
 =item invoicing_list_emailonly
 }
 
 =item invoicing_list_emailonly
@@ -2969,7 +2947,16 @@ sub invoicing_list_emailonly {
   my $self = shift;
   warn "$me invoicing_list_emailonly called"
     if $DEBUG;
   my $self = shift;
   warn "$me invoicing_list_emailonly called"
     if $DEBUG;
-  grep { $_ !~ /^([A-Z]+)$/ } $self->invoicing_list;
+  return () if !$self->custnum; # not yet inserted
+  return map { $_->emailaddress }
+    qsearch({
+        table     => 'cust_contact',
+        select    => 'emailaddress',
+        addl_from => ' JOIN contact USING (contactnum) '.
+                     ' JOIN contact_email USING (contactnum)',
+        hashref   => { 'custnum' => $self->custnum, },
+        extra_sql => q( AND invoice_dest = 'Y'),
+    });
 }
 
 =item invoicing_list_emailonly_scalar
 }
 
 =item invoicing_list_emailonly_scalar
index 097f2fb..c8a084c 100644 (file)
@@ -531,10 +531,12 @@ sub email_search {
       if $DEBUG;
 
     push @cust_main,
       if $DEBUG;
 
     push @cust_main,
-      map $_->cust_main,
+      map { $_->cust_main }
+      map { $_->cust_contact }
+      map { $_->contact }
           qsearch( {
           qsearch( {
-                     'table'     => 'cust_main_invoice',
-                     'hashref'   => { 'dest' => $email },
+                     'table'     => 'contact_email',
+                     'hashref'   => { 'emailaddress' => $email },
                    }
                  );
 
                    }
                  );
 
@@ -808,30 +810,24 @@ sub search {
   ##
 
   push @where,
   ##
 
   push @where,
-    'EXISTS ( SELECT 1 FROM cust_main_invoice
-                WHERE cust_main_invoice.custnum = cust_main.custnum
-                  AND length(dest) > 5
-            )'  # AND dest LIKE '%@%'
+    'EXISTS ( SELECT 1 FROM contact_email
+                JOIN cust_contact USING (contactnum)
+                WHERE cust_contact.custnum = cust_main.custnum
+            )'
     if $params->{'with_email'};
 
   ##
   # "with postal mail invoices" checkbox
   ##
 
     if $params->{'with_email'};
 
   ##
   # "with postal mail invoices" checkbox
   ##
 
-  push @where,
-    "EXISTS ( SELECT 1 FROM cust_main_invoice
-                WHERE cust_main_invoice.custnum = cust_main.custnum
-                  AND dest = 'POST' )"
+  push @where, "cust_main.postal_invoice = 'Y'"
     if $params->{'POST'};
 
   ##
   # "without postal mail invoices" checkbox
   ##
 
     if $params->{'POST'};
 
   ##
   # "without postal mail invoices" checkbox
   ##
 
-  push @where,
-    "NOT EXISTS ( SELECT 1 FROM cust_main_invoice
-                    WHERE cust_main_invoice.custnum = cust_main.custnum
-                      AND dest = 'POST' )"
+  push @where, "cust_main.postal_invoice IS NULL"
     if $params->{'no_POST'};
 
   ##
     if $params->{'no_POST'};
 
   ##
index b6ef260..6c155ac 100644 (file)
@@ -11,6 +11,11 @@ use FS::Msgcat qw(gettext);
 
 FS::cust_main_invoice - Object methods for cust_main_invoice records
 
 
 FS::cust_main_invoice - Object methods for cust_main_invoice records
 
+=head1 ANNOUNCEMENT
+
+This is deprecated in version 4. Instead, contacts with the "invoice_dest"
+attribute should be used.
+
 =head1 SYNOPSIS
 
   use FS::cust_main_invoice;
 =head1 SYNOPSIS
 
   use FS::cust_main_invoice;
index b95cd5c..ea55ba5 100644 (file)
@@ -10,17 +10,13 @@ sub condition {
   my( $self, $object ) = @_;
   my $cust_main = $self->cust_main($object);
 
   my( $self, $object ) = @_;
   my $cust_main = $self->cust_main($object);
 
-  scalar( grep { $_ eq 'POST' } $cust_main->invoicing_list ) ? 0 : 1;
+  $cust_main->postal_invoice eq '';
 }
 
 sub condition_sql {
   my( $self, $table ) = @_;
 
 }
 
 sub condition_sql {
   my( $self, $table ) = @_;
 
-  " NOT EXISTS( SELECT 1 FROM cust_main_invoice
-              WHERE cust_main_invoice.custnum = cust_main.custnum
-                AND cust_main_invoice.dest    = 'POST'
-          )
-  ";
+  " cust_main.postal_invoice IS NULL ";
 }
 
 1;
 }
 
 1;
index d0bd419..1dbe054 100644 (file)
@@ -10,17 +10,13 @@ sub condition {
   my( $self, $object ) = @_;
   my $cust_main = $self->cust_main($object);
 
   my( $self, $object ) = @_;
   my $cust_main = $self->cust_main($object);
 
-  scalar( grep { $_ eq 'POST' } $cust_main->invoicing_list );
+  $cust_main->postal_invoice eq 'Y';
 }
 
 sub condition_sql {
   my( $self, $table ) = @_;
 
 }
 
 sub condition_sql {
   my( $self, $table ) = @_;
 
-  " EXISTS( SELECT 1 FROM cust_main_invoice
-              WHERE cust_main_invoice.custnum = cust_main.custnum
-                AND cust_main_invoice.dest    = 'POST'
-          )
-  ";
+  " cust_main.postal_invoice = 'Y' "
 }
 
 1;
 }
 
 1;
index d3e23f2..9323976 100644 (file)
@@ -44,7 +44,6 @@ use FS::PagedSearch qw( psearch ); # XXX in v4, replace with FS::Cursor
 use FS::part_pkg;
 use FS::part_svc;
 use FS::svc_acct_pop;
 use FS::part_pkg;
 use FS::part_svc;
 use FS::svc_acct_pop;
-use FS::cust_main_invoice;
 use FS::svc_domain;
 use FS::svc_pbx;
 use FS::raddb;
 use FS::svc_domain;
 use FS::svc_pbx;
 use FS::raddb;
@@ -711,9 +710,37 @@ sub insert {
         || $conf->exists('emailinvoiceauto')
         && ! $cust_main->invoicing_list_emailonly
        ) {
         || $conf->exists('emailinvoiceauto')
         && ! $cust_main->invoicing_list_emailonly
        ) {
-      my @invoicing_list = $cust_main->invoicing_list;
-      push @invoicing_list, $self->email;
-      $cust_main->invoicing_list(\@invoicing_list);
+
+      # slight false laziness w/ edit/process/cust_main.cgi...
+      # and also slightly arbitrary behavior.
+      # if the "real name" of this account matches the first + last name
+      # of a contact, attach the email address to that person.
+      my @contacts = map { $_->contact } $cust_main->cust_contact;
+      my $myname = $self->get('finger');
+      my ($contact) =
+        grep { $_->get('first') . ' ' . $_->get('last') eq $myname } @contacts;
+      # otherwise just pick the first one
+      $contact ||= $contacts[0];
+      # if there is one
+      $contact ||= FS::contact->new({
+          'custnum'       => $cust_main->get('custnum'),
+          'locationnum'   => $cust_main->get('bill_locationnum'),
+          'last'          => $cust_main->get('last'),
+          'first'         => $cust_main->get('first'),
+      });
+      $contact->set('emailaddress', $self->email);
+      $contact->set('invoice_dest', 'Y');
+
+      if ( $contact->get('contactnum') ) {
+        $error = $contact->replace;
+      } else {
+        $error = $contact->insert;
+      }
+
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "creating invoice destination contact: $error";
+      }
     }
 
     #welcome email
     }
 
     #welcome email
@@ -800,23 +827,6 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  foreach my $cust_main_invoice (
-    qsearch( 'cust_main_invoice', { 'dest' => $self->svcnum } )
-  ) {
-    unless ( defined($cust_main_invoice) ) {
-      warn "WARNING: something's wrong with qsearch";
-      next;
-    }
-    my %hash = $cust_main_invoice->hash;
-    $hash{'dest'} = $self->email;
-    my $new = new FS::cust_main_invoice \%hash;
-    my $error = $new->replace($cust_main_invoice);
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-  }
-
   foreach my $svc_domain (
     qsearch( 'svc_domain', { 'catchall' => $self->svcnum } )
   ) {
   foreach my $svc_domain (
     qsearch( 'svc_domain', { 'catchall' => $self->svcnum } )
   ) {
index 4656bcb..5401195 100644 (file)
@@ -47,17 +47,23 @@ if ( $r->method eq 'GET' ) {
     if ( $cgi->param('cust_main_invoice_dest') ) {
       my $dest = dbh->quote(scalar($cgi->param('cust_main_invoice_dest')));
       $extra_sql = "
     if ( $cgi->param('cust_main_invoice_dest') ) {
       my $dest = dbh->quote(scalar($cgi->param('cust_main_invoice_dest')));
       $extra_sql = "
-        WHERE EXISTS ( SELECT 1 FROM cust_main_invoice
-                         WHERE cust_main.custnum = cust_main_invoice.custnum
-                           AND dest = $dest
+        WHERE EXISTS ( SELECT 1 FROM cust_contact
+                         JOIN contact USING (contactnum)
+                         JOIN contact_email USING (contactnum)
+                         WHERE cust_main.custnum = cust_contact.custnum
+                           AND contact.invoice_dest = 'Y'
+                           AND contact_email.emailaddress = $dest
                      )
       ";
     } elsif ( $cgi->param('cust_main_invoice_dest_substring') ) {
       my $dest = dbh->quote('%'. scalar($cgi->param('cust_main_invoice_dest_substring')). '%');
       $extra_sql = "
                      )
       ";
     } elsif ( $cgi->param('cust_main_invoice_dest_substring') ) {
       my $dest = dbh->quote('%'. scalar($cgi->param('cust_main_invoice_dest_substring')). '%');
       $extra_sql = "
-        WHERE EXISTS ( SELECT 1 FROM cust_main_invoice
-                         WHERE cust_main.custnum = cust_main_invoice.custnum
-                           AND dest ILIKE $dest
+        WHERE EXISTS ( SELECT 1 FROM cust_contact
+                         JOIN contact USING (contactnum)
+                         JOIN contact_email USING (contactnum)
+                         WHERE cust_main.custnum = cust_contact.custnum
+                           AND contact.invoice_dest = 'Y'
+                           AND contact_email.emailaddress ILIKE $dest
                      )
       ";
     }
                      )
       ";
     }
index effe84b..bdf3431 100755 (executable)
@@ -294,8 +294,7 @@ if ( $cgi->param('error') ) {
   $cust_main->agentnum( $conf->config('default_agentnum') )
     if $conf->exists('default_agentnum');
   $cust_main->referral_custnum( $cgi->param('referral_custnum') );
   $cust_main->agentnum( $conf->config('default_agentnum') )
     if $conf->exists('default_agentnum');
   $cust_main->referral_custnum( $cgi->param('referral_custnum') );
-  @invoicing_list = ();
-  push @invoicing_list, 'POST'
+  $cust_main->set('postal_invoice', 'Y')
     unless $conf->exists('disablepostalinvoicedefault');
   $ss = '';
   $stateid = '';
     unless $conf->exists('disablepostalinvoicedefault');
   $ss = '';
   $stateid = '';
index 32a03bb..c3768ac 100644 (file)
@@ -31,6 +31,8 @@
       $('#spouse_label').slideUp();
       $('#spouse_last_input').slideUp();
       $('#spouse_first_input').slideUp();
       $('#spouse_label').slideUp();
       $('#spouse_last_input').slideUp();
       $('#spouse_first_input').slideUp();
+      $('#invoice_email_label').slideUp();
+      $('#invoice_email_input').slideUp();
     } else {
       if ( document.getElementById('company').value.length == 0 ) {
         $('#company_label').slideUp();
     } else {
       if ( document.getElementById('company').value.length == 0 ) {
         $('#company_label').slideUp();
@@ -40,6 +42,8 @@
       $('#spouse_label').slideDown();
       $('#spouse_last_input').slideDown();
       $('#spouse_first_input').slideDown();
       $('#spouse_label').slideDown();
       $('#spouse_last_input').slideDown();
       $('#spouse_first_input').slideDown();
+      $('#invoice_email_label').slideDown();
+      $('#invoice_email_input').slideDown();
     }
   }
 
     }
   }
 
index 6f716c1..7bca17b 100644 (file)
 
 %   if ( $curuser->access_right('Complimentary customer') ) {
 
 
 %   if ( $curuser->access_right('Complimentary customer') ) {
 
-      <TR>
-        <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="complimentary" VALUE="Y" <% $cust_main->complimentary eq "Y" ? 'CHECKED' : '' %>>Complimentary customer
-      </TR>
+    <& /elements/tr-checkbox.html,
+      field       => 'complimentary',
+      label       => emt('Complimentary customer'),
+      value       => 'Y',
+      curr_value  => $cust_main->complimentary,
+      box_first   => 1,
+    &>
 
 %   } else {
 
 
 %   } else {
 
 
 %   } else {
 
 
 %   } else {
 
-      <TR>
-        <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %></TD>
-      </TR>
+    <& /elements/tr-checkbox.html,
+      field       => 'tax',
+      label       => emt('Tax Exempt' . (scalar(@exempt_groups) ? '(all taxes)' : '') ),
+      value       => 'Y',
+      curr_value  => $cust_main->tax,
+      box_first   => 1,
+    &>
 
 %   }
 
 
 %   }
 
@@ -66,7 +74,7 @@
           <TD STYLE="white-space:nowrap">&nbsp;&nbsp;<INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" ID="tax_<% $exempt_group %>" VALUE="Y" <% $checked ? 'CHECKED' : '' %> onChange="tax_changed(this)"> Tax Exempt (<% $exempt_group %> taxes)</TD>
           <TD> - Exemption number <INPUT TYPE="text" NAME="tax_<% $exempt_group %>_num" ID="tax_<% $exempt_group %>_num" VALUE="<% $cgi->param("tax_$exempt_group".'_num') || ( $cust_main_exemption ? $cust_main_exemption->exempt_number : '' ) |h %>" <% $checked ? '' : 'DISABLED' %>></TD>
         </TR>
           <TD STYLE="white-space:nowrap">&nbsp;&nbsp;<INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" ID="tax_<% $exempt_group %>" VALUE="Y" <% $checked ? 'CHECKED' : '' %> onChange="tax_changed(this)"> Tax Exempt (<% $exempt_group %> taxes)</TD>
           <TD> - Exemption number <INPUT TYPE="text" NAME="tax_<% $exempt_group %>_num" ID="tax_<% $exempt_group %>_num" VALUE="<% $cgi->param("tax_$exempt_group".'_num') || ( $cust_main_exemption ? $cust_main_exemption->exempt_number : '' ) |h %>" <% $checked ? '' : 'DISABLED' %>></TD>
         </TR>
-%     }
+%     } #"
 %   }
 
 %   ###
 %   }
 
 %   ###
 
 % unless ( $conf->exists('emailinvoiceonly') ) {
 
 
 % unless ( $conf->exists('emailinvoiceonly') ) {
 
-    <TR>
-      <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST" <%
-
-        ( grep { $_ eq 'POST' } @invoicing_list )
-
-          ? 'CHECKED'
-          : ''
-
-        %>> <% mt('Postal mail invoices') |h %> 
-
-      </TD>
-    </TR>
+    <& /elements/tr-checkbox.html,
+      field       => 'postal_invoice',
+      label       => emt('Postal mail invoices'),
+      value       => 'Y',
+      curr_value  => $cust_main->postal_invoice,
+      box_first   => 1,
+    &>
 
 % }
 
 
 % }
 
 %   # email invoices
 %   ###
 
 %   # email invoices
 %   ###
 
-    <TR>
-      <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoice_email" VALUE="Y" <%
-
-        ( $cust_main->invoice_noemail eq 'Y' )
-          ? ''
-          : 'CHECKED'
-
-        %>> <% mt('Email invoices') |h %> 
-
-      </TD>
-    </TR>
+    <& /elements/tr-checkbox.html,
+      field       => 'invoice_noemail',
+      label       => emt('Do not send email invoices'),
+      value       => 'Y',
+      curr_value  => $cust_main->invoice_noemail,
+      box_first   => 1,
+    &>
 
 
-% unless ( $conf->exists('cust-email-high-visibility')) {
-   <TR>
-      <TH ALIGN="right" WIDTH="200">
-        <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum) 
-            ? $r : '' %>Email address(es)
-      </TD>
-      <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>">
-      <INPUT TYPE="checkbox" NAME="message_noemail" VALUE="Y" <%
-        ( $cust_main->message_noemail eq 'Y' )
-          ? 'CHECKED'
-          : ''
-        %>> <FONT SIZE="-1"><% emt('Do not send notices') %></FONT>
-      </TD>
-    </TR>
-% }
+    <& /elements/tr-checkbox.html,
+      field       => 'message_noemail',
+      label       => emt('Do not send other email notices'),
+      value       => 'Y',
+      curr_value  => $cust_main->message_noemail,
+      box_first   => 1,
+    &>
 
 %   ###
 %   # prorate_day
 
 %   ###
 %   # prorate_day
index 13bd097..12d9d74 100644 (file)
 </TR>
 % }
 
 </TR>
 % }
 
-% if ( $conf->exists('cust-email-high-visibility') ) {
 <TR>
 <TR>
-  <TH ALIGN="right" CLASS="
+  <TH ALIGN="right">
+    <SPAN ID="invoice_email_label" CLASS="
     <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum)
         ? 'required label'
     <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum)
         ? 'required label'
-        : 'label' %>">Email address(es)
-  </TD>
-  <TD BGCOLOR="#FFFF00">
-    <INPUT TYPE="text" NAME="invoicing_list" 
+        : 'label' %>">Email address(es)</SPAN>
+  </TH>
+  <TD>
+    <INPUT TYPE="text" NAME="invoice_email"  ID="invoice_email_input"
            VALUE="<% $cust_main->invoicing_list_emailonly_scalar %>">
   </TD>
 </TR>
            VALUE="<% $cust_main->invoicing_list_emailonly_scalar %>">
   </TD>
 </TR>
-% }
 <%init>
 my $cust_main = shift;
 my $agentnum = $cust_main->agentnum if $cust_main->custnum;
 <%init>
 my $cust_main = shift;
 my $agentnum = $cust_main->agentnum if $cust_main->custnum;
index 10ec363..2a7185b 100644 (file)
@@ -3,6 +3,7 @@
      'error_redirect' => popurl(3). 'edit/cust_main-contacts.html?',
      'agent_virt'     => 1,
      'skip_process'   => 1, #we don't want to make any changes to cust_main
      'error_redirect' => popurl(3). 'edit/cust_main-contacts.html?',
      'agent_virt'     => 1,
      'skip_process'   => 1, #we don't want to make any changes to cust_main
+     'precheck_callback' => $precheck_callback,
      'process_o2m' => {
        'table'  => 'contact',
        'fields' => FS::contact->cgi_contact_fields,
      'process_o2m' => {
        'table'  => 'contact',
        'fields' => FS::contact->cgi_contact_fields,
      'redirect' => popurl(3). 'view/cust_main.cgi?',
    )
 %>
      'redirect' => popurl(3). 'view/cust_main.cgi?',
    )
 %>
+<%init>
+my $precheck_callback = sub {
+  my $cgi = shift;
+  my $conf = FS::Conf->new;
+  if ( $conf->exists('cust_main-require_invoicing_list_email') ) {
+    my $has_email = 0;
+    foreach my $prefix (grep /^contactnum\d+$/, $cgi->param) {
+      if ( length($cgi->param($prefix . '_emailaddress'))
+           and $cgi->param($prefix . '_invoice_dest') ) {
+        $has_email = 1;
+        last;
+      }
+    }
+    return "At least one contact must receive email invoices"
+      unless $has_email;
+  }
+  '';
+};
+</%init>
index 52a2608..a9f7cf4 100755 (executable)
@@ -29,10 +29,12 @@ $cgi->param('tax','') unless defined $cgi->param('tax');
 
 $cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] );
 
 
 $cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] );
 
-my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
-push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
-push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
-$cgi->param('invoicing_list', join(',', @invoicing_list) );
+#my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
+#push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
+#push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
+#$cgi->param('invoicing_list', join(',', @invoicing_list) );
+
+my $agentnum = $cgi->param('agentnum');
 
 # is this actually used?  if so, we need to clone locations...
 # but I can't find anything that sets this parameter to a non-empty value
 
 # is this actually used?  if so, we need to clone locations...
 # but I can't find anything that sets this parameter to a non-empty value
@@ -78,7 +80,7 @@ my $new = new FS::cust_main ( {
   map { ( "ship_$_", '' ) } (FS::cust_main->location_fields)
 } );
 
   map { ( "ship_$_", '' ) } (FS::cust_main->location_fields)
 } );
 
-$new->invoice_noemail( ($cgi->param('invoice_email') eq 'Y') ? '' : 'Y' );
+warn Dumper( $new ) if $DEBUG > 1;
 
 if ( $duplicate_of ) {
   # then negate all changes to the customer; the only change we should
 
 if ( $duplicate_of ) {
   # then negate all changes to the customer; the only change we should
@@ -157,6 +159,36 @@ if ( $curuser->access_right('Edit customer tax exemptions') ) {
 $options{'contact_params'} = scalar($cgi->Vars);
 $options{'cust_payby_params'} = scalar($cgi->Vars);
 
 $options{'contact_params'} = scalar($cgi->Vars);
 $options{'cust_payby_params'} = scalar($cgi->Vars);
 
+my $email;
+
+if ( $cgi->param('residential_commercial') eq 'Residential' ) {
+
+  $email = $cgi->param('invoice_email') || '';
+  if ( length($email) == 0 and $conf->exists('cust_main-require_invoicing_list_email', $agentnum) ) {
+    $error = 'Email address required';
+  }
+
+  # XXX really should include the phone numbers in here also
+
+} else {
+
+  # contact UI is enabled; everything will be passed through via
+  # contact_params
+  if ($conf->exists('cust_main-require_invoicing_list_email', $agentnum)) {
+    my $has_email = 0;
+    foreach my $prefix (grep /^contactnum\d+$/, $cgi->param) {
+      if ( length($cgi->param($prefix . '_emailaddress'))
+           and $cgi->param($prefix . '_invoice_dest') ) {
+        $has_email = 1;
+        last;
+      }
+    }
+    $error = "At least one contact must receive email invoices"
+      unless $has_email;
+  }
+
+}
+
 #perhaps this stuff should go to cust_main.pm
 if ( $new->custnum eq '' or $duplicate_of ) {
 
 #perhaps this stuff should go to cust_main.pm
 if ( $new->custnum eq '' or $duplicate_of ) {
 
@@ -263,7 +295,8 @@ if ( $new->custnum eq '' or $duplicate_of ) {
   }
   else {
     # create the customer
   }
   else {
     # create the customer
-    $error ||= $new->insert( \%hash, \@invoicing_list,
+    $error ||= $new->insert( \%hash,
+                             [ $email ],
                              %options,
                              prospectnum => scalar($cgi->param('prospectnum')),
                            );
                              %options,
                              prospectnum => scalar($cgi->param('prospectnum')),
                            );
@@ -296,16 +329,14 @@ if ( $new->custnum eq '' or $duplicate_of ) {
     $new->signupdate($old->signupdate);
   }
 
     $new->signupdate($old->signupdate);
   }
 
-  warn "$me calling $new -> replace( $old, \ @invoicing_list )" if $DEBUG;
+  warn "$me calling $new -> replace( $old )" if $DEBUG;
   local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG;
   local($FS::Record::DEBUG)    = $DEBUG if $DEBUG;
 
   local($Data::Dumper::Sortkeys) = 1;
   warn Dumper({ new => $new, old => $old }) if $DEBUG;
 
   local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG;
   local($FS::Record::DEBUG)    = $DEBUG if $DEBUG;
 
   local($Data::Dumper::Sortkeys) = 1;
   warn Dumper({ new => $new, old => $old }) if $DEBUG;
 
-  $error ||= $new->replace( $old, \@invoicing_list,
-                            %options,
-                          );
+  $error ||= $new->replace( $old, [ $email ], %options );
 
   warn "$me returned from replace" if $DEBUG;
   
 
   warn "$me returned from replace" if $DEBUG;
   
index 87e15de..ab14dfb 100644 (file)
 %               }
 %             }
             </SELECT>
 %               }
 %             }
             </SELECT>
-
+%         } elsif ( $field eq 'invoice_dest' ) {
+%           my $curr_value = $cgi->param($name . '_' . $field);
+%           $curr_value = $value if !defined($curr_value);
+            <& select.html,
+                field         => $name . '_' . $field,
+                curr_value    => $curr_value,
+                options       => [ '', 'Y' ],
+                option_labels => { '' => 'no', 'Y' => 'yes' },
+                style         => 'width: 100%',
+            &>
 %         } else {
             <INPUT TYPE  = "text"
                    NAME  = "<%$name%>_<%$field%>"
                    ID    = "<%$id%>_<%$field%>"
                    SIZE  = "<% $size{$field} || 14 %>"
 %         } else {
             <INPUT TYPE  = "text"
                    NAME  = "<%$name%>_<%$field%>"
                    ID    = "<%$id%>_<%$field%>"
                    SIZE  = "<% $size{$field} || 14 %>"
-                   VALUE = "<% scalar($cgi->param($name."_$field"))
+                   VALUE = "<% scalar($cgi->param($name . '_' . $field))
                                || $value |h %>"
                    <% $onchange %>
             >
                                || $value |h %>"
                    <% $onchange %>
             >
@@ -130,6 +139,7 @@ tie my %label, 'Tie::IxHash',
   'last'               => 'Last name',
   'title'              => 'Title/Position',
   'emailaddress'       => 'Email',
   'last'               => 'Last name',
   'title'              => 'Title/Position',
   'emailaddress'       => 'Email',
+  'invoice_dest'       => 'Send invoices',
   'selfservice_access' => 'Self-service'
 ;
 
   'selfservice_access' => 'Self-service'
 ;
 
index 5761263..ed16650 100644 (file)
@@ -9,13 +9,26 @@ Example:
   &>
 
 </%doc>
   &>
 
 </%doc>
-<% include('tr-td-label.html', @_ ) %>
+% if ( $opt{'box_first'} ) {
+  <TR>
+    <TH COLSPAN="<% $opt{'colspan'} || 2 %>"
+      VALIGN = "<% $opt{'valign'} || 'top' %>"
+      STYLE  = "<% $style %>"
+      ID     = "<% $opt{label_id} || $opt{id}. '_label0' %>"
+    >
+      <& checkbox.html, @_ &>
+      <% $required %><% $opt{label} %>
+    </TH>
+  </TR>
+% } else {
+<& tr-td-label.html, @_ &>
 
   <TD <% $style %>>
     <% include('checkbox.html', @_) %>
   </TD>
 
 </TR>
 
   <TD <% $style %>>
     <% include('checkbox.html', @_) %>
   </TD>
 
 </TR>
+% }
 
 <%init>
 
 
 <%init>
 
@@ -25,6 +38,12 @@ my $onchange = $opt{'onchange'}
                  ? 'onChange="'. $opt{'onchange'}. '(this)"'
                  : '';
 
                  ? 'onChange="'. $opt{'onchange'}. '(this)"'
                  : '';
 
-my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+my $style = 'text-align: left; padding-top: 3px';
+$style .= '; '. $opt{'cell_style'} if $opt{'cell_style'};
+
+my $required = $opt{'required'} ? '<font color="#ff0000">*</font>&nbsp;' : '';
+if ($required) {
+  $style .= ';font-weight: bold';
+}
 
 </%init>
 
 </%init>
index f706722..3111f43 100644 (file)
@@ -2,6 +2,9 @@
 
 Actually <TR> <TH> $label </TH>
 
 
 Actually <TR> <TH> $label </TH>
 
+Note that this puts the 'label' argument into the document verbatim, with no
+escaping or localization.
+
 </%doc>
 <TR>
 
 </%doc>
 <TR>
 
index 0d83082..eb9ecc8 100644 (file)
@@ -6,14 +6,14 @@ die 'access denied'
 my $sub = $cgi->param('sub');
 my $email = $cgi->param('arg');
 my @where = (
 my $sub = $cgi->param('sub');
 my $email = $cgi->param('arg');
 my @where = (
-  "cust_main_invoice.dest != 'POST'",
-  "cust_main_invoice.dest LIKE ".dbh->quote('%'.$email.'%'),
+  'contact_email.emailaddress LIKE '.dbh->quote('%'.$email.'%'),
   $FS::CurrentUser::CurrentUser->agentnums_sql(table => 'cust_main'),
 );
 my @cust_main = qsearch({
   'table'     => 'cust_main',
   $FS::CurrentUser::CurrentUser->agentnums_sql(table => 'cust_main'),
 );
 my @cust_main = qsearch({
   'table'     => 'cust_main',
-  'select'    => 'cust_main.*, cust_main_invoice.dest',
-  'addl_from' => 'JOIN cust_main_invoice USING (custnum)',
+  'select'    => 'cust_main.*',
+  'addl_from' => ' JOIN cust_contact USING (custnum) '.
+                 ' JOIN contact_email USING (contactnum)',
   'extra_sql' => 'WHERE '.join(' AND ', @where),
 });
 
   'extra_sql' => 'WHERE '.join(' AND ', @where),
 });
 
index 590409d..d55ee3d 100644 (file)
@@ -1,4 +1,4 @@
-%if ( @cust_contacts ) {
+% if ( $display and @cust_contacts ) {
 <BR>
 <FONT CLASS="fsinnerbox-title">Contacts</FONT>
 
 <BR>
 <FONT CLASS="fsinnerbox-title">Contacts</FONT>
 
@@ -9,6 +9,7 @@
   <%$th%>Type</TH>
   <%$th%>Contact</TH>
   <%$th%>Email</TH>
   <%$th%>Type</TH>
   <%$th%>Contact</TH>
   <%$th%>Email</TH>
+  <%$th%>Send invoices</TH>
   <%$th%>Self-service</TH>
 % foreach my $phone_type (@phone_type) {
     <%$th%><% $phone_type->typename |h %></TH>
   <%$th%>Self-service</TH>
 % foreach my $phone_type (@phone_type) {
     <%$th%><% $phone_type->typename |h %></TH>
@@ -30,7 +31,7 @@
 
 %       my @contact_email = $contact->contact_email;
         <%$td%><% join(', ', map $_->emailaddress, @contact_email) %></TD>
 
 %       my @contact_email = $contact->contact_email;
         <%$td%><% join(', ', map $_->emailaddress, @contact_email) %></TD>
-
+        <%$td%><% $contact->invoice_dest eq 'Y' ? 'Yes' : 'No' %></TD>
         <%$td%>
 %         if ( $cust_contact->selfservice_access ) {
             Enabled
         <%$td%>
 %         if ( $cust_contact->selfservice_access ) {
             Enabled
@@ -75,4 +76,9 @@ my( $cust_main ) = @_;
 
 my @cust_contacts = $cust_main->cust_contact;
 
 
 my @cust_contacts = $cust_main->cust_contact;
 
+# residential customers have a default "invisible" contact, but if they
+# somehow get more than one contact, show them
+my $display = (length($cust_main->residential_commercial) > 0)
+              or ( scalar(@cust_contacts) > 1 );
+
 </%init>
 </%init>