From b2fd002f3285b70311642f8ff0025598d42bd16e Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Wed, 18 Nov 2015 13:07:47 -0800 Subject: [PATCH] track customer invoice destination emails using contact_email, #25536 --- FS/FS/Schema.pm | 2 + FS/FS/contact.pm | 51 ++++++ FS/FS/cust_main.pm | 199 ++++++++++----------- FS/FS/cust_main/Search.pm | 26 ++- FS/FS/cust_main_invoice.pm | 5 + FS/FS/part_event/Condition/nopostal.pm | 8 +- FS/FS/part_event/Condition/postal.pm | 8 +- FS/FS/svc_acct.pm | 52 +++--- httemplate/REST/1.0/cust_main | 18 +- httemplate/edit/cust_main.cgi | 3 +- httemplate/edit/cust_main/basics.html | 4 + httemplate/edit/cust_main/billing.html | 81 ++++----- httemplate/edit/cust_main/name.html | 13 +- httemplate/edit/process/cust_main-contacts.html | 20 +++ httemplate/edit/process/cust_main.cgi | 51 ++++-- httemplate/elements/contact.html | 14 +- httemplate/elements/tr-checkbox.html | 23 ++- httemplate/elements/tr-td-label.html | 3 + .../misc/xmlhttp-cust_main-email_search.html | 8 +- httemplate/view/cust_main/contacts_new.html | 10 +- 20 files changed, 365 insertions(+), 234 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 5a2a9bead..c1ed79cda 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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, '', '', + 'postal_invoice', 'char', 'NULL', 1, '', '', ], '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, '', '', + 'invoice_dest', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'contactnum', 'unique' => [], diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm index 612048022..0428d898b 100644 --- a/FS/FS/contact.pm +++ b/FS/FS/contact.pm @@ -6,6 +6,7 @@ use vars qw( $skip_fuzzyfiles ); 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; @@ -88,6 +89,9 @@ empty or bcrypt disabled +=item invoice_dest + +empty, or 'Y' if email invoices should be sent to this contact =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. +If the object has an C field, L 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 an +L 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 record identifying the type of number: daytime, night, +etc.). + +After inserting the record, if the object has a 'custnum' or 'prospectnum' +field, an L or L 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 { @@ -643,6 +666,7 @@ sub check { || $self->ut_textn('_password') || $self->ut_enum('_password_encoding', [ '', 'bcrypt']) || $self->ut_enum('disabled', [ '', 'Y' ]) + || $self->ut_flag('invoice_dest') ; return $error if $error; @@ -886,6 +910,7 @@ sub cgi_contact_fields { my @contact_fields = qw( classnum first last title comment emailaddress selfservice_access + invoice_dest ); push @contact_fields, 'phonetypenum'. $_->phonetypenum @@ -899,6 +924,32 @@ use FS::upgrade_journal; 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', {})) { diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 1f64b9ec1..4c09d8c1e 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -325,14 +325,7 @@ a better explanation of this, but until then, here's an example: ); $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, I, I, I, I and I. @@ -352,8 +345,8 @@ created and inserted. If I is set, moves contacts and locations from that prospect. -If I is set to an arrayref of FS::contact objects, inserts those -new contacts with this new customer. +If I is set to an arrayref of FS::contact objects, those will be +inserted. If I is set to a hashref of CGI parameters (and I 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 : {}; - 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" @@ -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; @@ -595,6 +578,27 @@ sub insert { 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; @@ -1274,12 +1278,9 @@ To change the customer's address, set the pseudo-fields C and C. 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. @@ -1347,6 +1348,45 @@ sub replace { $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); @@ -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 @@ -1605,6 +1635,7 @@ sub check { || $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)) { @@ -2814,18 +2845,10 @@ sub tax_exemption { =item cust_main_exemption -=item invoicing_list [ ARRAYREF ] - -If an arguement is given, sets these email addresses as invoice recipients -(see L). 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 @@ -2833,47 +2856,13 @@ sub invoicing_list { 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 @@ -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. @@ -2952,10 +2929,11 @@ to receive postal invoices, does nothing. 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 @@ -2969,7 +2947,16 @@ sub invoicing_list_emailonly { 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 diff --git a/FS/FS/cust_main/Search.pm b/FS/FS/cust_main/Search.pm index 097f2fb47..c8a084c9b 100644 --- a/FS/FS/cust_main/Search.pm +++ b/FS/FS/cust_main/Search.pm @@ -531,10 +531,12 @@ sub email_search { if $DEBUG; push @cust_main, - map $_->cust_main, + map { $_->cust_main } + map { $_->cust_contact } + map { $_->contact } qsearch( { - 'table' => 'cust_main_invoice', - 'hashref' => { 'dest' => $email }, + 'table' => 'contact_email', + 'hashref' => { 'emailaddress' => $email }, } ); @@ -808,30 +810,24 @@ sub search { ## 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 ## - 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 ## - 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'}; ## diff --git a/FS/FS/cust_main_invoice.pm b/FS/FS/cust_main_invoice.pm index b6ef26066..6c155aca6 100644 --- a/FS/FS/cust_main_invoice.pm +++ b/FS/FS/cust_main_invoice.pm @@ -11,6 +11,11 @@ use FS::Msgcat qw(gettext); 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; diff --git a/FS/FS/part_event/Condition/nopostal.pm b/FS/FS/part_event/Condition/nopostal.pm index b95cd5c85..ea55ba5d6 100644 --- a/FS/FS/part_event/Condition/nopostal.pm +++ b/FS/FS/part_event/Condition/nopostal.pm @@ -10,17 +10,13 @@ sub condition { 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 ) = @_; - " 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; diff --git a/FS/FS/part_event/Condition/postal.pm b/FS/FS/part_event/Condition/postal.pm index d0bd4194b..1dbe05466 100644 --- a/FS/FS/part_event/Condition/postal.pm +++ b/FS/FS/part_event/Condition/postal.pm @@ -10,17 +10,13 @@ sub condition { 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 ) = @_; - " 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; diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index d3e23f237..9323976cb 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -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::cust_main_invoice; 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 ) { - 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 @@ -800,23 +827,6 @@ sub delete { 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 } ) ) { diff --git a/httemplate/REST/1.0/cust_main b/httemplate/REST/1.0/cust_main index 4656bcb25..5401195fc 100644 --- a/httemplate/REST/1.0/cust_main +++ b/httemplate/REST/1.0/cust_main @@ -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 = " - 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 = " - 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 ) "; } diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index effe84b96..bdf3431d7 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -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') ); - @invoicing_list = (); - push @invoicing_list, 'POST' + $cust_main->set('postal_invoice', 'Y') unless $conf->exists('disablepostalinvoicedefault'); $ss = ''; $stateid = ''; diff --git a/httemplate/edit/cust_main/basics.html b/httemplate/edit/cust_main/basics.html index 32a03bbe6..c3768ac42 100644 --- a/httemplate/edit/cust_main/basics.html +++ b/httemplate/edit/cust_main/basics.html @@ -31,6 +31,8 @@ $('#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(); @@ -40,6 +42,8 @@ $('#spouse_label').slideDown(); $('#spouse_last_input').slideDown(); $('#spouse_first_input').slideDown(); + $('#invoice_email_label').slideDown(); + $('#invoice_email_input').slideDown(); } } diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html index 6f716c1be..7bca17b50 100644 --- a/httemplate/edit/cust_main/billing.html +++ b/httemplate/edit/cust_main/billing.html @@ -25,9 +25,13 @@ % if ( $curuser->access_right('Complimentary customer') ) { - - complimentary eq "Y" ? 'CHECKED' : '' %>>Complimentary customer - + <& /elements/tr-checkbox.html, + field => 'complimentary', + label => emt('Complimentary customer'), + value => 'Y', + curr_value => $cust_main->complimentary, + box_first => 1, + &> % } else { @@ -51,9 +55,13 @@ % } else { - - tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %> - + <& /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 @@    onChange="tax_changed(this)"> Tax Exempt (<% $exempt_group %> taxes) - Exemption number exempt_number : '' ) |h %>" <% $checked ? '' : 'DISABLED' %>> -% } +% } #" % } % ### @@ -75,18 +83,13 @@ % unless ( $conf->exists('emailinvoiceonly') ) { - - > <% mt('Postal mail invoices') |h %> - - - + <& /elements/tr-checkbox.html, + field => 'postal_invoice', + label => emt('Postal mail invoices'), + value => 'Y', + curr_value => $cust_main->postal_invoice, + box_first => 1, + &> % } @@ -94,33 +97,21 @@ % # email invoices % ### - - invoice_noemail eq 'Y' ) - ? '' - : 'CHECKED' - - %>> <% mt('Email invoices') |h %> - - - + <& /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')) { - - - <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum) - ? $r : '' %>Email address(es) - - - message_noemail eq 'Y' ) - ? 'CHECKED' - : '' - %>> <% emt('Do not send notices') %> - - -% } + <& /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 diff --git a/httemplate/edit/cust_main/name.html b/httemplate/edit/cust_main/name.html index 13bd09764..12d9d7405 100644 --- a/httemplate/edit/cust_main/name.html +++ b/httemplate/edit/cust_main/name.html @@ -29,19 +29,18 @@ % } -% if ( $conf->exists('cust-email-high-visibility') ) { - + Email address(es) - - - ">Email address(es) + + + -% } <%init> my $cust_main = shift; my $agentnum = $cust_main->agentnum if $cust_main->custnum; diff --git a/httemplate/edit/process/cust_main-contacts.html b/httemplate/edit/process/cust_main-contacts.html index 10ec3638f..2a7185b5c 100644 --- a/httemplate/edit/process/cust_main-contacts.html +++ b/httemplate/edit/process/cust_main-contacts.html @@ -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 + 'precheck_callback' => $precheck_callback, 'process_o2m' => { 'table' => 'contact', 'fields' => FS::contact->cgi_contact_fields, @@ -10,3 +11,22 @@ '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; + } + ''; +}; + diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 52a2608fd..a9f7cf4ac 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -29,10 +29,12 @@ $cgi->param('tax','') unless defined $cgi->param('tax'); $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 @@ -78,7 +80,7 @@ my $new = new FS::cust_main ( { 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 @@ -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); +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 ) { @@ -263,7 +295,8 @@ if ( $new->custnum eq '' or $duplicate_of ) { } else { # create the customer - $error ||= $new->insert( \%hash, \@invoicing_list, + $error ||= $new->insert( \%hash, + [ $email ], %options, prospectnum => scalar($cgi->param('prospectnum')), ); @@ -296,16 +329,14 @@ if ( $new->custnum eq '' or $duplicate_of ) { $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; - $error ||= $new->replace( $old, \@invoicing_list, - %options, - ); + $error ||= $new->replace( $old, [ $email ], %options ); warn "$me returned from replace" if $DEBUG; diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html index 87e15debe..ab14dfbe8 100644 --- a/httemplate/elements/contact.html +++ b/httemplate/elements/contact.html @@ -59,13 +59,22 @@ % } % } - +% } 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 { > @@ -130,6 +139,7 @@ tie my %label, 'Tie::IxHash', 'last' => 'Last name', 'title' => 'Title/Position', 'emailaddress' => 'Email', + 'invoice_dest' => 'Send invoices', 'selfservice_access' => 'Self-service' ; diff --git a/httemplate/elements/tr-checkbox.html b/httemplate/elements/tr-checkbox.html index 5761263cf..ed166502b 100644 --- a/httemplate/elements/tr-checkbox.html +++ b/httemplate/elements/tr-checkbox.html @@ -9,13 +9,26 @@ Example: &> -<% include('tr-td-label.html', @_ ) %> +% if ( $opt{'box_first'} ) { + + + <& checkbox.html, @_ &> + <% $required %><% $opt{label} %> + + +% } else { +<& tr-td-label.html, @_ &> > <% include('checkbox.html', @_) %> +% } <%init> @@ -25,6 +38,12 @@ my $onchange = $opt{'onchange'} ? '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'} ? '* ' : ''; +if ($required) { + $style .= ';font-weight: bold'; +} diff --git a/httemplate/elements/tr-td-label.html b/httemplate/elements/tr-td-label.html index f7067221b..3111f435c 100644 --- a/httemplate/elements/tr-td-label.html +++ b/httemplate/elements/tr-td-label.html @@ -2,6 +2,9 @@ Actually $label +Note that this puts the 'label' argument into the document verbatim, with no +escaping or localization. + diff --git a/httemplate/misc/xmlhttp-cust_main-email_search.html b/httemplate/misc/xmlhttp-cust_main-email_search.html index 0d830826c..eb9ecc8f6 100644 --- a/httemplate/misc/xmlhttp-cust_main-email_search.html +++ b/httemplate/misc/xmlhttp-cust_main-email_search.html @@ -6,14 +6,14 @@ die 'access denied' 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', - '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), }); diff --git a/httemplate/view/cust_main/contacts_new.html b/httemplate/view/cust_main/contacts_new.html index 590409df9..d55ee3dc3 100644 --- a/httemplate/view/cust_main/contacts_new.html +++ b/httemplate/view/cust_main/contacts_new.html @@ -1,4 +1,4 @@ -%if ( @cust_contacts ) { +% if ( $display and @cust_contacts ) {
Contacts @@ -9,6 +9,7 @@ <%$th%>Type <%$th%>Contact <%$th%>Email + <%$th%>Send invoices <%$th%>Self-service % foreach my $phone_type (@phone_type) { <%$th%><% $phone_type->typename |h %> @@ -30,7 +31,7 @@ % my @contact_email = $contact->contact_email; <%$td%><% join(', ', map $_->emailaddress, @contact_email) %> - + <%$td%><% $contact->invoice_dest eq 'Y' ? 'Yes' : 'No' %> <%$td%> % if ( $cust_contact->selfservice_access ) { Enabled @@ -75,4 +76,9 @@ my( $cust_main ) = @_; 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 ); + -- 2.11.0