X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=d0d85b93a436ae7b8e30144c207ff88214995efb;hb=58dfeb5412c54d676119d1c6b8a9ff7edde7e234;hp=8058357a5ca2f015f08234d93a555e7956dc8f18;hpb=f2166c28623ef38fe7db2069fce6cc16be865f11;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 8058357a5..d0d85b93a 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 ); @@ -2145,6 +2146,10 @@ sub check { if !$import && !$ignore_expired_card && ( $y<$nowy || ( $y==$nowy && $1<$nowm ) ); + + if ( my $error = $self->ut_daten('paydate') ) { + return $error; + } } if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ && @@ -2299,7 +2304,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 @@ -2491,10 +2496,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] } @@ -2507,8 +2511,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; @@ -2536,7 +2547,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; @@ -2790,7 +2806,7 @@ sub batch_card { } ); foreach (qw( address1 address2 city state zip country latitude longitude - payby payinfo paydate payname )) + payby payinfo paydate payname paycode paytype )) { $options{$_} = '' unless exists($options{$_}); } @@ -2822,6 +2838,7 @@ sub batch_card { 'exp' => $options{paydate} || $self->paydate, 'payname' => $options{payname} || $self->payname, 'amount' => $amount, # consolidating + 'paycode' => $options{paycode} || '', } ); $cust_pay_batch->paybatchnum($old_cust_pay_batch->paybatchnum) @@ -2839,6 +2856,62 @@ sub batch_card { die $error; } + 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, + 'setuptax' => 'Y', + 'cust_pkg_ref' => \$pf_cust_pkg, + }); + + if($pf_change_error) { + warn 'Unable to add payment processing fee'; + return ''; + } + + $pf_cust_pkg->setup(time); + my $pf_error = $pf_cust_pkg->replace; + if($pf_error) { + warn 'Unable to set setup time on cust_pkg for processing fee'; + # but keep going... + } + + my $cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum }); + unless ( $cust_bill ) { + warn "race condition + invoice deletion just happened"; + return ''; + } + + my $grand_pf_error = + $cust_bill->add_cc_surcharge($pf_cust_pkg->pkgnum,$options{'processing-fee'}); + + warn "cannot add Processing fee to invoice #$invnum: $grand_pf_error" + if $grand_pf_error; + } + my $unapplied = $self->total_unapplied_credits + $self->total_unapplied_payments + $self->in_transit_payments; @@ -5642,8 +5715,86 @@ sub _upgrade_data { #class method FS::Setup::enable_encryption(); } + $class->_upgrade_data_paydate_edgebug; +} + +=item _upgrade_data_paydate_edgebug + +Correct bad data injected into payment expire date column by Edge browser bug + +The month and year values may have an extra character injected into form POST +data by Edge browser. It was possible for some bad month values to slip +past data validation. + +If the stored value was out of range, it was causing payments screen to crash. +We can detect and fix this by dropping the second digit. + +If the stored value is is 11 or 12, it's possible the user inputted a 1. In +this case, the payment method will fail to authorize, but the record will +not cause crashdumps for being out of range. + +In short, check for any expiration month > 12, and drop the extra digit + +=cut + +sub _upgrade_data_paydate_edgebug { + my $journal_label = 'cust_main_paydate_edgebug'; + return if FS::upgrade_journal->is_done( $journal_label ); + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + + for my $row ( + FS::Record::qsearch( + cust_main => { paydate => { op => '!=', value => '' }} + ) + ) { + next unless $row->ut_daten('paydate'); + + # paydate column stored in database has failed date validation + my $bad_paydate = $row->paydate; + + my @date = split /[\-\/]/, $bad_paydate; + @date = @date[2,0,1] if $date[2] > 1900; + + # Only autocorrecting when month > 12 - notify operator + unless ( $date[1] > 12 ) { + die sprintf( + 'Unable to correct bad paydate stored in cust_main row '. + 'custnum(%s) paydate(%s)', + $row->custnum, + $bad_paydate, + ); + } + + $date[1] = substr( $date[1], 0, 1 ); + $row->paydate( join('-', @date )); + + if ( my $error = $row->replace ) { + die sprintf( + 'Failed to autocorrect bad paydate stored in cust_main row '. + 'custnum(%s) paydate(%s) - error: %s', + $row->custnum, + $bad_paydate, + $error + ); + } + + warn sprintf( + 'Autocorrected bad paydate stored in cust_main row '. + "custnum(%s) old-paydate(%s) new-paydate(%s)\n", + $row->custnum, + $bad_paydate, + $row->paydate, + ); + + } + + FS::upgrade_journal->set_done( $journal_label ); + dbh->commit unless $oldAutoCommit; } + sub queueable_upgrade { my $class = shift;