X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=fbd461841192e4d1b189555458da4cdf0da3be77;hp=59e357055535a505a166bb5d2657e9f80082b778;hb=08feb6698a96eed199404b8319a39ec4c02a084e;hpb=73f34cbe08a9be968a2557cd6906dde5bb9a97a3 diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 59e357055..fbd461841 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -895,7 +895,7 @@ sub check { my( $m, $y ); if ( $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ ) { ( $m, $y ) = ( $1, length($2) == 4 ? $2 : "20$2" ); - } elsif ( $self->paydate =~ /^(20)?(\d{2})[\/\-](\d{2})[\/\-]\d+$/ ) { + } elsif ( $self->paydate =~ /^(20)?(\d{2})[\/\-](\d{1,2})[\/\-]\d+$/ ) { ( $m, $y ) = ( $3, "20$2" ); } else { return "Illegal expiration date: ". $self->paydate; @@ -1028,6 +1028,38 @@ sub suspend { grep { $_->suspend } $self->unsuspended_pkgs; } +=item suspend_if_pkgpart PKGPART [ , PKGPART ... ] + +Suspends all unsuspended packages (see L) matching the listed +PKGPARTs (see L). Always returns a list: an empty list on +success or a list of errors. + +=cut + +sub suspend_if_pkgpart { + my $self = shift; + my @pkgparts = @_; + grep { $_->suspend } + grep { my $pkgpart = $_->pkgpart; grep { $pkgpart eq $_ } @pkgparts } + $self->unsuspended_pkgs; +} + +=item suspend_unless_pkgpart PKGPART [ , PKGPART ... ] + +Suspends all unsuspended packages (see L) unless they match the +listed PKGPARTs (see L). Always returns a list: an empty list +on success or a list of errors. + +=cut + +sub suspend_unless_pkgpart { + my $self = shift; + my @pkgparts = @_; + grep { $_->suspend } + grep { my $pkgpart = $_->pkgpart; ! grep { $pkgpart eq $_ } @pkgparts } + $self->unsuspended_pkgs; +} + =item cancel [ OPTION => VALUE ... ] Cancels all uncancelled packages (see L) for this customer. @@ -1098,6 +1130,8 @@ sub bill { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + $self->select_for_update; #mutex + # find the packages which are due for billing, find out how much they are # & generate invoice database. @@ -1499,6 +1533,8 @@ sub collect { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + $self->select_for_update; #mutex + my $balance = $self->balance; warn "collect customer". $self->custnum. ": balance $balance" if $DEBUG; unless ( $balance > 0 ) { #redundant????? @@ -1533,7 +1569,7 @@ sub collect { || $a->weight <=> $b->weight || $a->eventpart <=> $b->eventpart } grep { $_->seconds <= ( $invoice_time - $cust_bill->_date ) - && ! qsearchs( 'cust_bill_event', { + && ! qsearch( 'cust_bill_event', { 'invnum' => $cust_bill->invnum, 'eventpart' => $_->eventpart, 'status' => 'done', @@ -1875,15 +1911,19 @@ sub realtime_bop { } ); my $error = $cust_pay->insert; if ( $error ) { - # gah, even with transactions. - my $e = 'WARNING: Card/ACH debited but database not updated - '. - 'error applying payment, invnum #' . $self->invnum. - " ($processor): $error"; - warn $e; - return $e; - } else { - return ''; + $cust_pay->invnum(''); #try again with no specific invnum + my $error2 = $cust_pay->insert; + if ( $error2 ) { + # gah, even with transactions. + my $e = 'WARNING: Card/ACH debited but database not updated - '. + "error inserting payment ($processor): $error2". + " (previously tried insert with invnum #$options{'invnum'}" . + ": $error )"; + warn $e; + return $e; + } } + return ''; #no error } else { @@ -2143,6 +2183,19 @@ sub paydate_monthyear { } } +=item payinfo_masked + +Returns a "masked" payinfo field with all but the last four characters replaced +by 'x'es. Useful for displaying credit cards. + +=cut + +sub payinfo_masked { + my $self = shift; + my $payinfo = $self->payinfo; + 'x'x(length($payinfo)-4). substr($payinfo,(length($payinfo)-4)); +} + =item invoicing_list [ ARRAYREF ] If an arguement is given, sets these email addresses as invoice recipients @@ -2473,6 +2526,18 @@ sub cust_refund { qsearch( 'cust_refund', { 'custnum' => $self->custnum } ) } +=item select_for_update + +Selects this record with the SQL "FOR UPDATE" command. This can be useful as +a mutex. + +=cut + +sub select_for_update { + my $self = shift; + qsearch('cust_main', { 'custnum' => $self->custnum }, '*', 'FOR UPDATE' ); +} + =back =head1 SUBROUTINES @@ -2663,7 +2728,7 @@ sub batch_import { my %cust_main = ( agentnum => $agentnum, refnum => $refnum, - country => 'US', #default + country => $conf->config('countrydefault') || 'US', payby => 'BILL', #default paydate => '12/2037', #default ); @@ -2824,6 +2889,8 @@ card types. No multiple currency support (probably a larger project than just this module). +payinfo_masked false laziness with cust_pay.pm and cust_refund.pm + =head1 SEE ALSO L, L, L, L