X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=5ba851de74800fb44059e0ddf57729323225d8e1;hb=fcb68930c8ae99dd835b5d4899b055d7ea38a1d6;hp=590783b277bd2f535790f8dc72a09efb821f6bef;hpb=959a59186f30a33d167b12e67d3c9cc4ce58a0f5;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 590783b27..5ba851de7 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -34,6 +34,7 @@ use Date::Format; use File::Temp; #qw( tempfile ); use Email::Address; use Business::CreditCard 0.28; +use Try::Tiny; use FS::UID qw( getotaker dbh driver_name ); use FS::Record qw( qsearchs qsearch dbdef regexp_sql ); use FS::Misc qw( generate_email send_email generate_ps do_print money_pretty card_types ); @@ -77,6 +78,7 @@ use FS::contact; use FS::Locales; use FS::upgrade_journal; use FS::reason; +use FS::DBI; # 1 is mostly method/subroutine entry and options # 2 traces progress of some operations @@ -266,7 +268,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 @@ -1211,7 +1213,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"; } @@ -1457,7 +1459,7 @@ sub merge { $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"; } @@ -2327,8 +2329,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 ... ] @@ -2432,7 +2440,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) @@ -2495,10 +2503,9 @@ sub cancel_pkgs { } dbh->commit; - $FS::UID::AutoCommit = 1; my @errors; - # now cancel all services, the same way we would for individual packages. - # if any of them fail, cancel the rest anyway. + # try to cancel each service, the same way we would for individual packages, + # but in cancel weight order. my @cust_svc = map { $_->cust_svc } @pkgs; my @sorted_cust_svc = map { $_->[0] } @@ -2511,8 +2518,15 @@ sub cancel_pkgs { foreach my $cust_svc (@sorted_cust_svc) { my $part_svc = $cust_svc->part_svc; next if ( defined($part_svc) and $part_svc->preserve ); - my $error = $cust_svc->cancel; # immediate cancel, no date option - push @errors, $error if $error; + # immediate cancel, no date option + # transactionize individually + my $error = try { $cust_svc->cancel } catch { $_ }; + if ( $error ) { + dbh->rollback; + push @errors, $error; + } else { + dbh->commit; + } } if (@errors) { return @errors; @@ -2540,7 +2554,12 @@ sub cancel_pkgs { } } my $error = $_->cancel(%lopt); - push @errors, 'pkgnum '.$_->pkgnum.': '.$error if $error; + if ( $error ) { + dbh->rollback; + push @errors, 'pkgnum '.$_->pkgnum.': '.$error; + } else { + dbh->commit; + } } return @errors; @@ -2762,6 +2781,10 @@ sub batch_card { ); } + my $paycode = $options{paycode} || ''; + my $batch_type = "DEBIT"; + $batch_type = "CREDIT" if $paycode eq 'C'; + my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; @@ -2774,6 +2797,7 @@ sub batch_card { my %pay_batch = ( 'status' => 'O', 'payby' => FS::payby->payby2payment($payby), + 'type' => $batch_type, ); $pay_batch{agentnum} = $self->agentnum if $conf->exists('batch-spoolagent'); @@ -2847,6 +2871,27 @@ sub batch_card { if ($options{'processing-fee'} > 0) { my $pf_cust_pkg; my $processing_fee_text = 'Payment Processing Fee'; + + unless ( $invnum ) { # probably from a payment screen + # do we have any open invoices? pick earliest + # uses the fact that cust_main->cust_bill sorts by date ascending + my @open = $self->open_cust_bill; + $invnum = $open[0]->invnum if scalar(@open); + } + + unless ( $invnum ) { # still nothing? pick last closed invoice + # again uses fact that cust_main->cust_bill sorts by date ascending + my @closed = $self->cust_bill; + $invnum = $closed[$#closed]->invnum if scalar(@closed); + } + + unless ( $invnum ) { + # XXX: unlikely case - pre-paying before any invoices generated + # what it should do is create a new invoice and pick it + warn '\PROCESS FEE AND NO INVOICES PICKED TO APPLY IT!'; + return ''; + } + my $pf_change_error = $self->charge({ 'amount' => $options{'processing-fee'}, 'pkg' => $processing_fee_text, @@ -3140,7 +3185,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 @@ -3643,6 +3688,31 @@ sub contact_list_email { values %emails; } +=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 => 'contact', + select => 'phonetypenum, phonenum, first, last, countrycode', + addl_from => ' JOIN contact_phone USING (contactnum)', + hashref => { 'custnum' => $self->custnum, 'phonetypenum' => $phone_type, }, + order_by => 'ORDER BY contactnum DESC', + extra_sql => '', + }); + } + =item referral_custnum_cust_main Returns the customer who referred this customer (or the empty string, if