X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=site_perl%2Fcust_main.pm;h=0777409950bc942f69fa211bb8fed971b1e902e5;hb=05c60524e89b7b0f0f70a531c7b7f56db45a3d9d;hp=83a7d786dcd2da038c85af848acb7f344e3d29c6;hpb=0bf5ad9ff0a65195db88ed0bac3aa11c33ec1ad3;p=freeside.git diff --git a/site_perl/cust_main.pm b/site_perl/cust_main.pm index 83a7d786d..077740995 100644 --- a/site_perl/cust_main.pm +++ b/site_perl/cust_main.pm @@ -5,29 +5,37 @@ use vars qw($paymentserversecret $paymentserverport $paymentserverhost); 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; +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 Exporter); -@EXPORT_OK = qw(hfields); +@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; @@ -70,8 +78,8 @@ FS::cust_main - Object methods for cust_main records 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; @@ -153,7 +161,7 @@ FS::Record. The following fields are currently supported: =over 4 -=item create HASHREF +=item new HASHREF Creates a new customer. To add the customer to the database, see L<"insert">. @@ -162,39 +170,13 @@ points to. You can ask the object for a copy with the I method. =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 @@ -205,12 +187,8 @@ be no record the customer ever existed (which is bad, no?) =cut -# Usage: $error = $record -> delete; sub delete { return "Can't (yet?) delete customers."; -# my($self)=@_; -# -# $self->del; } =item replace OLD_RECORD @@ -218,17 +196,6 @@ sub delete { 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 @@ -238,12 +205,11 @@ and repalce methods. =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') @@ -258,10 +224,10 @@ sub check { 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->setfield('last',$1); @@ -279,20 +245,24 @@ sub check { $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($1); + unless ( qsearchs('cust_main_county', { + 'country' => $self->country, + 'state' => '', + } ) ) { + return "Unknown state/county/country" + #" state ". $self->state. " county ". $self->county. " country ". $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($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($1); @@ -305,22 +275,17 @@ sub check { $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/ ); + 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" 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" if $error; } @@ -362,7 +327,7 @@ Returns all packages (see L) for this customer. =cut sub all_pkgs { - my($self)=@_; + my $self = shift; qsearch( 'cust_pkg', { 'custnum' => $self->custnum }); } @@ -373,7 +338,7 @@ Returns all non-cancelled packages (see L) for this customer. =cut sub ncancelled_pkgs { - my($self)=@_; + my $self = shift; qsearch( 'cust_pkg', { 'custnum' => $self->custnum, 'cancel' => '', @@ -395,10 +360,10 @@ If there is an error, returns the error, otherwise returns false. =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'; @@ -406,42 +371,38 @@ sub bill { 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 ", @@ -453,16 +414,16 @@ sub bill { } #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 ", @@ -471,13 +432,14 @@ sub bill { #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; } } @@ -485,15 +447,14 @@ sub bill { 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, @@ -508,24 +469,24 @@ sub bill { } - 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, @@ -535,23 +496,23 @@ sub bill { 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" @@ -587,10 +548,10 @@ return an error. By default, they don't. =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? @@ -599,91 +560,109 @@ sub collect { 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'), @@ -696,7 +675,7 @@ sub collect { 'Card-Exp' => $exp, ); - my(%result); + my %result; if ( $processor eq 'cybercash2' ) { $^W=0; #CCLib isn't -w safe, ugh! %result = &CCLib::sendmserver(@full_xaction); @@ -710,21 +689,21 @@ sub collect { #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 ''; } @@ -735,8 +714,7 @@ sub collect { } 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'), @@ -753,16 +731,19 @@ sub collect { '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; } + + + + } ''; @@ -776,15 +757,14 @@ Returns the total owed for this customer on all invoices =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 @@ -794,15 +774,14 @@ Returns the total credits (see L) for this customer. =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 @@ -812,30 +791,110 @@ Returns the balance for this customer (total owed minus total credited). =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). 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 = + qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } ); + 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; + } + } + @cust_main_invoice = + qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } ); + 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.15 1999-04-07 13:41:54 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, L, L, L L, L, L, -L, L, schema.html from the base documentation. +L, L, +L, schema.html from the base documentation. =head1 HISTORY @@ -868,7 +927,45 @@ enable cybercash, cybercash v3 support, don't need to import FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21 $Log: cust_main.pm,v $ -Revision 1.3 1998-11-13 09:56:54 ivan +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 + +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.)