summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-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
8 files changed, 197 insertions, 154 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 9f2cb76f0..4918e793c 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<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 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<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 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 } )
) {