fix invoice sub-totals, RT#6489
[freeside.git] / FS / FS / cust_bill.pm
index 961d86e..a0885f1 100644 (file)
@@ -96,6 +96,18 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 
 =item charged - amount of this invoice
 
+=item invoice_terms - optional terms override for this specific invoice
+
+=back
+
+Customer info at invoice generation time
+
+=over 4
+
+=item previous_balance
+
+=item billing_balance
+
 =back
 
 Deprecated
@@ -521,6 +533,7 @@ Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice.
 
 sub cust_bill_pay {
   my $self = shift;
+  map { $_ } #return $self->num_cust_bill_pay unless wantarray;
   sort { $a->_date <=> $b->_date }
     qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum } );
 }
@@ -535,6 +548,7 @@ Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice.
 
 sub cust_credited {
   my $self = shift;
+  map { $_ } #return $self->num_cust_credit_bill unless wantarray;
   sort { $a->_date <=> $b->_date }
     qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum } )
   ;
@@ -553,6 +567,7 @@ with matching pkgnum.
 
 sub cust_bill_pay_pkgnum {
   my( $self, $pkgnum ) = @_;
+  map { $_ } #return $self->num_cust_bill_pay_pkgnum($pkgnum) unless wantarray;
   sort { $a->_date <=> $b->_date }
     qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum,
                                 'pkgnum' => $pkgnum,
@@ -562,6 +577,8 @@ sub cust_bill_pay_pkgnum {
 
 =item cust_credited_pkgnum PKGNUM
 
+=item cust_credit_bill_pkgnum PKGNUM
+
 Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice
 with matching pkgnum.
 
@@ -569,6 +586,7 @@ with matching pkgnum.
 
 sub cust_credited_pkgnum {
   my( $self, $pkgnum ) = @_;
+  map { $_ } #return $self->num_cust_credit_bill_pkgnum($pkgnum) unless wantarray;
   sort { $a->_date <=> $b->_date }
     qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum,
                                    'pkgnum' => $pkgnum,
@@ -576,6 +594,10 @@ sub cust_credited_pkgnum {
            );
 }
 
+sub cust_credit_bill_pkgnum {
+  shift->cust_credited_pkgnum(@_);
+}
+
 =item tax
 
 Returns the tax amount (see L<FS::cust_bill_pkg>) for this invoice.
@@ -1065,7 +1087,7 @@ sub send {
     }
     $invoice_from = $opt->{'invoice_from'};
     $balance_over = $opt->{'balance_over'} if $opt->{'balance_over'};
-    $notice_name = $opt=>{'notice_name'};
+    $notice_name = $opt->{'notice_name'};
   } else {
     $template = scalar(@_) ? shift : '';
     if ( scalar(@_) && $_[0]  ) {
@@ -2345,8 +2367,8 @@ sub print_generic {
 #  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
   #my $balance_due = $self->owed + $pr_total - $cr_total;
   my $balance_due = $self->owed + $pr_total;
-  $invoice_data{'true_previous_balance'} = sprintf("%.2f", $self->previous_balance);
-  $invoice_data{'balance_adjustments'} = sprintf("%.2f", $self->previous_balance - $self->billing_balance);
+  $invoice_data{'true_previous_balance'} = sprintf("%.2f", ($self->previous_balance || 0) );
+  $invoice_data{'balance_adjustments'} = sprintf("%.2f", ($self->previous_balance || 0) - ($self->billing_balance || 0) );
   $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
   $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
 
@@ -2979,10 +3001,10 @@ sub _translate_old_latex_format {
         $line_item_line =~ s/\$(\w+)/'. \$_tr_line->{$1}. '/g;
         push @template, "    \$OUT .= '$line_item_line';";
       }
-  
+
       push @template, '}',
                       '--@]';
-
+      #' doh, gvim
     } elsif ( $line =~ /^%%TotalDetails\s*$/ ) {
 
       push @template, '[@--',
@@ -3016,11 +3038,12 @@ sub _translate_old_latex_format {
 sub terms {
   my $self = shift;
 
-  #check for an invoice- specific override (eventually)
+  #check for an invoice-specific override
+  return $self->invoice_terms if $self->invoice_terms;
   
   #check for a customer- specific override
-  return $self->cust_main->invoice_terms
-    if $self->cust_main->invoice_terms;
+  my $cust_main = $self->cust_main;
+  return $cust_main->invoice_terms if $cust_main->invoice_terms;
 
   #use configured default
   $conf->config('invoice_default_terms') || '';
@@ -3085,70 +3108,72 @@ sub _date_pretty {
   time2str('%x', $self->_date);
 }
 
+use vars qw(%pkg_category_cache);
 sub _items_sections {
   my $self = shift;
   my $late = shift;
   my $summarypage = shift;
   my $escape = shift;
 
-  my %s = ();
-  my %l = ();
+  my %subtotal = ();
+  my %late_subtotal = ();
   my %not_tax = ();
 
   foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
   {
 
-
       my $usage = $cust_bill_pkg->usage;
 
       foreach my $display ($cust_bill_pkg->cust_bill_pkg_display) {
         next if ( $display->summary && $summarypage );
 
-        my $desc = $display->section;
-        my $type = $display->type;
+        my $section = $display->section;
+        my $type    = $display->type;
 
-        if ( $cust_bill_pkg->pkgnum > 0 ) {
-          $not_tax{$desc} = 1;
-        }
+        $not_tax{$section} = 1
+          unless $cust_bill_pkg->pkgnum == 0;
 
         if ( $display->post_total && !$summarypage ) {
           if (! $type || $type eq 'S') {
-            $l{$desc} += $cust_bill_pkg->setup
-              if ( $cust_bill_pkg->setup != 0 );
+            $late_subtotal{$section} += $cust_bill_pkg->setup
+              if $cust_bill_pkg->setup != 0;
           }
 
           if (! $type) {
-            $l{$desc} += $cust_bill_pkg->recur
-              if ( $cust_bill_pkg->recur != 0 );
+            $late_subtotal{$section} += $cust_bill_pkg->recur
+              if $cust_bill_pkg->recur != 0;
           }
 
           if ($type && $type eq 'R') {
-            $l{$desc} += $cust_bill_pkg->recur - $usage
-              if ( $cust_bill_pkg->recur != 0 );
+            $late_subtotal{$section} += $cust_bill_pkg->recur - $usage
+              if $cust_bill_pkg->recur != 0;
           }
           
           if ($type && $type eq 'U') {
-            $l{$desc} += $usage;
+            $late_subtotal{$section} += $usage;
           }
 
         } else {
+
+          next if $cust_bill_pkg->pkgnum == 0 && ! $section;
+
           if (! $type || $type eq 'S') {
-            $s{$desc} += $cust_bill_pkg->setup
-              if ( $cust_bill_pkg->setup != 0 );
+            $subtotal{$section} += $cust_bill_pkg->setup
+              if $cust_bill_pkg->setup != 0;
           }
 
           if (! $type) {
-            $s{$desc} += $cust_bill_pkg->recur
-              if ( $cust_bill_pkg->recur != 0 );
+            $subtotal{$section} += $cust_bill_pkg->recur
+              if $cust_bill_pkg->recur != 0;
           }
 
           if ($type && $type eq 'R') {
-            $s{$desc} += $cust_bill_pkg->recur - $usage
-              if ( $cust_bill_pkg->recur != 0 );
+            $subtotal{$section} += $cust_bill_pkg->recur - $usage
+              if $cust_bill_pkg->recur != 0;
           }
           
           if ($type && $type eq 'U') {
-            $s{$desc} += $usage;
+            $subtotal{$section} += $usage;
           }
 
         }
@@ -3157,28 +3182,42 @@ sub _items_sections {
 
   }
 
-  my %cache = map { $_->categoryname => $_ }
-              qsearch( 'pkg_category', {disabled => 'Y'} );
-  $cache{$_->categoryname} = $_
-    foreach qsearch( 'pkg_category', {disabled => ''} );
+  %pkg_category_cache = ();
 
   push @$late, map { { 'description' => &{$escape}($_),
-                       'subtotal'    => $l{$_},
+                       'subtotal'    => $late_subtotal{$_},
                        'post_total'  => 1,
                    } }
-                 sort { $cache{$a}->weight <=> $cache{$b}->weight } keys %l;
+                 sort _categorysort keys %late_subtotal;
+
+  my @sections;
+  if ( $summarypage ) {
+    @sections = grep { exists($subtotal{$_}) || ! _pkg_category{$_}->disabled }
+                keys %pkg_category_cache;
+  } else {
+    @sections = keys %subtotal;
+  }
 
   map { { 'description' => &{$escape}($_),
-          'subtotal'    => $s{$_},
+          'subtotal'    => $subtotal{$_},
           'summarized'  => $not_tax{$_} ? '' : 'Y',
           'tax_section' => $not_tax{$_} ? '' : 'Y',
-      } }
-    sort { $cache{$a}->weight <=> $cache{$b}->weight }
-    ( $summarypage
-        ? ( grep { exists($s{$_}) || !$cache{$_}->disabled } keys %cache )
-        : ( keys %s )
-    );
+        }
+      }
+    sort _categorysort @sections;
+
+}
+
+#helper subs for above
+
+sub _categorysort {
+  _pkg_category($a)->weight <=> _pkg_category($b)->weight;
+}
 
+sub _pkg_category {
+  my $categoryname = shift;
+  $pkg_category_cache{$categoryname} ||=
+    qsearchs( 'pkg_category', { 'categoryname' => $categoryname } );
 }
 
 sub _items {