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);
use Safe;
-use Exporter;
use Carp;
use Time::Local;
use Date::Format;
use Date::Manip;
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::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 {
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')
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);
$self->ss("$1-$2-$3");
}
- return "Unknown state/county/country, state ".
- "\"". $self->state. "\" county \"". $self->county. "\""
- 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 =~ /^([\w\-]{10})$/ 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);
$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;
}
=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'} || $^T;
- my($error);
+ my $error;
#put below somehow?
local $SIG{HUP} = '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( $total_setup, $total_recur ) = ( 0, 0 );
+ my @cust_bill_pkg;
- 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'} || $^T;
- my($total_owed) = $self->balance;
+ my $total_owed = $self->balance;
return '' unless $total_owed > 0; #redundant?????
#put below somehow?
local $SIG{TERM} = 'IGNORE';
local $SIG{TSTP} = '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: $!";
+ 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})$/;
+ my $exp = "$1/$2";
- 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 [ ITEM, ITEM, ... ]
+
+If arguements are 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).
+
+=cut
+
+sub invoicing_list {
+ my( $self, @addresses ) = @_;
+ if ( @addresses ) {
+ my @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ foreach my $cust_main_invoice ( @cust_main_invoice ) {
+ unless ( grep { $cust_main_invoice->address eq $_ } @addresses ) {
+ $cust_main_invoice->delete;
+ }
+ }
+ @cust_main_invoice =
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+ foreach my $address ( @addresses ) {
+ 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;
+ }
+ }
+ }
+ map { $_->address }
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+}
+
+=item check_invoicing_list ITEM, ITEM
+
+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, @addresses ) = @_;
+ foreach my $address ( @addresses ) {
+ my $cust_main_invoice = new FS::cust_main_invoice (
+ 'custnum' => $self->custnum,
+ 'dest' => $address,
+ );
+ my $error = $cust_main_invoice->check;
+ return $error if $error;
+ }
+ '';
}
=back
-=head1 BUGS
+=head1 VERSION
-The delete method.
+$Id: cust_main.pm,v 1.8 1998-12-29 11:59:39 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.5 1998-11-15 11:23:14 ivan
+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