use Carp;
use Time::Local;
use Date::Format;
-use Date::Manip;
+#use Date::Manip;
use Mail::Internet;
use Mail::Header;
use Business::CreditCard;
-use FS::UID qw( getotaker );
+use FS::UID qw( getotaker dbh );
use FS::Record qw( qsearchs qsearch );
use FS::cust_pkg;
use FS::cust_bill;
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<tablename> 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,
+but until then, here's an example:
+
+ use Tie::RefHash;
+ tie %hash, 'Tie::RefHash'; #this part is important
+ %hash = {
+ $cust_pkg => [ $svc_acct ],
+ };
+ $cust_main->insert( \%hash );
+
=cut
sub insert {
my $self = shift;
- my $flag = 0;
- if ( $self->payby eq 'PREPAY' ) {
- $self->payby('BILL');
- $flag = 1;
- }
-
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
- my $error = $self->SUPER::insert;
- return $error if $error;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
- if ( $flag ) {
- my $prepay_credit =
- qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+ my $amount = 0;
+ my $seconds = 0;
+ if ( $self->payby eq 'PREPAY' ) {
+ $self->payby('BILL');
+ my $prepay_credit = qsearchs(
+ 'prepay_credit',
+ { 'identifier' => $self->payinfo },
+ '',
+ 'FOR UPDATE'
+ );
warn "WARNING: can't find pre-found prepay_credit: ". $self->payinfo
unless $prepay_credit;
- my $amount = $prepay_credit->amount;
+ $amount = $prepay_credit->amount;
+ $seconds = $prepay_credit->seconds;
my $error = $prepay_credit->delete;
if ( $error ) {
- warn "WARNING: can't delete prepay_credit: ". $self->payinfo;
- } else {
- my $cust_credit = new FS::cust_credit {
- 'custnum' => $self->custnum,
- 'amount' => $amount,
- };
- my $error = $cust_credit->insert;
- warn "WARNING: error inserting cust_credit for prepay_credit: $error"
- if $error;
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( @_ ) {
+ my $cust_pkgs = shift;
+ foreach my $cust_pkg ( keys %$cust_pkgs ) {
+ $cust_pkg->custnum( $self->custnum );
+ $error = $cust_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ foreach my $svc_something ( @{$cust_pkgs->{$cust_pkg}} ) {
+ $svc_something->pkgnum( $cust_pkg->pkgnum );
+ if ( $seconds && $svc_something->isa('FS::svc_acct') ) {
+ $svc_something->seconds( $svc_something->seconds + $seconds );
+ $seconds = 0;
+ }
+ $error = $svc_something->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
}
+ }
+
+ if ( $seconds ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "No svc_acct record to apply pre-paid time";
+ }
+ if ( $amount ) {
+ my $cust_credit = new FS::cust_credit {
+ 'custnum' => $self->custnum,
+ 'amount' => $amount,
+ };
+ $error = $cust_credit->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
sub delete {
my $self = shift;
- if ( qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) ) {
- return "Can't delete a customer with invoices";
- }
- if ( qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) ) {
- return "Can't delete a customer with credits";
- }
-
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ if ( qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with invoices";
+ }
+ if ( qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Can't delete a customer with credits";
+ }
+
my @cust_pkg = qsearch( 'cust_pkg', { 'custnum' => $self->custnum } );
if ( @cust_pkg ) {
my $new_custnum = shift;
- return "Invalid new customer number: $new_custnum"
- unless qsearchs( 'cust_main', { 'custnum' => $new_custnum } );
+ unless ( qsearchs( 'cust_main', { 'custnum' => $new_custnum } ) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Invalid new customer number: $new_custnum";
+ }
foreach my $cust_pkg ( @cust_pkg ) {
my %hash = $cust_pkg->hash;
$hash{'custnum'} = $new_custnum;
my $new_cust_pkg = new FS::cust_pkg ( \%hash );
my $error = $new_cust_pkg->replace($cust_pkg);
- return $error if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
}
foreach my $cust_main_invoice (
qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } )
) {
my $error = $cust_main_invoice->delete;
- return $error if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
- $self->SUPER::delete;
+ my $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
}
=item replace OLD_RECORD
|| $self->ut_text('city')
|| $self->ut_textn('county')
|| $self->ut_textn('state')
- || $self->ut_phonen('daytime')
- || $self->ut_phonen('night')
- || $self->ut_phonen('fax')
;
+ #barf. need message catalogs. i18n. etc.
+ $error .= "Please select a referral."
+ if $error =~ /^Illegal or empty \(numeric\) refnum: /;
return $error if $error;
return "Unknown agent"
} );
}
- $self->zip =~ /^\s*(\w[\w\-\s]{3,8}\w)\s*$/
+ $error =
+ $self->ut_phonen('daytime', $self->country)
+ || $self->ut_phonen('night', $self->country)
+ || $self->ut_phonen('fax', $self->country)
+ ;
+ return $error if $error;
+
+ $self->zip =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/
or return "Illegal zip: ". $self->zip;
$self->zip($1);
}
- if ( $self->paydate eq '' ) {
+ if ( $self->paydate eq '' || $self->paydate eq '-' ) {
return "Expriation date required"
unless $self->payby eq 'BILL' || $self->payby eq 'PREPAY';
$self->paydate('');
sub ncancelled_pkgs {
my $self = shift;
- qsearch( 'cust_pkg', {
- 'custnum' => $self->custnum,
- 'cancel' => '',
- }),
- qsearch( 'cust_pkg', {
- 'custnum' => $self->custnum,
- 'cancel' => 0,
- }),
- ;
+ @{ [ # force list context
+ qsearch( 'cust_pkg', {
+ 'custnum' => $self->custnum,
+ 'cancel' => '',
+ }),
+ qsearch( 'cust_pkg', {
+ 'custnum' => $self->custnum,
+ 'cancel' => 0,
+ }),
+ ] };
}
=item bill OPTIONS
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
# find the packages which are due for billing, find out how much they are
# & generate invoice database.
warn "Error reval-ing part_pkg->recur pkgpart ",
$part_pkg->pkgpart, ": $@";
} else {
- #change this bit to use Date::Manip?
+ #change this bit to use Date::Manip? CAREFUL with timezones (see
+ # mailing list archive)
#$sdate=$cust_pkg->bill || time;
#$sdate=$cust_pkg->bill || $time;
$sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
}
}
- warn "setup is undefinded" unless defined($setup);
- warn "recur is undefinded" unless defined($recur);
- warn "cust_pkg bill is undefinded" unless defined($cust_pkg->bill);
+ warn "setup is undefined" unless defined($setup);
+ warn "recur is undefined" unless defined($recur);
+ warn "cust_pkg bill is undefined" unless defined($cust_pkg->bill);
if ( $cust_pkg_mod_flag ) {
$error=$cust_pkg->replace($old_cust_pkg);
my $charged = sprintf( "%.2f", $total_setup + $total_recur );
- return '' if scalar(@cust_bill_pkg) == 0;
+ unless ( @cust_bill_pkg ) {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
+ }
unless ( $self->getfield('tax') =~ /Y/i
|| $self->getfield('payby') eq 'COMP'
'charged' => $charged,
} );
$error = $cust_bill->insert;
- #shouldn't happen, but how else to handle this? (wrap me in eval, to catch
- # fatal errors)
- die "Error creating cust_bill record: $error!\n",
- "Check updated but unbilled packages for customer", $self->custnum, "\n"
- if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "$error for customer #". $self->custnum;
+ }
my $invnum = $cust_bill->invnum;
my $cust_bill_pkg;
$cust_bill_pkg->setfield( 'invnum', $invnum );
$error = $cust_bill_pkg->insert;
#shouldn't happen, but how else tohandle this?
- die "Error creating cust_bill_pkg record: $error!\n",
- "Check incomplete invoice ", $invnum, "\n"
- if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "$error for customer #". $self->custnum;
+ }
}
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
}
my( $self, %options ) = @_;
my $invoice_time = $options{'invoice_time'} || time;
- my $total_owed = $self->balance;
- warn "collect: total owed $total_owed " if $Debug;
- return '' unless $total_owed > 0; #redundant?????
-
#put below somehow?
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $total_owed = $self->balance;
+ warn "collect: total owed $total_owed " if $Debug;
+ unless ( $total_owed > 0 ) { #redundant?????
+ $dbh->rollback if $oldAutoCommit;
+ return '';
+ }
+
foreach my $cust_bill (
qsearch('cust_bill', { 'custnum' => $self->custnum, } )
) {
'paybatch' => ''
} );
my $error = $cust_pay->insert;
- return 'Error COMPing invnum #' . $cust_bill->invnum .
- ':' . $error if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return 'Error COMPing invnum #'. $cust_bill->invnum. ": $error";
+ }
+
} elsif ( $self->payby eq 'CARD' ) {
if ( $options{'batch_card'} ne 'yes' ) {
- return "Real time card processing not enabled!" unless $processor;
+ unless ( $processor ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Real time card processing not enabled!";
+ }
if ( $processor =~ /^cybercash/ ) {
} elsif ( $processor eq 'cybercash3.2' ) {
%result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
} else {
- return "Unkonwn real-time processor $processor\n";
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown real-time processor $processor";
}
#if ( $result{'MActionCode'} == 7 ) { #cybercash smps v.1.1.3
'paybatch' => "$processor:$paybatch",
} );
my $error = $cust_pay->insert;
- return 'Error applying payment, invnum #' .
- $cust_bill->invnum. ':'. $error if $error;
+ 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.
+ " (CyberCash Order-ID $paybatch): $error";
+ warn $e;
+ return $e;
+ }
} elsif ( $result{'Mstatus'} ne 'failure-bad-money'
|| $options{'report_badcard'} ) {
+ $dbh->commit if $oldAutoCommit;
return 'Cybercash error, invnum #' .
$cust_bill->invnum. ':'. $result{'MErrMsg'};
} else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
return '';
}
} else {
- return "Unkonwn real-time processor $processor\n";
+ $dbh->rollback if $oldAutoCommit;
+ return "Unknown real-time processor $processor\n";
}
} else { #batch card
'amount' => $amount,
} );
my $error = $cust_pay_batch->insert;
- return "Error adding to cust_pay_batch: $error" if $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error adding to cust_pay_batch: $error";
+ }
}
} else {
+ $dbh->rollback if $oldAutoCommit;
return "Unknown payment type ". $self->payby;
}
}
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
=head1 VERSION
-$Id: cust_main.pm,v 1.3 2000-01-31 05:22:23 ivan Exp $
+$Id: cust_main.pm,v 1.12 2001-04-23 07:12:44 ivan Exp $
=head1 BUGS