X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=9e305c72ab970ee074f04956ae390e351cdbba97;hp=2e8fe8159516f2cd8264c0d514ab4339c7068959;hb=ffc8927ad1df5a565256bdb47210006afa4b2f78;hpb=829aa888318799d2ff4871c92b0d457abda49714 diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 2e8fe8159..9e305c72a 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -80,6 +80,7 @@ use FS::cust_payby; use FS::contact; use FS::reason; use FS::Misc::Savepoint; +use FS::DBI; # 1 is mostly method/subroutine entry and options # 2 traces progress of some operations @@ -272,7 +273,7 @@ Enable individual CDR spooling, empty or `Y' =item dundate -A suggestion to events (see L) to delay until this unix timestamp +A suggestion to events (see L) to delay until this unix timestamp =item squelch_cdr @@ -743,20 +744,6 @@ sub insert { } } - # FS::geocode_Mixin::after_insert or something? - if ( $conf->config('tax_district_method') and !$import ) { - # if anything non-empty, try to look it up - my $queue = new FS::queue { - 'job' => 'FS::geocode_Mixin::process_district_update', - 'custnum' => $self->custnum, - }; - my $error = $queue->insert( ref($self), $self->custnum ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing tax district update: $error"; - } - } - # cust_main exports! warn " exporting\n" if $DEBUG > 1; @@ -1260,7 +1247,7 @@ sub delete { $ticket_dbh = $dbh; } elsif ($conf->config('ticket_system') eq 'RT_External') { my ($datasrc, $user, $pass) = $conf->config('ticket_system-rt_external_datasrc'); - $ticket_dbh = DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 }); + $ticket_dbh = FS::DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 }); #or die "RT_External DBI->connect error: $DBI::errstr\n"; } @@ -2064,7 +2051,7 @@ Returns a list: an empty list on success or a list of errors. sub unsuspend { my $self = shift; - grep { ($_->get('setup')) && $_->unsuspend } $self->suspended_pkgs; + grep { ($_->get('setup')) && $_->unsuspend } $self->suspended_pkgs(@_); } =item release_hold @@ -2088,8 +2075,14 @@ Returns a list: an empty list on success or a list of errors. =cut sub suspend { - my $self = shift; - grep { $_->suspend(@_) } $self->unsuspended_pkgs; + my($self, %opt) = @_; + + my @pkgs = $self->unsuspended_pkgs; + + @pkgs = grep { ! $_->get('start_date') } @pkgs + if $opt{skip_future_startdate}; + + grep { $_->suspend(%opt) } @pkgs; } =item suspend_if_pkgpart HASHREF | PKGPART [ , PKGPART ... ] @@ -2193,7 +2186,7 @@ FS::cust_pkg::cancel() methods. =item quiet - can be set true to supress email cancellation notices. -=item reason - can be set to a cancellation reason (see L), either a +=item reason - can be set to a cancellation reason (see L), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L) @@ -2744,7 +2737,7 @@ UNIX timestamps; see L). Also see L and L for conversion functions. The empty string can be passed to disable that time constraint completely. -Accepts the same options as L: +Accepts the same options as L: =over 4 @@ -3164,6 +3157,127 @@ sub contact_list_email { @emails; } +=item contact_list_email_destinations + +Returns a list of emails and whether they receive invoices or messages destinations. +{ emailaddress => 'email.com', invoice => 'Y', message => '', } + +=cut + +sub contact_list_email_destinations { + my $self = shift; + warn "$me contact_list_email_destinations" + if $DEBUG; + return () if !$self->custnum; # not yet inserted + return map { $_ } + qsearch({ + table => 'cust_contact', + select => 'emailaddress, cust_contact.invoice_dest as invoice, cust_contact.message_dest as message', + addl_from => ' JOIN contact USING (contactnum) '. + ' JOIN contact_email USING (contactnum)', + hashref => { 'custnum' => $self->custnum, }, + order_by => 'ORDER BY custcontactnum DESC', + extra_sql => '', + }); +} + +=item contact_list_name_phones + +Returns a list of contact phone numbers. +{ phonetypenum => '1', phonenum => 'xxxxxxxxxx', first => 'firstname', last => 'lastname', countrycode => '1' } + +=cut + +sub contact_list_name_phones { + my $self = shift; + my $phone_type = shift; + + warn "$me contact_list_phones" if $DEBUG; + + return () if !$self->custnum; # not yet inserted + return map { $_ } + qsearch({ + table => 'cust_contact', + select => 'phonetypenum, phonenum, first, last, countrycode', + addl_from => ' JOIN contact USING (contactnum) '. + ' JOIN contact_phone USING (contactnum)', + hashref => { 'custnum' => $self->custnum, 'phonetypenum' => $phone_type, }, + order_by => 'ORDER BY custcontactnum DESC', + extra_sql => '', + }); +} + +=item contact_list_emailonly + +Returns an array of hashes containing the emails. Used for displaying contact email field in advanced customer reports. +[ { data => 'email.com', }, ] + +=cut + +sub contact_list_emailonly { + my $self = shift; + warn "$me contact_list_emailonly called" + if $DEBUG; + my @emails; + foreach ($self->contact_list_email_destinations) { + my $data = [ + { + 'data' => $_->emailaddress, + }, + ]; + push @emails, $data; + } + return \@emails; +} + +=item contact_list_cust_invoice_only + +Returns an array of hashes containing cust_contact.invoice_dest. Does this email receive invoices. Used for displaying email Invoice field in advanced customer reports. +[ { data => 'Yes', }, ] + +=cut + +sub contact_list_cust_invoice_only { + my $self = shift; + warn "$me contact_list_cust_invoice_only called" + if $DEBUG; + my @emails; + foreach ($self->contact_list_email_destinations) { + my $invoice = $_->invoice ? 'Yes' : 'No'; + my $data = [ + { + 'data' => $invoice, + }, + ]; + push @emails, $data; + } + return \@emails; +} + +=item contact_list_cust_message_only + +Returns an array of hashes containing cust_contact.message_dest. Does this email receive message notifications. Used for displaying email Message field in advanced customer reports. +[ { data => 'Yes', }, ] + +=cut + +sub contact_list_cust_message_only { + my $self = shift; + warn "$me contact_list_cust_message_only called" + if $DEBUG; + my @emails; + foreach ($self->contact_list_email_destinations) { + my $message = $_->message ? 'Yes' : 'No'; + my $data = [ + { + 'data' => $message, + }, + ]; + push @emails, $data; + } + return \@emails; +} + =item referral_custnum_cust_main Returns the customer who referred this customer (or the empty string, if @@ -5429,7 +5543,13 @@ sub process_bill_and_collect { $param->{'fatal'} = 1; # runs from job queue, will be caught $param->{'retry'} = 1; - $cust_main->bill_and_collect( %$param ); + local $@; + eval { $cust_main->bill_and_collect( %$param) }; + if ( $@ ) { + die $@ =~ /cancel_pkgs cannot be run inside a transaction/ + ? "Bill Now unavailable for customer with pending package expiration\n" + : $@; + } } =item pending_invoice_count @@ -5442,6 +5562,51 @@ sub pending_invoice_count { FS::cust_bill->count( 'custnum = '.shift->custnum."AND pending = 'Y'" ); } +=item cust_locations_missing_district + +Always returns empty list, unless tax_district_method eq 'wa_sales' + +Return cust_location rows for this customer, associated with active +customer packages, where tax district column is empty. Presense of +these rows should block billing, because invoice would be generated +with incorrect taxes + +=cut + +sub cust_locations_missing_district { + my ( $self ) = @_; + + my $tax_district_method = FS::Conf->new->config('tax_district_method'); + + return () + unless $tax_district_method + && $tax_district_method eq 'wa_sales'; + + qsearch({ + table => 'cust_location', + select => 'cust_location.*', + addl_from => ' + LEFT JOIN cust_main USING (custnum) + LEFT JOIN cust_pkg ON cust_location.locationnum = cust_pkg.locationnum + ', + extra_sql => sprintf(q{ + WHERE cust_location.state = 'WA' + AND cust_location.custnum = %s + AND ( + cust_location.district IS NULL + or cust_location.district = '' + ) + AND cust_pkg.pkgnum IS NOT NULL + AND ( + cust_pkg.cancel > %s + OR cust_pkg.cancel IS NULL + ) + }, + $self->custnum, time() + ), + }); +} + #starting to take quite a while for big dbs # (JRNL: journaled so it only happens once per database) # - seq scan of h_cust_main (yuck), but not going to index paycvv, so