*** empty log message ***
[freeside.git] / site_perl / cust_bill.pm
index 0023451..0e87755 100644 (file)
@@ -1,16 +1,22 @@
 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
 
@@ -20,8 +26,8 @@ FS::cust_bill - Object methods for cust_bill records
 
   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;
 
@@ -70,7 +76,7 @@ all payments (see L<FS::cust_pay>).
 
 =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
@@ -78,17 +84,7 @@ 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
 
@@ -101,14 +97,13 @@ automatically set to charged).
 =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
@@ -120,8 +115,6 @@ no record you ever posted this invoice (which is bad, no?)
 
 sub delete {
   return "Can't remove invoice!"
-  #my($self)=@_;
-  #$self->del;
 }
 
 =item replace OLD_RECORD
@@ -136,21 +129,14 @@ calling the collect method of a customer object (see L<FS::cust_main>).
 =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
@@ -162,30 +148,24 @@ methods.
 =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
 }
@@ -198,13 +178,13 @@ followed by the previous outstanding invoices (as FS::cust_bill objects also).
 =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;
 }
 
@@ -215,7 +195,7 @@ Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
 =cut
 
 sub cust_bill_pkg {
-  my($self)=@_;
+  my $self = shift;
   qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
 }
 
@@ -228,9 +208,9 @@ credits (FS::cust_credit objects).
 =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 } )
   ;
@@ -245,7 +225,7 @@ Returns all payments (see L<FS::cust_pay>) for this invoice.
 =cut
 
 sub cust_pay {
-  my($self)=@_;
+  my $self = shift;
   sort { $a->_date <=> $b->date }
     qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
   ;
@@ -264,216 +244,201 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 
 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->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 Previous Balance','$' . sprintf("%10.2f",$pr_total ) );
+    push @buf,('','');
+  }
 
-    #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 ) {
 
-    #new charges
-    foreach ( $self->cust_bill_pkg ) {
+    if ( $_->pkgnum ) {
 
-      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;
 
-        my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } );
-        my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart});
-        my($pkg)=$part_pkg->pkg;
+      if ( $_->setup != 0 ) {
+        push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) );
+        push @buf, map { "  ". $_->[0]. ": ". $_->[1], '' } $cust_pkg->labels;
+      }
 
-        push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) )
-          if $_->setup != 0;
+      if ( $_->recur != 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, map { "  ". $_->[0]. ": ". $_->[1], '' } $cust_pkg->labels;
       }
-    }
-
-    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)
-      );
+    } else { #pkgnum Tax
+      push @buf,("Tax",'$' . sprintf("%10.2f",$_->setup) ) 
+        if $_->setup != 0;
     }
-
-    #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_pages)=int(scalar(@buf)/30); #15 lines, 2 values per line
-    $tot_pages++ if scalar(@buf) % 30;
-
-    while (@buf) {
+  }
+
+  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.7 1999-02-09 09:55:05 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>,
@@ -489,6 +454,28 @@ charges can be negative ivan@sisd.com 98-jul-13
 
 pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20
 
+$Log: cust_bill.pm,v $
+Revision 1.7  1999-02-09 09:55:05  ivan
+invoices show line items for each service in a package (see the label method
+of FS::cust_svc)
+
+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
+
+
 =cut
 
 1;