X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=23cae96ac04e08e4d9f1f5ec70611ffd0c37b642;hb=5a45d24c6015145d0592a947ae1b1e51de81e1d0;hp=eb265820f05067d2f73f3d18a041597b9f92950d;hpb=6f36d35b9db86ad5b9e42edc27c14b14444771a5;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index eb265820f..23cae96ac 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -6,7 +6,8 @@ package FS::cust_main; use strict; use vars qw( @ISA $conf $lpr $processor $xaction $E_NoErr $invoice_from - $smtpmachine $Debug ); + $smtpmachine $Debug $bop_processor $bop_login $bop_password + $bop_action @bop_options); use Safe; use Carp; use Time::Local; @@ -16,7 +17,7 @@ use Mail::Internet; use Mail::Header; use Business::CreditCard; use FS::UID qw( getotaker dbh ); -use FS::Record qw( qsearchs qsearch ); +use FS::Record qw( qsearchs qsearch dbdef ); use FS::cust_pkg; use FS::cust_bill; use FS::cust_bill_pkg; @@ -71,6 +72,16 @@ $FS::UID::callback{'FS::cust_main'} = sub { $xaction, ) = $conf->config('cybercash2'); $processor='cybercash2'; + } elsif ( $conf->exists('business-onlinepayment') ) { + ( $bop_processor, + $bop_login, + $bop_password, + $bop_action, + @bop_options + ) = $conf->config('business-onlinepayment'); + $bop_action ||= 'normal authorization'; + eval "use Business::OnlinePayment"; + $processor="Business::OnlinePayment::$bop_processor"; } }; @@ -206,15 +217,16 @@ points to. You can ask the object for a copy with the I method. sub table { 'cust_main'; } -=item insert +=item insert [ CUST_PKG_HASHREF [ , INVOICING_LIST_ARYREF ] ] Adds this customer to the database. If there is an error, returns the error, otherwise returns false. -There is a special insert mode in which you pass a data structure to the insert -method containing FS::cust_pkg and FS::svc_I objects. When -running under a transactional database, all records are inserted atomicly, or -the transaction is rolled back. There should be a better explanation of this, +CUST_PKG_HASHREF: If you pass a Tie::RefHash data structure to the insert +method containing FS::cust_pkg and FS::svc_I objects, all records +are inserted atomicly, or the transaction is rolled back (this requries a +transactional database). Passing an empty hash reference is equivalent to +not supplying this parameter. There should be a better explanation of this, but until then, here's an example: use Tie::RefHash; @@ -225,6 +237,15 @@ 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 set set an +invoicing_list destination to the newly-created svc_acct. Here's an example: + + $cust_main->insert( {}, [ $email, 'POST' ] ); + =cut sub insert { @@ -269,7 +290,7 @@ sub insert { return $error; } - if ( @param ) { + if ( @param ) { # CUST_PKG_HASHREF my $cust_pkgs = shift @param; foreach my $cust_pkg ( keys %$cust_pkgs ) { $cust_pkg->custnum( $self->custnum ); @@ -298,6 +319,16 @@ sub insert { return "No svc_acct record to apply pre-paid time"; } + if ( @param ) { # 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 ( $amount ) { my $cust_credit = new FS::cust_credit { 'custnum' => $self->custnum, @@ -394,11 +425,58 @@ sub delete { } -=item replace OLD_RECORD +=item replace OLD_RECORD [ INVOICING_LIST_ARYREF ] Replaces the OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false. +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' ] ); + +=cut + +sub replace { + my $self = shift; + my $old = shift; + my @param = @_; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error = $self->SUPER::replace($old); + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + if ( @param ) { # 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 ); + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + =item check Checks all fields to make sure this is a valid customer record. If there is @@ -422,6 +500,7 @@ sub check { || $self->ut_text('city') || $self->ut_textn('county') || $self->ut_textn('state') + || $self->ut_country('country') || $self->ut_anything('comments') ; #barf. need message catalogs. i18n. etc. @@ -445,8 +524,6 @@ sub check { $self->ss("$1-$2-$3"); } - $self->country =~ /^(\w\w)$/ or return "Illegal country: ". $self->country; - $self->country($1); unless ( qsearchs('cust_main_county', { 'country' => $self->country, 'state' => '', @@ -487,13 +564,11 @@ sub check { || $self->ut_text('ship_city') || $self->ut_textn('ship_county') || $self->ut_textn('ship_state') + || $self->ut_country('ship_country') ; return $error if $error; #false laziness with above - $self->ship_country =~ /^(\w\w)$/ - or return "Illegal ship_country: ". $self->ship_country; - $self->ship_country($1); unless ( qsearchs('cust_main_county', { 'country' => $self->ship_country, 'state' => '', @@ -570,8 +645,6 @@ sub check { or return "Illegal expiration date: ". $self->paydate; if ( length($2) == 4 ) { $self->paydate("$2-$1-01"); - } elsif ( $2 > 97 ) { #should pry change to check for "this year" - $self->paydate("19$2-$1-01"); } else { $self->paydate("20$2-$1-01"); } @@ -952,6 +1025,14 @@ sub collect { return "Real time card processing not enabled!"; } + my $address = $self->address1; + $address .= ", ". $self->address2 if $self->address2; + + #fix exp. date + #$self->paydate =~ /^(\d+)\/\d*(\d{2})$/; + $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/; + my $exp = "$2/$1"; + if ( $processor =~ /^cybercash/ ) { #fix exp. date for cybercash @@ -965,10 +1046,8 @@ sub collect { my $payname = $self->payname || $self->getfield('first'). ' '. $self->getfield('last'); - my $address = $self->address1; - $address .= ", ". $self->address2 if $self->address2; - my $country = 'USA' if $self->country eq 'US'; + my $country = $self->country eq 'US' ? 'USA' : $self->country; my @full_xaction = ( $xaction, 'Order-ID' => $paybatch, @@ -1026,6 +1105,73 @@ sub collect { return ''; } + } elsif ( $processor =~ /^Business::OnlinePayment::(.*)$/ ) { + + my($payname, $payfirst, $paylast); + if ( $self->payname ) { + $payname = $self->payname; + $payname =~ /^\s*([\w \,\.\-\']*\w)?\s+([\w\,\.\-\']+)$/ + or do { + $dbh->rollback if $oldAutoCommit; + return "Illegal payname $payname"; + }; + ($payfirst, $paylast) = ($1, $2); + } else { + $payfirst = $self->getfield('first'); + $paylast = $self->getfield('first'); + $payname = "$payfirst $paylast"; + } + + my $transaction = new Business::OnlinePayment( $1, @bop_options ); + $transaction->content( + 'type' => 'CC', + 'login' => $bop_login, + 'password' => $bop_password, + 'action' => $bop_action, + 'amount' => $amount, + 'invoice_number' => $cust_bill->invnum, + 'customer_id' => $self->custnum, + 'last_name' => $paylast, + 'first_name' => $payfirst, + 'name' => $payname, + 'address' => $address, + 'city' => $self->city, + 'state' => $self->state, + 'zip' => $self->zip, + 'country' => $self->country, + 'card_number' => $self->payinfo, + 'expiration' => $exp, + ); + $transaction->submit(); + + if ( $transaction->is_success()) { + my $cust_pay = new FS::cust_pay ( { + 'invnum' => $cust_bill->invnum, + 'paid' => $amount, + '_date' => '', + 'payby' => 'CARD', + 'payinfo' => $self->payinfo, + 'paybatch' => "$processor:". $transaction->authorization, + } ); + my $error = $cust_pay->insert; + if ( $error ) { + # gah, even with transactions. + $dbh->commit if $oldAutoCommit; #well. + my $e = 'WARNING: Card debited but database not updated - '. + 'error applying payment, invnum #' . $cust_bill->invnum. + " ($processor): $error"; + warn $e; + return $e; + } + } elsif ( $options{'report_badcard'} ) { + $dbh->commit if $oldAutoCommit; + return "$processor error, invnum #". $cust_bill->invnum. ': '. + $transaction->result_code. ": ". $transaction->error_message; + } else { + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + return '' + } + } else { $dbh->rollback if $oldAutoCommit; return "Unknown real-time processor $processor\n"; @@ -1198,9 +1344,28 @@ sub check_invoicing_list { =back +=head1 SUBROUTINES + +=over 4 + +=item rebuild_fuzzyfile + +=cut + +sub rebuild_fuzzyfiles { + my @all_last = map $_->getfield('last'), qsearch('cust_main', {}); + push @all_last, + grep $_, map $_->getfield('ship_last'), qsearch('cust_main',{}) + if defined dbdef->table('cust_main')->column('ship_last'); +# open( + +} + +=back + =head1 VERSION -$Id: cust_main.pm,v 1.16 2001-08-11 05:52:15 ivan Exp $ +$Id: cust_main.pm,v 1.20 2001-08-23 06:17:03 ivan Exp $ =head1 BUGS