package FS::cust_main;
use strict;
-use vars qw(@ISA @EXPORT_OK $conf $lpr $processor $xaction $E_NoErr);
+use vars qw( @ISA $conf $lpr $processor $xaction $E_NoErr $invoice_from
+ $smtpmachine );
use Safe;
-use Exporter;
use Carp;
use Time::Local;
use Date::Format;
use Date::Manip;
+use Mail::Internet;
+use Mail::Header;
use Business::CreditCard;
-use FS::UID qw(getotaker);
-use FS::Record qw(fields hfields qsearchs qsearch);
+use FS::UID qw( getotaker );
+use FS::Record qw( qsearchs qsearch );
use FS::cust_pkg;
use FS::cust_bill;
use FS::cust_bill_pkg;
use FS::cust_pay;
-#use FS::cust_pay_batch;
-
-@ISA = qw(FS::Record Exporter);
-@EXPORT_OK = qw(hfields);
-
-$conf = new FS::Conf;
-$lpr = $conf->config('lpr');
-
-if ( $conf->exists('cybercash3.2') ) {
- require CCMckLib3_2;
- #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
- require CCMckDirectLib3_2;
- #qw(SendCC2_1Server);
- require CCMckErrno3_2;
- #qw(MCKGetErrorMessage $E_NoErr);
- import CCMckErrno3_2 qw($E_NoErr);
- my $merchant_conf;
- ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
- my $status = &CCMckLib3_2::InitConfig($merchant_conf);
- if ( $status != $E_NoErr ) {
- warn "CCMckLib3_2::InitConfig error:\n";
- foreach my $key (keys %CCMckLib3_2::Config) {
- warn " $key => $CCMckLib3_2::Config{$key}\n"
+use FS::cust_credit;
+use FS::cust_pay_batch;
+use FS::part_referral;
+use FS::cust_main_county;
+use FS::agent;
+use FS::cust_main_invoice;
+
+@ISA = qw( FS::Record );
+
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::cust_main'} = sub {
+ $conf = new FS::Conf;
+ $lpr = $conf->config('lpr');
+ $invoice_from = $conf->config('invoice_from');
+ $smtpmachine = $conf->config('smtpmachine');
+
+ if ( $conf->exists('cybercash3.2') ) {
+ require CCMckLib3_2;
+ #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
+ require CCMckDirectLib3_2;
+ #qw(SendCC2_1Server);
+ require CCMckErrno3_2;
+ #qw(MCKGetErrorMessage $E_NoErr);
+ import CCMckErrno3_2 qw($E_NoErr);
+
+ my $merchant_conf;
+ ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
+ my $status = &CCMckLib3_2::InitConfig($merchant_conf);
+ if ( $status != $E_NoErr ) {
+ warn "CCMckLib3_2::InitConfig error:\n";
+ foreach my $key (keys %CCMckLib3_2::Config) {
+ warn " $key => $CCMckLib3_2::Config{$key}\n"
+ }
+ my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
+ die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
}
- my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
- die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
+ $processor='cybercash3.2';
+ } elsif ( $conf->exists('cybercash2') ) {
+ require CCLib;
+ #qw(sendmserver);
+ ( $main::paymentserverhost,
+ $main::paymentserverport,
+ $main::paymentserversecret,
+ $xaction,
+ ) = $conf->config('cybercash2');
+ $processor='cybercash2';
}
- $processor='cybercash3.2';
-} elsif ( $conf->exists('cybercash2') ) {
- require CCLib;
- #qw(sendmserver);
- ( $main::paymentserverhost,
- $main::paymentserverport,
- $main::paymentserversecret,
- $xaction,
- ) = $conf->config('cybercash2');
- $processor='cybercash2';
-}
+};
=head1 NAME
use FS::cust_main;
- $record = create FS::cust_main \%hash;
- $record = create FS::cust_main { 'column' => 'value' };
+ $record = new FS::cust_main \%hash;
+ $record = new FS::cust_main { 'column' => 'value' };
$error = $record->insert;
=over 4
-=item create HASHREF
+=item new HASHREF
Creates a new customer. To add the customer to the database, see L<"insert">.
=cut
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my $field;
- #foreach $field (fields('cust_main')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_main',$hashref);
-}
+sub table { 'cust_main'; }
=item insert
Adds this customer to the database. If there is an error, returns the error,
otherwise returns false.
-=cut
-
-sub insert {
- my($self)=@_;
-
- #no callbacks in check, only data checks
- #local $SIG{HUP} = 'IGNORE';
- #local $SIG{INT} = 'IGNORE';
- #local $SIG{QUIT} = 'IGNORE';
- #local $SIG{TERM} = 'IGNORE';
- #local $SIG{TSTP} = 'IGNORE';
-
- $self->check or
- $self->add;
-}
-
=item delete
Currently unimplemented. Maybe cancel all of this customer's
=cut
-# Usage: $error = $record -> delete;
sub delete {
return "Can't (yet?) delete customers.";
-# my($self)=@_;
-#
-# $self->del;
}
=item replace OLD_RECORD
Replaces the OLD_RECORD with this one in the database. If there is an error,
returns the error, otherwise returns false.
-=cut
-
-sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_main record!" unless $old->table eq "cust_main";
- return "Can't change custnum!"
- unless $old->getfield('custnum') eq $new->getfield('custnum');
- $new->check or
- $new->rep($old);
-}
-
=item check
Checks all fields to make sure this is a valid customer record. If there is
=cut
sub check {
- my($self)=@_;
-
- return "Not a cust_main record!" unless $self->table eq "cust_main";
+ my $self = shift;
my $error =
- $self->ut_number('agentnum')
+ $self->ut_numbern('custnum')
+ || $self->ut_number('agentnum')
|| $self->ut_number('refnum')
|| $self->ut_textn('company')
|| $self->ut_text('address1')
return $error if $error;
return "Unknown agent"
- unless qsearchs('agent',{'agentnum'=>$self->agentnum});
+ unless qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
return "Unknown referral"
- unless qsearchs('part_referral',{'refnum'=>$self->refnum});
+ unless qsearchs( 'part_referral', { 'refnum' => $self->refnum } );
- $self->getfield('last') =~ /^([\w \,\.\-\']+)$/ or return "Illegal last name";
+ $self->getfield('last') =~ /^([\w \,\.\-\']+)$/
+ or return "Illegal last name: ". $self->getfield('last');
$self->setfield('last',$1);
- $self->first =~ /^([\w \,\.\-\']+)$/ or return "Illegal first name";
+ $self->first =~ /^([\w \,\.\-\']+)$/
+ or return "Illegal first name: ". $self->first;
$self->first($1);
if ( $self->ss eq '' ) {
my $ss = $self->ss;
$ss =~ s/\D//g;
$ss =~ /^(\d{3})(\d{2})(\d{4})$/
- or return "Illegal social security number";
+ or return "Illegal social security number: ". $self->ss;
$self->ss("$1-$2-$3");
}
- return "Unknown state/county/country"
- unless qsearchs('cust_main_county',{
- 'state' => $self->state,
- 'county' => $self->county,
- } );
+ $self->country =~ /^(\w\w)$/ or return "Illegal country: ". $self->country;
+ $self->country($1);
+ unless ( qsearchs('cust_main_county', {
+ 'country' => $self->country,
+ 'state' => '',
+ } ) ) {
+ return "Unknown state/county/country: ".
+ $self->state. "/". $self->county. "/". $self->country
+ unless qsearchs('cust_main_county',{
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ } );
+ }
- #int'l zips?
- $self->zip =~ /^(\d{5}(-\d{4})?)$/ or return "Illegal zip";
+ $self->zip =~ /^\s*(\w[\w\-\s]{3,8}\w)\s*$/
+ or return "Illegal zip: ". $self->zip;
$self->zip($1);
- #int'l countries!
- $self->country =~ /^(US)$/ or return "Illegal country";
- $self->country($1);
-
- $self->payby =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby";
+ $self->payby =~ /^(CARD|BILL|COMP)$/
+ or return "Illegal payby: ". $self->payby;
$self->payby($1);
if ( $self->payby eq 'CARD' ) {
my $payinfo = $self->payinfo;
$payinfo =~ s/\D//g;
$payinfo =~ /^(\d{13,16})$/
- or return "Illegal credit card number";
+ or return "Illegal credit card number: ". $self->payinfo;
$payinfo = $1;
$self->payinfo($payinfo);
- validate($payinfo) or return "Illegal credit card number";
- my $type = cardtype($payinfo);
- return "Unknown credit card type"
- unless ( $type =~ /^VISA/ ||
- $type =~ /^MasterCard/ ||
- $type =~ /^American Express/ ||
- $type =~ /^Discover/ );
+ validate($payinfo)
+ or return "Illegal credit card number: ". $self->payinfo;
+ return "Unknown card type" if cardtype($self->payinfo) eq "Unknown";
} elsif ( $self->payby eq 'BILL' ) {
- $self->payinfo =~ /^([\w \-]*)$/ or return "Illegal P.O. number";
- $self->payinfo($1);
+ $error = $self->ut_textn('payinfo');
+ return "Illegal P.O. number: ". $self->payinfo if $error;
} elsif ( $self->payby eq 'COMP' ) {
- $self->payinfo =~ /^(\w{2,8})$/ or return "Illegal comp account issuer";
- $self->payinfo($1);
+ $error = $self->ut_textn('payinfo');
+ return "Illegal comp account issuer: ". $self->payinfo if $error;
}
$self->paydate('');
} else {
$self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/
- or return "Illegal expiration date";
+ 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->payname( $self->first. " ". $self->getfield('last') );
} else {
$self->payname =~ /^([\w \,\.\-\']+)$/
- or return "Illegal billing name";
+ or return "Illegal billing name: ". $self->payname;
$self->payname($1);
}
- $self->tax =~ /^(Y?)$/ or return "Illegal tax";
+ $self->tax =~ /^(Y?)$/ or return "Illegal tax: ". $self->tax;
$self->tax($1);
$self->otaker(getotaker);
=cut
sub all_pkgs {
- my($self)=@_;
+ my $self = shift;
qsearch( 'cust_pkg', { 'custnum' => $self->custnum });
}
=cut
sub ncancelled_pkgs {
- my($self)=@_;
+ my $self = shift;
qsearch( 'cust_pkg', {
'custnum' => $self->custnum,
'cancel' => '',
=cut
sub bill {
- my($self,%options)=@_;
- my($time) = $options{'time'} || $^T;
+ my( $self, %options ) = @_;
+ my $time = $options{'time'} || time;
- my($error);
+ my $error;
#put below somehow?
local $SIG{HUP} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
local $SIG{TERM} = 'IGNORE';
local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
# find the packages which are due for billing, find out how much they are
# & generate invoice database.
- my($total_setup,$total_recur)=(0,0);
-
- my(@cust_bill_pkg);
+ my( $total_setup, $total_recur ) = ( 0, 0 );
+ my @cust_bill_pkg;
- my($cust_pkg);
- foreach $cust_pkg (
+ foreach my $cust_pkg (
qsearch('cust_pkg',{'custnum'=> $self->getfield('custnum') } )
) {
- bless($cust_pkg,"FS::cust_pkg");
-
- next if ( $cust_pkg->getfield('cancel') );
+ next if $cust_pkg->getfield('cancel');
#? to avoid use of uninitialized value errors... ?
$cust_pkg->setfield('bill', '')
unless defined($cust_pkg->bill);
- my($part_pkg)=
- qsearchs('part_pkg',{'pkgpart'=> $cust_pkg->pkgpart } );
+ my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $cust_pkg->pkgpart } );
#so we don't modify cust_pkg record unnecessarily
- my($cust_pkg_mod_flag)=0;
- my(%hash)=$cust_pkg->hash;
- my($old_cust_pkg)=create FS::cust_pkg(\%hash);
+ my $cust_pkg_mod_flag = 0;
+ my %hash = $cust_pkg->hash;
+ my $old_cust_pkg = new FS::cust_pkg \%hash;
# bill setup
- my($setup)=0;
+ my $setup = 0;
unless ( $cust_pkg->setup ) {
- my($setup_prog)=$part_pkg->getfield('setup');
- my($cpt) = new Safe;
+ my $setup_prog = $part_pkg->getfield('setup');
+ my $cpt = new Safe;
#$cpt->permit(); #what is necessary?
- $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods?
+ $cpt->share(qw( $cust_pkg )); #can $cpt now use $cust_pkg methods?
$setup = $cpt->reval($setup_prog);
unless ( defined($setup) ) {
warn "Error reval-ing part_pkg->setup pkgpart ",
}
#bill recurring fee
- my($recur)=0;
- my($sdate);
+ my $recur = 0;
+ my $sdate;
if ( $part_pkg->getfield('freq') > 0 &&
! $cust_pkg->getfield('susp') &&
( $cust_pkg->getfield('bill') || 0 ) < $time
) {
- my($recur_prog)=$part_pkg->getfield('recur');
- my($cpt) = new Safe;
+ my $recur_prog = $part_pkg->getfield('recur');
+ my $cpt = new Safe;
#$cpt->permit(); #what is necessary?
- $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods?
+ $cpt->share(qw( $cust_pkg )); #can $cpt now use $cust_pkg methods?
$recur = $cpt->reval($recur_prog);
unless ( defined($recur) ) {
warn "Error reval-ing part_pkg->recur pkgpart ",
#change this bit to use Date::Manip?
#$sdate=$cust_pkg->bill || time;
#$sdate=$cust_pkg->bill || $time;
- $sdate=$cust_pkg->bill || $cust_pkg->setup || $time;
- my($sec,$min,$hour,$mday,$mon,$year)=
+ $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
+ my ($sec,$min,$hour,$mday,$mon,$year) =
(localtime($sdate) )[0,1,2,3,4,5];
$mon += $part_pkg->getfield('freq');
until ( $mon < 12 ) { $mon -= 12; $year++; }
- $cust_pkg->setfield('bill',timelocal($sec,$min,$hour,$mday,$mon,$year));
- $cust_pkg_mod_flag=1;
+ $cust_pkg->setfield('bill',
+ timelocal($sec,$min,$hour,$mday,$mon,$year));
+ $cust_pkg_mod_flag = 1;
}
}
warn "recur is undefinded" unless defined($recur);
warn "cust_pkg bill is undefinded" unless defined($cust_pkg->bill);
- if ($cust_pkg_mod_flag) {
+ if ( $cust_pkg_mod_flag ) {
$error=$cust_pkg->replace($old_cust_pkg);
- if ( $error ) {
+ if ( $error ) { #just in case
warn "Error modifying pkgnum ", $cust_pkg->pkgnum, ": $error";
} else {
- #just in case
- $setup=sprintf("%.2f",$setup);
- $recur=sprintf("%.2f",$recur);
- my($cust_bill_pkg)=create FS::cust_bill_pkg ({
+ $setup = sprintf( "%.2f", $setup );
+ $recur = sprintf( "%.2f", $recur );
+ my $cust_bill_pkg = new FS::cust_bill_pkg ({
'pkgnum' => $cust_pkg->pkgnum,
'setup' => $setup,
'recur' => $recur,
}
- my($charged)=sprintf("%.2f",$total_setup + $total_recur);
+ my $charged = sprintf( "%.2f", $total_setup + $total_recur );
return '' if scalar(@cust_bill_pkg) == 0;
- unless ( $self->getfield('tax') eq 'Y' ||
- $self->getfield('tax') eq 'y' ||
- $self->getfield('payby') eq 'COMP'
+ unless ( $self->getfield('tax') =~ /Y/i
+ || $self->getfield('payby') eq 'COMP'
) {
- my($cust_main_county) = qsearchs('cust_main_county',{
- 'county' => $self->getfield('county'),
- 'state' => $self->getfield('state'),
+ my $cust_main_county = qsearchs('cust_main_county',{
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
} );
- my($tax) = sprintf("%.2f",
+ my $tax = sprintf( "%.2f",
$charged * ( $cust_main_county->getfield('tax') / 100 )
);
- $charged = sprintf("%.2f",$charged+$tax);
+ $charged = sprintf( "%.2f", $charged+$tax );
- my($cust_bill_pkg)=create FS::cust_bill_pkg ({
+ my $cust_bill_pkg = new FS::cust_bill_pkg ({
'pkgnum' => 0,
'setup' => $tax,
'recur' => 0,
push @cust_bill_pkg, $cust_bill_pkg;
}
- my($cust_bill) = create FS::cust_bill ( {
+ my $cust_bill = new FS::cust_bill ( {
'custnum' => $self->getfield('custnum'),
'_date' => $time,
'charged' => $charged,
} );
- $error=$cust_bill->insert;
+ $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;
- my($invnum)=$cust_bill->invnum;
- my($cust_bill_pkg);
+ my $invnum = $cust_bill->invnum;
+ my $cust_bill_pkg;
foreach $cust_bill_pkg ( @cust_bill_pkg ) {
- $cust_bill_pkg->setfield('invnum',$invnum);
- $error=$cust_bill_pkg->insert;
+ $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"
=cut
sub collect {
- my($self,%options)=@_;
- my($invoice_time) = $options{'invoice_time'} || $^T;
+ my( $self, %options ) = @_;
+ my $invoice_time = $options{'invoice_time'} || time;
- my($total_owed) = $self->balance;
+ my $total_owed = $self->balance;
return '' unless $total_owed > 0; #redundant?????
#put below somehow?
local $SIG{QUIT} = 'IGNORE';
local $SIG{TERM} = 'IGNORE';
local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
- foreach my $cust_bill ( qsearch('cust_bill', {
- 'custnum' => $self->getfield('custnum'),
- } ) ) {
-
- bless($cust_bill,"FS::cust_bill");
+ foreach my $cust_bill (
+ qsearch('cust_bill', { 'custnum' => $self->custnum, } )
+ ) {
#this has to be before next's
- my($amount) = sprintf("%.2f", $total_owed < $cust_bill->owed
+ my $amount = sprintf( "%.2f", $total_owed < $cust_bill->owed
? $total_owed
: $cust_bill->owed
);
- $total_owed = sprintf("%.2f",$total_owed-$amount);
+ $total_owed = sprintf( "%.2f", $total_owed - $amount );
next unless $cust_bill->owed > 0;
- next if qsearchs('cust_pay_batch',{'invnum'=> $cust_bill->invnum });
+ next if qsearchs( 'cust_pay_batch', { 'invnum' => $cust_bill->invnum } );
#warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, total_owed $total_owed)";
next unless $amount > 0;
- if ( $self->getfield('payby') eq 'BILL' ) {
+ if ( $self->payby eq 'BILL' ) {
#30 days 2592000
- my($since)=$invoice_time - ( $cust_bill->_date || 0 );
+ my $since = $invoice_time - ( $cust_bill->_date || 0 );
#warn "$invoice_time ", $cust_bill->_date, " $since";
if ( $since >= 0 #don't print future invoices
&& ( $cust_bill->printed * 2592000 ) <= $since
) {
- open(LPR,"|$lpr") or die "Can't open $lpr: $!";
- print LPR $cust_bill->print_text; #( date )
- close LPR
- or die $! ? "Error closing $lpr: $!"
- : "Exit status $? from $lpr";
+ #my @print_text = $cust_bill->print_text; #( date )
+ my @invoicing_list = $self->invoicing_list;
+ if ( grep { $_ ne 'POST' } @invoicing_list ) { #email invoice
+ $ENV{SMTPHOSTS} = $smtpmachine;
+ $ENV{MAILADDRESS} = $invoice_from;
+ my $header = new Mail::Header ( [
+ "From: $invoice_from",
+ "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
+ "Sender: $invoice_from",
+ "Reply-To: $invoice_from",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: Invoice",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ $cust_bill->print_text ], #( date)
+ );
+ $message->smtpsend or die "Can't send invoice email!"; #die? warn?
+
+ } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) {
+ open(LPR, "|$lpr") or die "Can't open pipe to $lpr: $!";
+ print LPR $cust_bill->print_text; #( date )
+ close LPR
+ or die $! ? "Error closing $lpr: $!"
+ : "Exit status $? from $lpr";
+ }
- my(%hash)=$cust_bill->hash;
+ my %hash = $cust_bill->hash;
$hash{'printed'}++;
- my($new_cust_bill)=create FS::cust_bill(\%hash);
- my($error)=$new_cust_bill->replace($cust_bill);
- if ( $error ) {
- warn "Error updating $cust_bill->printed: $error";
- }
+ my $new_cust_bill = new FS::cust_bill(\%hash);
+ my $error = $new_cust_bill->replace($cust_bill);
+ warn "Error updating $cust_bill->printed: $error" if $error;
}
- } elsif ( $self->getfield('payby') eq 'COMP' ) {
- my($cust_pay) = create FS::cust_pay ( {
- 'invnum' => $cust_bill->getfield('invnum'),
+ } elsif ( $self->payby eq 'COMP' ) {
+ my $cust_pay = new FS::cust_pay ( {
+ 'invnum' => $cust_bill->invnum,
'paid' => $amount,
'_date' => '',
'payby' => 'COMP',
- 'payinfo' => $self->getfield('payinfo'),
+ 'payinfo' => $self->payinfo,
'paybatch' => ''
} );
- my($error)=$cust_pay->insert;
- return 'Error COMPing invnum #' . $cust_bill->getfield('invnum') .
+ my $error = $cust_pay->insert;
+ return 'Error COMPing invnum #' . $cust_bill->invnum .
':' . $error if $error;
- } elsif ( $self->getfield('payby') eq 'CARD' ) {
+
+ } elsif ( $self->payby eq 'CARD' ) {
if ( $options{'batch_card'} ne 'yes' ) {
return "Real time card processing not enabled!" unless $processor;
- if ( $processor =~ /cybercash/ ) {
+ if ( $processor =~ /^cybercash/ ) {
#fix exp. date for cybercash
- $self->getfield('paydate') =~ /^(\d+)\/\d*(\d{2})$/;
- my($exp)="$1/$2";
+ #$self->paydate =~ /^(\d+)\/\d*(\d{2})$/;
+ $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ my $exp = "$2/$1";
- my($paybatch)= $cust_bill->getfield('invnum') .
- '-' . time2str("%y%m%d%H%M%S",time);
+ my $paybatch = $cust_bill->invnum.
+ '-' . time2str("%y%m%d%H%M%S", time);
- my($payname)= $self->getfield('payname') ||
- $self->getfield('first') . ' ' .$self->getfield('last');
+ my $payname = $self->payname ||
+ $self->getfield('first'). ' '. $self->getfield('last');
- my($address)= $self->getfield('address1');
- $address .= ", " . $self->getfield('address2')
- if $self->getfield('address2');
+ my $address = $self->address1;
+ $address .= ", ". $self->address2 if $self->address2;
- my($country) = $self->getfield('country') eq 'US' ?
- 'USA' : $self->getfield('country');
+ my $country = 'USA' if $self->country eq 'US';
- my(@full_xaction)=($xaction,
+ my @full_xaction = ( $xaction,
'Order-ID' => $paybatch,
'Amount' => "usd $amount",
'Card-Number' => $self->getfield('payinfo'),
'Card-Exp' => $exp,
);
- my(%result);
+ my %result;
if ( $processor eq 'cybercash2' ) {
$^W=0; #CCLib isn't -w safe, ugh!
%result = &CCLib::sendmserver(@full_xaction);
#if ( $result{'MActionCode'} == 7 ) { #cybercash smps v.1.1.3
#if ( $result{'action-code'} == 7 ) { #cybercash smps v.2.1
if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3
- my($cust_pay) = create FS::cust_pay ( {
- 'invnum' => $cust_bill->getfield('invnum'),
+ my $cust_pay = new FS::cust_pay ( {
+ 'invnum' => $cust_bill->invnum,
'paid' => $amount,
'_date' => '',
'payby' => 'CARD',
- 'payinfo' => $self->getfield('payinfo'),
+ 'payinfo' => $self->payinfo,
'paybatch' => "$processor:$paybatch",
} );
- my($error)=$cust_pay->insert;
+ my $error = $cust_pay->insert;
return 'Error applying payment, invnum #' .
- $cust_bill->getfield('invnum') . ':' . $error if $error;
+ $cust_bill->invnum. ':'. $error if $error;
} elsif ( $result{'Mstatus'} ne 'failure-bad-money'
|| $options{'report_badcard'} ) {
return 'Cybercash error, invnum #' .
- $cust_bill->getfield('invnum') . ':' . $result{'MErrMsg'};
+ $cust_bill->invnum. ':'. $result{'MErrMsg'};
} else {
return '';
}
} else { #batch card
-# my($cust_pay_batch) = create FS::cust_pay_batch ( {
- my($cust_pay_batch) = new FS::Record ('cust_pay_batch', {
+ my $cust_pay_batch = new FS::Record ('cust_pay_batch', {
'invnum' => $cust_bill->getfield('invnum'),
'custnum' => $self->getfield('custnum'),
'last' => $self->getfield('last'),
'payname' => $self->getfield('payname'),
'amount' => $amount,
} );
-# my($error)=$cust_pay_batch->insert;
- my($error)=$cust_pay_batch->add;
+ my $error = $cust_pay_batch->insert;
return "Error adding to cust_pay_batch: $error" if $error;
}
} else {
- return "Unknown payment type ".$self->getfield('payby');
+ return "Unknown payment type ". $self->payby;
}
+
+
+
+
}
'';
=cut
sub total_owed {
- my($self) = @_;
- my($total_bill) = 0;
- my($cust_bill);
- foreach $cust_bill ( qsearch('cust_bill', {
- 'custnum' => $self->getfield('custnum'),
+ my $self = shift;
+ my $total_bill = 0;
+ foreach my $cust_bill ( qsearch('cust_bill', {
+ 'custnum' => $self->custnum,
} ) ) {
- $total_bill += $cust_bill->getfield('owed');
+ $total_bill += $cust_bill->owed;
}
- sprintf("%.2f",$total_bill);
+ sprintf( "%.2f", $total_bill );
}
=item total_credited
=cut
sub total_credited {
- my($self) = @_;
- my($total_credit) = 0;
- my($cust_credit);
- foreach $cust_credit ( qsearch('cust_credit', {
- 'custnum' => $self->getfield('custnum'),
+ my $self = shift;
+ my $total_credit = 0;
+ foreach my $cust_credit ( qsearch('cust_credit', {
+ 'custnum' => $self->custnum,
} ) ) {
- $total_credit += $cust_credit->getfield('credited');
+ $total_credit += $cust_credit->credited;
}
- sprintf("%.2f",$total_credit);
+ sprintf( "%.2f", $total_credit );
}
=item balance
=cut
sub balance {
- my($self) = @_;
- sprintf("%.2f",$self->total_owed - $self->total_credited);
+ my $self = shift;
+ sprintf( "%.2f", $self->total_owed - $self->total_credited );
+}
+
+=item invoicing_list [ ARRAYREF ]
+
+If an arguement is given, sets these email addresses as invoice recipients
+(see L<FS::cust_main_invoice>). Errors are not fatal and are not reported
+(except as warnings), so use check_invoicing_list first.
+
+Returns a list of email addresses (with svcnum entries expanded).
+
+Note: You can clear the invoicing list by passing an empty ARRAYREF. You can
+check it without disturbing anything by passing nothing.
+
+This interface may change in the future.
+
+=cut
+
+sub invoicing_list {
+ my( $self, $arrayref ) = @_;
+ if ( $arrayref ) {
+ my @cust_main_invoice;
+ if ( $self->custnum ) {
+ @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ @cust_main_invoice = ();
+ }
+ foreach my $cust_main_invoice ( @cust_main_invoice ) {
+ #warn $cust_main_invoice->destnum;
+ unless ( grep { $cust_main_invoice->address eq $_ } @{$arrayref} ) {
+ #warn $cust_main_invoice->destnum;
+ my $error = $cust_main_invoice->delete;
+ warn $error if $error;
+ }
+ }
+ if ( $self->custnum ) {
+ @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ @cust_main_invoice = ();
+ }
+ foreach my $address ( @{$arrayref} ) {
+ unless ( grep { $address eq $_->address } @cust_main_invoice ) {
+ my $cust_main_invoice = new FS::cust_main_invoice ( {
+ 'custnum' => $self->custnum,
+ 'dest' => $address,
+ } );
+ my $error = $cust_main_invoice->insert;
+ warn $error if $error;
+ }
+ }
+ }
+ if ( $self->custnum ) {
+ map { $_->address }
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ } else {
+ ();
+ }
+}
+
+=item check_invoicing_list ARRAYREF
+
+Checks these arguements as valid input for the invoicing_list method. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub check_invoicing_list {
+ my( $self, $arrayref ) = @_;
+ foreach my $address ( @{$arrayref} ) {
+ my $cust_main_invoice = new FS::cust_main_invoice ( {
+ 'custnum' => $self->custnum,
+ 'dest' => $address,
+ } );
+ my $error = $self->custnum
+ ? $cust_main_invoice->check
+ : $cust_main_invoice->checkdest
+ ;
+ return $error if $error;
+ }
+ '';
}
=back
-=head1 BUGS
+=head1 VERSION
-The delete method.
+$Id: cust_main.pm,v 1.20 1999-04-10 08:35:14 ivan Exp $
-It doesn't properly override FS::Record yet.
+=head1 BUGS
-hfields should be removed.
+The delete method.
Bill and collect options should probably be passed as references instead of a
list.
CyberCash v2 forces us to define some variables in package main.
+There should probably be a configuration file with a list of allowed credit
+card types.
+
+CyberCash is the only processor.
+
+No multiple currency support (probably a larger project than just this module).
+
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_credit>
L<FS::cust_pay_batch>, L<FS::agent>, L<FS::part_referral>,
-L<FS::cust_main_county>, L<FS::UID>, schema.html from the base documentation.
+L<FS::cust_main_county>, L<FS::cust_main_invoice>,
+L<FS::UID>, schema.html from the base documentation.
=head1 HISTORY
FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21
$Log: cust_main.pm,v $
-Revision 1.2 1998-11-07 10:24:25 ivan
+Revision 1.20 1999-04-10 08:35:14 ivan
+say what the unknown state/county/country are!
+
+Revision 1.19 1999/04/10 07:38:06 ivan
+_all_ check stuff with illegal data return the bad data too, to help debugging
+
+Revision 1.18 1999/04/10 06:54:11 ivan
+ditto
+
+Revision 1.17 1999/04/10 05:27:38 ivan
+display an illegal payby, to assist importing
+
+Revision 1.16 1999/04/07 14:32:19 ivan
+more &invoicing_list logic to skip searches when there is no custnum
+
+Revision 1.15 1999/04/07 13:41:54 ivan
+in &invoicing_list, don't search if there's no custnum yet
+
+Revision 1.14 1999/03/29 12:06:15 ivan
+buglet in email invoices fixed
+
+Revision 1.13 1999/02/28 20:09:03 ivan
+allow spaces in zip codes, for (at least) canada. pointed out by
+Clayton Gray <clgray@bcgroup.net>
+
+Revision 1.12 1999/02/27 21:24:22 ivan
+parse paydate correctly for cybercash
+
+Revision 1.11 1999/02/23 08:09:27 ivan
+beginnings of one-screen new customer entry and some other miscellania
+
+Revision 1.10 1999/01/25 12:26:09 ivan
+yet more mod_perl stuff
+
+Revision 1.9 1999/01/18 09:22:41 ivan
+changes to track email addresses for email invoicing
+
+Revision 1.8 1998/12/29 11:59:39 ivan
+mostly properly OO, some work still to be done with svc_ stuff
+
+Revision 1.7 1998/12/16 09:58:52 ivan
+library support for editing email invoice destinations (not in sub collect yet)
+
+Revision 1.6 1998/11/18 09:01:42 ivan
+i18n! i18n!
+
+Revision 1.5 1998/11/15 11:23:14 ivan
+use FS::table_name for all searches to eliminate warnings,
+emit state/county when they don't match
+
+Revision 1.4 1998/11/15 05:30:48 ivan
+bugfix for new config layout
+
+Revision 1.3 1998/11/13 09:56:54 ivan
+change configuration file layout to support multiple distinct databases (with
+own set of config files, export, etc.)
+
+Revision 1.2 1998/11/07 10:24:25 ivan
don't use depriciated FS::Bill and FS::Invoice, other miscellania