use FS::part_svc and FS::svc_acct_pop to avoid warnings
[freeside.git] / site_perl / cust_bill.pm
index 455fc2d..0e87755 100644 (file)
@@ -1,17 +1,21 @@
 package FS::cust_bill;
 
 use strict;
 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 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 );
 
 #ask FS::UID to run this stuff for us later
 $FS::UID::callback{'FS::cust_bill'} = sub { 
   $conf = new FS::Conf;
 
 #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');
+  ( $add1, $add2, $add3, $add4 ) = ( $conf->config('address'), '', '', '', '' );
 };
 
 =head1 NAME
 };
 
 =head1 NAME
@@ -22,8 +26,8 @@ FS::cust_bill - Object methods for cust_bill records
 
   use FS::cust_bill;
 
 
   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;
 
 
   $error = $record->insert;
 
@@ -72,7 +76,7 @@ all payments (see L<FS::cust_pay>).
 
 =over 4
 
 
 =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
 
 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
@@ -80,17 +84,7 @@ Invoices are normally created by calling the bill method of a customer object
 
 =cut
 
 
 =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
 
 
 =item insert
 
@@ -103,14 +97,13 @@ automatically set to charged).
 =cut
 
 sub 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;
 
   return "owed != charged!"
     unless $self->owed == $self->charged;
 
-  $self->check or
-  $self->add;
+  $self->SUPER::insert;
 }
 
 =item delete
 }
 
 =item delete
@@ -122,8 +115,6 @@ no record you ever posted this invoice (which is bad, no?)
 
 sub delete {
   return "Can't remove invoice!"
 
 sub delete {
   return "Can't remove invoice!"
-  #my($self)=@_;
-  #$self->del;
 }
 
 =item replace OLD_RECORD
 }
 
 =item replace OLD_RECORD
@@ -138,21 +129,14 @@ calling the collect method of a customer object (see L<FS::cust_main>).
 =cut
 
 sub replace {
 =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
 }
 
 =item check
@@ -164,30 +148,24 @@ methods.
 =cut
 
 sub 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"
   return "Unknown customer"
-    unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}});
-
-  $recref->{_date} =~ /^(\d*)$/ or return "Illegal date";
-  $recref->{_date} = $recref->{_date} ? $1 : time;
+    unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
 
 
-  #$recref->{charged} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal charged";
-  $recref->{charged} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal charged";
-  $recref->{charged} = $1;
+  $self->_date(time) unless $self->_date;
 
 
-  $recref->{owed} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal owed";
-  $recref->{owed} = $1;
-
-  $recref->{printed} =~ /^(\d*)$/ or return "Illegal printed";
-  $recref->{printed} = $1 || '0';
+  $self->printed(0) if $self->printed eq '';
 
   ''; #no error
 }
 
   ''; #no error
 }
@@ -200,13 +178,13 @@ followed by the previous outstanding invoices (as FS::cust_bill objects also).
 =cut
 
 sub previous {
 =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 }
     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;
 }
 
   $total, @cust_bill;
 }
 
@@ -217,7 +195,7 @@ Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
 =cut
 
 sub cust_bill_pkg {
 =cut
 
 sub cust_bill_pkg {
-  my($self)=@_;
+  my $self = shift;
   qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
 }
 
   qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
 }
 
@@ -230,9 +208,9 @@ credits (FS::cust_credit objects).
 =cut
 
 sub cust_credit {
 =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 } )
   ;
     grep { $_->credited != 0 && $_->_date < $self->_date }
       qsearch('cust_credit', { 'custnum' => $self->custnum } )
   ;
@@ -247,7 +225,7 @@ Returns all payments (see L<FS::cust_pay>) for this invoice.
 =cut
 
 sub cust_pay {
 =cut
 
 sub cust_pay {
-  my($self)=@_;
+  my $self = shift;
   sort { $a->_date <=> $b->date }
     qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
   ;
   sort { $a->_date <=> $b->date }
     qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
   ;
@@ -266,216 +244,201 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 
 sub print_text {
 
 
 sub print_text {
 
-  my($self,$today)=@_;
+  my( $self, $today ) = ( shift, shift );
   $today ||= time;
   $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?
 
   #overdue?
-  my($overdue) = ( 
+  my $overdue = ( 
     $balance_due > 0
     && $today > $self->_date 
     && $self->printed > 1
   );
 
     $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)
         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);
       $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
 
 
 }
 
 =back
 
+=head1 VERSION
+
+$Id: cust_bill.pm,v 1.7 1999-02-09 09:55:05 ivan Exp $
+
 =head1 BUGS
 
 The delete method.
 
 =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?)
 
 
 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>,
 =head1 SEE ALSO
 
 L<FS::Record>, L<FS::cust_main>, L<FS::cust_pay>, L<FS::cust_bill_pkg>,
@@ -492,7 +455,20 @@ 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 $
 pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20
 
 $Log: cust_bill.pm,v $
-Revision 1.3  1998-11-13 09:56:53  ivan
+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.)
 
 change configuration file layout to support multiple distinct databases (with
 own set of config files, export, etc.)