summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Schema.pm2
-rw-r--r--FS/FS/contact.pm51
-rw-r--r--FS/FS/cust_main.pm199
-rw-r--r--FS/FS/cust_main/Search.pm26
-rw-r--r--FS/FS/cust_main_invoice.pm5
-rw-r--r--FS/FS/part_event/Condition/nopostal.pm8
-rw-r--r--FS/FS/part_event/Condition/postal.pm8
-rw-r--r--FS/FS/svc_acct.pm52
-rw-r--r--httemplate/REST/1.0/cust_main18
-rwxr-xr-xhttemplate/edit/cust_main.cgi3
-rw-r--r--httemplate/edit/cust_main/basics.html4
-rw-r--r--httemplate/edit/cust_main/billing.html81
-rw-r--r--httemplate/edit/cust_main/name.html13
-rw-r--r--httemplate/edit/process/cust_main-contacts.html20
-rwxr-xr-xhttemplate/edit/process/cust_main.cgi51
-rw-r--r--httemplate/elements/contact.html14
-rw-r--r--httemplate/elements/tr-checkbox.html23
-rw-r--r--httemplate/elements/tr-td-label.html3
-rw-r--r--httemplate/misc/xmlhttp-cust_main-email_search.html8
-rw-r--r--httemplate/view/cust_main/contacts_new.html10
20 files changed, 365 insertions, 234 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 5a2a9be..c1ed79c 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 6120480..0428d89 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<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 {
@@ -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 1f64b9e..4c09d8c 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<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<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
@@ -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<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.
-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>.
@@ -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<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
@@ -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 097f2fb..c8a084c 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 b6ef260..6c155ac 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 b95cd5c..ea55ba5 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 d0bd419..1dbe054 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 d3e23f2..9323976 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 4656bcb..5401195 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 effe84b..bdf3431 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 32a03bb..c3768ac 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 6f716c1..7bca17b 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') ) {
- <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 {
@@ -51,9 +55,13 @@
% } 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>
-% }
+% } #"
% }
% ###
@@ -75,18 +83,13 @@
% 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,
+ &>
% }
@@ -94,33 +97,21 @@
% # 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
diff --git a/httemplate/edit/cust_main/name.html b/httemplate/edit/cust_main/name.html
index 13bd097..12d9d74 100644
--- a/httemplate/edit/cust_main/name.html
+++ b/httemplate/edit/cust_main/name.html
@@ -29,19 +29,18 @@
</TR>
% }
-% if ( $conf->exists('cust-email-high-visibility') ) {
<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'
- : '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>
-% }
<%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 10ec363..2a7185b 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;
+ }
+ '';
+};
+</%init>
diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi
index 52a2608..a9f7cf4 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 87e15de..ab14dfb 100644
--- a/httemplate/elements/contact.html
+++ b/httemplate/elements/contact.html
@@ -59,13 +59,22 @@
% }
% }
</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 %>"
- VALUE = "<% scalar($cgi->param($name."_$field"))
+ VALUE = "<% scalar($cgi->param($name . '_' . $field))
|| $value |h %>"
<% $onchange %>
>
@@ -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 5761263..ed16650 100644
--- a/httemplate/elements/tr-checkbox.html
+++ b/httemplate/elements/tr-checkbox.html
@@ -9,13 +9,26 @@ Example:
&>
</%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>
+% }
<%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'} ? '<font color="#ff0000">*</font>&nbsp;' : '';
+if ($required) {
+ $style .= ';font-weight: bold';
+}
</%init>
diff --git a/httemplate/elements/tr-td-label.html b/httemplate/elements/tr-td-label.html
index f706722..3111f43 100644
--- a/httemplate/elements/tr-td-label.html
+++ b/httemplate/elements/tr-td-label.html
@@ -2,6 +2,9 @@
Actually <TR> <TH> $label </TH>
+Note that this puts the 'label' argument into the document verbatim, with no
+escaping or localization.
+
</%doc>
<TR>
diff --git a/httemplate/misc/xmlhttp-cust_main-email_search.html b/httemplate/misc/xmlhttp-cust_main-email_search.html
index 0d83082..eb9ecc8 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 590409d..d55ee3d 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 ) {
<BR>
<FONT CLASS="fsinnerbox-title">Contacts</FONT>
@@ -9,6 +9,7 @@
<%$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>
@@ -30,7 +31,7 @@
% 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
@@ -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 );
+
</%init>