package FS::cust_bill;
use strict;
-use vars qw(@ISA $conf $add1 $add2 $add3 $add4);
-use Exporter;
+use vars qw( @ISA $conf $add1 $add2 $add3 $add4 );
use Date::Format;
-use FS::Record qw(fields qsearch qsearchs);
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_main;
+use FS::cust_bill_pkg;
+use FS::cust_credit;
+use FS::cust_pay;
+use FS::cust_pkg;
-@ISA = qw(FS::Record Exporter);
+@ISA = qw( FS::Record );
-$conf = new FS::Conf;
-
-($add1,$add2,$add3,$add4) = $conf->config('address');
+#ask FS::UID to run this stuff for us later
+$FS::UID::callback{'FS::cust_bill'} = sub {
+ $conf = new FS::Conf;
+ ( $add1, $add2, $add3, $add4 ) = ( $conf->config('address'), '', '', '', '' );
+};
=head1 NAME
use FS::cust_bill;
- $record = create FS::cust_bill \%hash;
- $record = create FS::cust_bill { 'column' => 'value' };
+ $record = new FS::cust_bill \%hash;
+ $record = new FS::cust_bill { 'column' => 'value' };
$error = $record->insert;
=over 4
-=item create HASHREF
+=item new HASHREF
Creates a new invoice. To add the invoice to the database, see L<"insert">.
Invoices are normally created by calling the bill method of a customer object
=cut
-sub create {
- my($proto,$hashref)=@_;
-
- #now in FS::Record::new
- #my($field);
- #foreach $field (fields('cust_bill')) {
- # $hashref->{$field}='' unless defined $hashref->{$field};
- #}
-
- $proto->new('cust_bill',$hashref);
-}
+sub table { 'cust_bill'; }
=item insert
=cut
sub insert {
- my($self)=@_;
+ my $self = shift;
- $self->setfield('owed',$self->charged) if $self->owed eq '';
+ $self->owed( $self->charged ) if $self->owed eq '';
return "owed != charged!"
unless $self->owed == $self->charged;
- $self->check or
- $self->add;
+ $self->SUPER::insert;
}
=item delete
sub delete {
return "Can't remove invoice!"
- #my($self)=@_;
- #$self->del;
}
=item replace OLD_RECORD
=cut
sub replace {
- my($new,$old)=@_;
- return "(Old) Not a cust_bill record!" unless $old->table eq "cust_bill";
- return "Can't change invnum!"
- unless $old->getfield('invnum') eq $new->getfield('invnum');
- return "Can't change custnum!"
- unless $old->getfield('custnum') eq $new->getfield('custnum');
- return "Can't change _date!"
- unless $old->getfield('_date') eq $new->getfield('_date');
- return "Can't change charged!"
- unless $old->getfield('charged') eq $new->getfield('charged');
- return "(New) owed can't be > (new) charged!"
- if $new->getfield('owed') > $new->getfield('charged');
-
- $new->check or
- $new->rep($old);
+ my( $new, $old ) = ( shift, shift );
+ return "Can't change custnum!" unless $old->custnum == $new->custnum;
+ #return "Can't change _date!" unless $old->_date eq $new->_date;
+ return "Can't change _date!" unless $old->_date == $new->_date;
+ return "Can't change charged!" unless $old->charged == $new->charged;
+ return "(New) owed can't be > (new) charged!" if $new->owed > $new->charged;
+
+ $new->SUPER::replace($old);
}
=item check
=cut
sub check {
- my($self)=@_;
- return "Not a cust_bill record!" unless $self->table eq "cust_bill";
- my($recref) = $self->hashref;
-
- $recref->{invnum} =~ /^(\d*)$/ or return "Illegal invnum";
- $recref->{invnum} = $1;
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('invnum')
+ || $self->ut_number('custnum')
+ || $self->ut_numbern('_date')
+ || $self->ut_money('charged')
+ || $self->ut_money('owed')
+ || $self->ut_numbern('printed')
+ ;
+ return $error if $error;
- $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum";
- $recref->{custnum} = $1;
return "Unknown customer"
- unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}});
-
- $recref->{_date} =~ /^(\d*)$/ or return "Illegal date";
- $recref->{_date} = $recref->{_date} ? $1 : time;
-
- #$recref->{charged} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal charged";
- $recref->{charged} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal charged";
- $recref->{charged} = $1;
+ unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
- $recref->{owed} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal owed";
- $recref->{owed} = $1;
+ $self->_date(time) unless $self->_date;
- $recref->{printed} =~ /^(\d*)$/ or return "Illegal printed";
- $recref->{printed} = $1 || '0';
+ $self->printed(0) if $self->printed eq '';
''; #no error
}
=cut
sub previous {
- my($self)=@_;
- my($total)=0;
- my(@cust_bill) = sort { $a->_date <=> $b->_date }
+ my $self = shift;
+ my $total = 0;
+ my @cust_bill = sort { $a->_date <=> $b->_date }
grep { $_->owed != 0 && $_->_date < $self->_date }
- qsearch('cust_bill',{ 'custnum' => $self->custnum } )
+ qsearch( 'cust_bill', { 'custnum' => $self->custnum } )
;
- foreach (@cust_bill) { $total += $_->owed; }
+ foreach ( @cust_bill ) { $total += $_->owed; }
$total, @cust_bill;
}
=cut
sub cust_bill_pkg {
- my($self)=@_;
+ my $self = shift;
qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
}
=cut
sub cust_credit {
- my($self)=@_;
- my($total)=0;
- my(@cust_credit) = sort { $a->_date <=> $b->date }
+ my $self = shift;
+ my $total = 0;
+ my @cust_credit = sort { $a->_date <=> $b->date }
grep { $_->credited != 0 && $_->_date < $self->_date }
qsearch('cust_credit', { 'custnum' => $self->custnum } )
;
=cut
sub cust_pay {
- my($self)=@_;
+ my $self = shift;
sort { $a->_date <=> $b->date }
qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
;
sub print_text {
- my($self,$today)=@_;
+ my( $self, $today ) = ( shift, shift );
$today ||= time;
- my($invnum)=$self->invnum;
- my($cust_main) = qsearchs('cust_main',
- { 'custnum', $self->custnum } );
- $cust_main->setfield('payname',
- $cust_main->first. ' '. $cust_main->getfield('last')
- ) unless $cust_main->payname;
+ my $invnum = $self->invnum;
+ my $cust_main = qsearchs('cust_main', { 'custnum', $self->custnum } );
+ $cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
+ unless $cust_main->payname;
- my($pr_total,@pr_cust_bill) = $self->previous; #previous balance
- my($cr_total,@cr_cust_credit) = $self->cust_credit; #credits
- my($balance_due) = $self->owed + $pr_total - $cr_total;
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+ my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
+ my $balance_due = $self->owed + $pr_total - $cr_total;
#overdue?
- my($overdue) = (
+ my $overdue = (
$balance_due > 0
&& $today > $self->_date
&& $self->printed > 1
);
- #printing bits here
-
- local($SIG{CHLD}) = sub { wait() };
- $|=1;
- my($pid)=open(CHILD,"-|");
- die "Can't fork: $!" unless defined($pid);
-
- if ($pid) { #parent
- my(@collect)=<CHILD>;
- close CHILD;
- return @collect;
- } else { #child
-
- my($description,$amount);
- my(@buf);
-
- #define format stuff
- $%=0;
- $= = 35;
- local($^L) = <<END;
-
-
-
-
+ #printing bits here (yuck!)
+ my @collect = ();
+ my($description,$amount);
+ my(@buf);
-END
-
- #format address
- my($l,@address)=(0,'','','','','');
- $address[$l++]=$cust_main->company if $cust_main->company;
- $address[$l++]=$cust_main->address1;
- $address[$l++]=$cust_main->address2 if $cust_main->address2;
- $address[$l++]=$cust_main->city. ", ". $cust_main->state. " ".
- $cust_main->zip;
- $address[$l++]=$cust_main->country unless $cust_main->country eq 'US';
-
- #previous balance
- foreach ( @pr_cust_bill ) {
- push @buf, (
- "Previous Balance, Invoice #". $_->invnum.
- " (". time2str("%x",$_->_date). ")",
- '$'. sprintf("%10.2f",$_->owed)
- );
- }
- if (@pr_cust_bill) {
- push @buf,('','-----------');
- push @buf,('Total Previous Balance','$' . sprintf("%10.2f",$pr_total ) );
- push @buf,('','');
- }
-
- #new charges
- foreach ( $self->cust_bill_pkg ) {
-
- if ( $_->pkgnum ) {
-
- my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } );
- my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart});
- my($pkg)=$part_pkg->pkg;
-
- push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) )
- if $_->setup != 0;
- push @buf, (
- "$pkg (" . time2str("%x",$_->sdate) . " - " .
- time2str("%x",$_->edate) . ")",
- '$' . sprintf("%10.2f",$_->recur)
- ) if $_->recur != 0;
-
- } else { #pkgnum Tax
- push @buf,("Tax",'$' . sprintf("%10.2f",$_->setup) )
- if $_->setup != 0;
- }
- }
-
- push @buf,('','-----------');
- push @buf,('Total New Charges',
- '$' . sprintf("%10.2f",$self->charged) );
- push @buf,('','');
-
+ #format address
+ my($l,@address)=(0,'','','','','','','');
+ $address[$l++] =
+ $cust_main->payname.
+ ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo
+ ? " (P.O. #". $cust_main->payinfo. ")"
+ : ''
+ )
+ ;
+ $address[$l++]=$cust_main->company if $cust_main->company;
+ $address[$l++]=$cust_main->address1;
+ $address[$l++]=$cust_main->address2 if $cust_main->address2;
+ $address[$l++]=$cust_main->city. ", ". $cust_main->state. " ".
+ $cust_main->zip;
+ $address[$l++]=$cust_main->country unless $cust_main->country eq 'US';
+
+ #previous balance
+ foreach ( @pr_cust_bill ) {
+ push @buf, (
+ "Previous Balance, Invoice #". $_->invnum.
+ " (". time2str("%x",$_->_date). ")",
+ '$'. sprintf("%10.2f",$_->owed)
+ );
+ }
+ if (@pr_cust_bill) {
push @buf,('','-----------');
- push @buf,('Total Charges',
- '$' . sprintf("%10.2f",$self->charged + $pr_total) );
+ push @buf,('Total Previous Balance','$' . sprintf("%10.2f",$pr_total ) );
push @buf,('','');
+ }
- #credits
- foreach ( @cr_cust_credit ) {
- push @buf,(
- "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
- '$' . sprintf("%10.2f",$_->credited)
- );
- }
-
- #get & print payments
- foreach ( $self->cust_pay ) {
- push @buf,(
- "Payment received ". time2str("%x",$_->_date ),
- '$' . sprintf("%10.2f",$_->paid )
- );
- }
-
- #balance due
- push @buf,('','-----------');
- push @buf,('Balance Due','$' .
- sprintf("%10.2f",$self->owed + $pr_total - $cr_total ) );
+ #new charges
+ foreach ( $self->cust_bill_pkg ) {
- #now print
+ if ( $_->pkgnum ) {
- my($tot_pages)=int(scalar(@buf)/30); #15 lines, 2 values per line
- $tot_pages++ if scalar(@buf) % 30;
+ my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } );
+ my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart});
+ my($pkg)=$part_pkg->pkg;
- while (@buf) {
+ push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) )
+ if $_->setup != 0;
+ push @buf, (
+ "$pkg (" . time2str("%x",$_->sdate) . " - " .
+ time2str("%x",$_->edate) . ")",
+ '$' . sprintf("%10.2f",$_->recur)
+ ) if $_->recur != 0;
+
+ } else { #pkgnum Tax
+ push @buf,("Tax",'$' . sprintf("%10.2f",$_->setup) )
+ if $_->setup != 0;
+ }
+ }
+
+ push @buf,('','-----------');
+ push @buf,('Total New Charges',
+ '$' . sprintf("%10.2f",$self->charged) );
+ push @buf,('','');
+
+ push @buf,('','-----------');
+ push @buf,('Total Charges',
+ '$' . sprintf("%10.2f",$self->charged + $pr_total) );
+ push @buf,('','');
+
+ #credits
+ foreach ( @cr_cust_credit ) {
+ push @buf,(
+ "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
+ '$' . sprintf("%10.2f",$_->credited)
+ );
+ }
+
+ #get & print payments
+ foreach ( $self->cust_pay ) {
+ push @buf,(
+ "Payment received ". time2str("%x",$_->_date ),
+ '$' . sprintf("%10.2f",$_->paid )
+ );
+ }
+
+ #balance due
+ push @buf,('','-----------');
+ push @buf,('Balance Due','$' .
+ sprintf("%10.2f",$self->owed + $pr_total - $cr_total ) );
+
+ #now print
+
+ my $tot_lines = 50; #should be configurable
+ #header is 17 lines
+ my $tot_pages = int( scalar(@buf) / ( 2 * ( $tot_lines - 17 ) ) );
+ $tot_pages++ if scalar(@buf) % ( 2 * ( $tot_lines - 17 ) );
+
+ my $page = 1;
+ my $lines;
+ while (@buf) {
+ $lines = $tot_lines;
+ my @header = &header(
+ $page, $tot_pages, $self->_date, $self->invnum, @address
+ );
+ push @collect, @header;
+ $lines -= scalar(@header);
+
+ while ( $lines-- && @buf ) {
$description=shift(@buf);
$amount=shift(@buf);
- write;
+ push @collect, myswrite($description, $amount);
}
- ($description,$amount)=('','');
- write while ( $- );
- print $^L;
-
- exit; #kid
-
- format STDOUT_TOP =
-
- @|||||||||||||||||||
- "Invoice"
- @||||||||||||||||||| @<<<<<<< @<<<<<<<<<<<
-{
- ( $tot_pages != 1 ) ? "Page $% of $tot_pages" : '',
- time2str("%x",( $self->_date )), "FS-$invnum"
-}
-
-
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add1
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add2
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add3
-@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$add4
-
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-{ $cust_main->payname,
- ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo )
- ? "P.O. #". $cust_main->payinfo : ''
-}
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[0],''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[1],$overdue ? "* This invoice is now PAST DUE! *" : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[2],$overdue ? " Please forward payment promptly " : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[3],$overdue ? "to avoid interruption of service." : ''
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-$address[4],''
-
-
-
-.
-
- format STDOUT =
+ $page++;
+ }
+ while ( $lines-- ) {
+ push @collect, myswrite('', '');
+ }
+
+ return @collect;
+
+ sub header { #17 lines
+ my ( $page, $tot_pages, $date, $invnum, @address ) = @_ ;
+ push @address, '', '', '', '';
+
+ my @return = ();
+ my $i = ' 'x32;
+ push @return,
+ '',
+ $i. 'Invoice',
+ $i. substr("Page $page of $tot_pages".' 'x10, 0, 20).
+ time2str("%x", $date ). " FS-". $invnum,
+ '',
+ '',
+ $add1,
+ $add2,
+ $add3,
+ $add4,
+ '',
+ splice @address, 0, 7;
+ ;
+ return map $_. "\n", @return;
+ }
+
+ sub myswrite {
+ my $format = <<END;
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<
- $description,$amount
-.
-
- } #endchild
+END
+ $^A = '';
+ formline( $format, @_ );
+ return $^A;
+ }
}
=back
+=head1 VERSION
+
+$Id: cust_bill.pm,v 1.6 1999-01-25 12:26:07 ivan Exp $
+
=head1 BUGS
The delete method.
-It doesn't properly override FS::Record yet.
-
-print_text formatting (and some logic :/) is in source as a format declaration,
-which needs to be slurped in from a file. the fork is rather kludgy as well.
-It could be cleaned with swrite from man perlform, and the picture could be
-put in a /var/spool/freeside/conf file. Also number of lines ($=).
+print_text formatting (and some logic :/) is in source, but needs to be
+slurped in from a file. Also number of lines ($=).
missing print_ps for a nice postscript copy (maybe HylaFAX-cover-page-style
or something similar so the look can be completely customized?)
-There is an off-by-one error in print_text which causes a visual error: "Page 1
-of 2" printed on some single-page invoices?
-
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_main>, L<FS::cust_pay>, L<FS::cust_bill_pkg>,
pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20
$Log: cust_bill.pm,v $
-Revision 1.2 1998-11-07 10:24:24 ivan
+Revision 1.6 1999-01-25 12:26:07 ivan
+yet more mod_perl stuff
+
+Revision 1.5 1999/01/18 21:58:03 ivan
+esthetic: eq and ne were used in a few places instead of == and !=
+
+Revision 1.4 1998/12/29 11:59:36 ivan
+mostly properly OO, some work still to be done with svc_ stuff
+
+Revision 1.3 1998/11/13 09:56:53 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:24 ivan
don't use depriciated FS::Bill and FS::Invoice, other miscellania