better invoice summary, RT#20601
[freeside.git] / FS / FS / Template_Mixin.pm
index c42e239..759b04e 100644 (file)
@@ -584,16 +584,20 @@ sub print_generic {
   #my $balance_due = $self->owed + $pr_total - $cr_total;
   my $balance_due = $self->owed + $pr_total;
 
-  # the customer's current balance as shown on the invoice before this one
-  $invoice_data{'true_previous_balance'} = sprintf("%.2f", ($self->previous_balance || 0) );
+  #these are used on the summary page only
 
-  # the change in balance from that invoice to this one
-  $invoice_data{'balance_adjustments'} = sprintf("%.2f", ($self->previous_balance || 0) - ($self->billing_balance || 0) );
+    # the customer's current balance as shown on the invoice before this one
+    $invoice_data{'true_previous_balance'} = sprintf("%.2f", ($self->previous_balance || 0) );
 
-  # the sum of amount owed on all previous invoices
-  $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
+    # the change in balance from that invoice to this one
+    $invoice_data{'balance_adjustments'} = sprintf("%.2f", ($self->previous_balance || 0) - ($self->billing_balance || 0) );
+
+    # the sum of amount owed on all previous invoices
+    # ($pr_total is used elsewhere but not as $previous_balance)
+    $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
 
   # the sum of amount owed on all invoices
+  # (this is used in the summary & on the payment coupon)
   $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
 
   # info from customer's last invoice before this one, for some 
@@ -727,10 +731,11 @@ sub print_generic {
 
 
   my $adjusttotal = 0;
-  my $adjust_section = { 'description' => 
-    $self->mt('Credits, Payments, and Adjustments'),
-                         'subtotal'    => 0,   # adjusted below
-                       };
+  my $adjust_section = {
+    'description'    => $self->mt('Credits, Payments, and Adjustments'),
+    'adjust_section' => 1,
+    'subtotal'       => 0,   # adjusted below
+  };
   my $adjust_weight = _pkg_category($adjust_section->{description})
                         ? _pkg_category($adjust_section->{description})->weight
                         : 0;
@@ -806,11 +811,11 @@ sub print_generic {
     }
   }
 
-  unless (    $conf->exists('disable_previous_balance', $agentnum)
-           || $conf->exists('previous_balance-summary_only')
-           || ! $self->can('_items_previous')
-         )
-  {
+  # previous invoice balances in the Previous Charges section if there
+  # is one, otherwise in the main detail section
+  if ( $self->can('_items_previous') &&
+       $self->enable_previous &&
+       ! $conf->exists('previous_balance-summary_only') ) {
 
     warn "$me adding previous balances\n"
       if $DEBUG > 1;
@@ -821,6 +826,7 @@ sub print_generic {
         ext_description => [],
       };
       $detail->{'ref'} = $line_item->{'pkgnum'};
+      $detail->{'pkgpart'} = $line_item->{'pkgpart'};
       $detail->{'quantity'} = 1;
       $detail->{'section'} = $multisection ? $previous_section
                                            : $default_section;
@@ -841,9 +847,8 @@ sub print_generic {
     }
 
   }
-  
-  if ( @pr_cust_bill && !$conf->exists('disable_previous_balance', $agentnum) ) 
-    {
+
+  if ( @pr_cust_bill && $self->enable_previous ) {
     push @buf, ['','-----------'];
     push @buf, [ $self->mt('Total Previous Balance'),
                  $money_char. sprintf("%10.2f", $pr_total) ];
@@ -895,7 +900,6 @@ sub print_generic {
     warn "$me   setting options\n"
       if $DEBUG > 1;
 
-    my $multilocation = scalar($cust_main->cust_location); #too expensive?
     my %options = ();
     $options{'section'} = $section if $multisection;
     $options{'format'} = $format;
@@ -905,7 +909,6 @@ sub print_generic {
     $options{'summary_page'} = $summarypage;
     $options{'skip_usage'} =
       scalar(@$extra_sections) && !grep{$section == $_} @$extra_sections;
-    $options{'multilocation'} = $multilocation;
     $options{'multisection'} = $multisection;
 
     warn "$me   searching for line items\n"
@@ -920,6 +923,7 @@ sub print_generic {
         ext_description => [],
       };
       $detail->{'ref'} = $line_item->{'pkgnum'};
+      $detail->{'pkgpart'} = $line_item->{'pkgpart'};
       $detail->{'quantity'} = $line_item->{'quantity'};
       $detail->{'section'} = $section;
       $detail->{'description'} = &$escape_function($line_item->{'description'});
@@ -961,7 +965,9 @@ sub print_generic {
   $invoice_data{current_less_finance} =
     sprintf('%.2f', $self->charged - $invoice_data{finance_amount} );
 
-  if ( $multisection && !$conf->exists('disable_previous_balance', $agentnum)
+  # create a major section for previous balance if we have major sections,
+  # or if previous_section is in summary form
+  if ( ( $multisection && $self->enable_previous )
     || $conf->exists('previous_balance-summary_only') )
   {
     unshift @sections, $previous_section if $pr_total;
@@ -1025,25 +1031,26 @@ sub print_generic {
 
   push @buf,['','-----------'];
   push @buf,[$self->mt( 
-              $conf->exists('disable_previous_balance', $agentnum) 
+              (!$self->enable_previous)
                ? 'Total Charges'
                : 'Total New Charges'
              ),
              $money_char. sprintf("%10.2f",$self->charged) ];
   push @buf,['',''];
 
+  # calculate total, possibly including total owed on previous
+  # invoices
   {
     my $total = {};
     my $item = 'Total';
     $item = $conf->config('previous_balance-exclude_from_total')
          || 'Total New Charges'
       if $conf->exists('previous_balance-exclude_from_total');
-    my $amount = $self->charged +
-                   ( $conf->exists('disable_previous_balance', $agentnum) ||
-                     $conf->exists('previous_balance-exclude_from_total')
-                     ? 0
-                     : $pr_total
-                   );
+    my $amount = $self->charged;
+    if ( $self->enable_previous and !$conf->exists('previous_balance-exclude_from_total') ) {
+      $amount += $pr_total;
+    }
+
     $total->{'total_item'} = &$embolden_function($self->mt($item));
     $total->{'total_amount'} =
       &$embolden_function( $other_money_char.  sprintf( '%.2f', $amount ) );
@@ -1065,12 +1072,13 @@ sub print_generic {
               ];
     push @buf,['',''];
   }
-  
-  unless (    $conf->exists('disable_previous_balance', $agentnum) 
-           || ! $self->can('_items_credits')
-           || ! $self->can('_items_payments')
-         )
-  {
+
+  # if we're showing previous invoices, also show previous
+  # credits and payments 
+  if ( $self->enable_previous 
+        and $self->can('_items_credits')
+        and $self->can('_items_payments') )
+    {
     #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
   
     # credits
@@ -1146,10 +1154,11 @@ sub print_generic {
       $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
       $total->{'total_amount'} =
         &$embolden_function(
-          $other_money_char. sprintf('%.2f', $summarypage 
-                                               ? $self->charged +
-                                                 $self->billing_balance
-                                               : $self->owed + $pr_total
+          $other_money_char. sprintf('%.2f', #why? $summarypage 
+                                             #  ? $self->charged +
+                                             #    $self->billing_balance
+                                             #  :
+                                                 $self->owed + $pr_total
                                     )
         );
       if ( $multisection && !$adjust_section->{sort_weight} ) {
@@ -1223,6 +1232,10 @@ sub print_generic {
     } } @discounts_avail;
   }
 
+  # debugging hook: call this with 'diag' => 1 to just get a hash of 
+  # the invoice variables
+  return \%invoice_data if ( $params{'diag'} );
+
   # All sections and items are built; now fill in templates.
   my @includelist = ();
   push @includelist, 'summary' if $summarypage;
@@ -1661,6 +1674,13 @@ sub _items_sections {
         $not_tax{$section} = 1
           unless $cust_bill_pkg->pkgnum == 0;
 
+        # there's actually a very important piece of logic buried in here:
+        # incrementing $late_subtotal{$section} CREATES 
+        # $late_subtotal{$section}.  keys(%late_subtotal) is later used 
+        # to define the list of late sections, and likewise keys(%subtotal).
+        # When _items_cust_bill_pkg is called to generate line items for 
+        # real, it will be called with 'section' => $section for each 
+        # of these.
         if ( $display->post_total && !$summarypage ) {
           if (! $type || $type eq 'S') {
             $late_subtotal{$section} += $cust_bill_pkg->setup
@@ -2108,11 +2128,9 @@ ignored.
 multisection: a flag indicating that this is a multisection invoice,
 which does something complicated.
 
-multilocation: a flag to display the location label for the package.
-
 Returns a list of hashrefs, each of which may contain:
 
-pkgnum, description, amount, unit_amount, quantity, _is_setup, and 
+pkgnum, description, amount, unit_amount, quantity, pkgpart, _is_setup, and 
 ext_description, which is an arrayref of detail lines to show below 
 the package line.
 
@@ -2131,13 +2149,13 @@ sub _items_cust_bill_pkg {
   my $unsquelched = $opt{unsquelched} || ''; #unused
   my $section = $opt{section}->{description} if $opt{section};
   my $summary_page = $opt{summary_page} || ''; #unused
-  my $multilocation = $opt{multilocation} || '';
   my $multisection = $opt{multisection} || '';
   my $discount_show_always = 0;
 
   my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50;
 
   my $cust_main = $self->cust_main;#for per-agent cust_bill-line_item-ate_style
+                                   # and location labels
 
   my @b = ();
   my ($s, $r, $u) = ( undef, undef, undef );
@@ -2168,14 +2186,13 @@ sub _items_cust_bill_pkg {
       if $DEBUG > 1;
 
     foreach my $display ( grep { defined($section)
-                                 ? $_->section eq $section
-                                 : 1
-                               }
-                          #grep { !$_->summary || !$summary_page } # bunk!
+                            ? $_->section eq $section
+                            : 1
+                          }
                           grep { !$_->summary || $multisection }
                           @cust_bill_pkg_display
                         )
-    {
+      {
 
       warn "$me _items_cust_bill_pkg considering cust_bill_pkg_display ".
            $display->billpkgdisplaynum. "\n"
@@ -2223,9 +2240,14 @@ sub _items_cust_bill_pkg {
  
         my $cust_pkg = $cust_bill_pkg->cust_pkg;
 
+        # which pkgpart to show for display purposes?
+        my $pkgpart = $cust_bill_pkg->pkgpart_override || $cust_pkg->pkgpart;
+
         # start/end dates for invoice formats that do nonstandard 
         # things with them
-        my %item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate');
+        my %item_dates = ();
+        %item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate')
+          unless $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1);
 
         if (    (!$type || $type eq 'S')
              && (    $cust_bill_pkg->setup != 0
@@ -2252,7 +2274,8 @@ sub _items_cust_bill_pkg {
                          $cust_pkg->h_labels_short($self->_date, undef, 'I')
               unless $cust_bill_pkg->pkgpart_override; #don't redisplay services
 
-            if ( $multilocation ) {
+            if ( ! $cust_pkg->locationnum or
+                   $cust_pkg->locationnum != $cust_main->ship_locationnum  ) {
               my $loc = $cust_pkg->location_label;
               $loc = substr($loc, 0, $maxlength). '...'
                 if $format eq 'latex' && length($loc) > $maxlength;
@@ -2272,7 +2295,7 @@ sub _items_cust_bill_pkg {
             $s = {
               _is_setup       => 1,
               description     => $description,
-              #pkgpart         => $part_pkg->pkgpart,
+              pkgpart         => $pkgpart,
               pkgnum          => $cust_bill_pkg->pkgnum,
               amount          => $cust_bill_pkg->setup,
               setup_show_zero => $cust_bill_pkg->setup_show_zero,
@@ -2301,16 +2324,23 @@ sub _items_cust_bill_pkg {
           my $description = ($is_summary && $type && $type eq 'U')
                             ? "Usage charges" : $desc;
 
+          my $part_pkg = $cust_pkg->part_pkg;
+
           #pry be a bit more efficient to look some of this conf stuff up
           # outside the loop
           unless (
             $conf->exists('disable_line_item_date_ranges')
-              || $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1)
+              || $part_pkg->option('disable_line_item_date_ranges',1)
           ) {
             my $time_period;
-            my $date_style = $conf->config( 'cust_bill-line_item-date_style',
+            my $date_style = '';
+            $date_style = $conf->config( 'cust_bill-line_item-date_style-non_monhtly',
+                                         $cust_main->agentnum
+                                       )
+              if $part_pkg && $part_pkg->freq !~ /^1m?$/;
+            $date_style ||= $conf->config( 'cust_bill-line_item-date_style',
                                             $cust_main->agentnum
-                                          );
+                                         );
             if ( defined($date_style) && $date_style eq 'month_of' ) {
               $time_period = time2str('The month of %B', $cust_bill_pkg->sdate);
             } elsif ( defined($date_style) && $date_style eq 'X_month' ) {
@@ -2336,10 +2366,11 @@ sub _items_cust_bill_pkg {
           push @dates, $prev->sdate if $prev;
           push @dates, undef if !$prev;
 
-          unless ( $cust_pkg->part_pkg->hide_svc_detail
+          unless ( $part_pkg->hide_svc_detail
                 || $cust_bill_pkg->itemdesc
                 || $cust_bill_pkg->hidden
-                || $is_summary && $type && $type eq 'U' )
+                || $is_summary && $type && $type eq 'U'
+              )
           {
 
             warn "$me _items_cust_bill_pkg adding service details\n"
@@ -2354,7 +2385,7 @@ sub _items_cust_bill_pkg {
             warn "$me _items_cust_bill_pkg done adding service details\n"
               if $DEBUG > 1;
 
-            if ( $multilocation ) {
+            if ( $cust_pkg->locationnum != $cust_main->ship_locationnum ) {
               my $loc = $cust_pkg->location_label;
               $loc = substr($loc, 0, $maxlength). '...'
                 if $format eq 'latex' && length($loc) > $maxlength;
@@ -2408,6 +2439,10 @@ sub _items_cust_bill_pkg {
             $amount = $cust_bill_pkg->usage;
           }
   
+          my $unit_amount =
+            ( $cust_bill_pkg->unitrecur > 0 ) ? $cust_bill_pkg->unitrecur
+                                              : $amount;
+
           if ( !$type || $type eq 'R' ) {
 
             warn "$me _items_cust_bill_pkg adding recur\n"
@@ -2415,16 +2450,16 @@ sub _items_cust_bill_pkg {
 
             if ( $cust_bill_pkg->hidden ) {
               $r->{amount}      += $amount;
-              $r->{unit_amount} += $cust_bill_pkg->unitrecur;
+              $r->{unit_amount} += $unit_amount;
               push @{ $r->{ext_description} }, @d;
             } else {
               $r = {
                 description     => $description,
-                #pkgpart         => $part_pkg->pkgpart,
+                pkgpart         => $pkgpart,
                 pkgnum          => $cust_bill_pkg->pkgnum,
                 amount          => $amount,
                 recur_show_zero => $cust_bill_pkg->recur_show_zero,
-                unit_amount     => $cust_bill_pkg->unitrecur,
+                unit_amount     => $unit_amount,
                 quantity        => $cust_bill_pkg->quantity,
                 %item_dates,
                 ext_description => \@d,
@@ -2439,16 +2474,16 @@ sub _items_cust_bill_pkg {
 
             if ( $cust_bill_pkg->hidden ) {
               $u->{amount}      += $amount;
-              $u->{unit_amount} += $cust_bill_pkg->unitrecur;
+              $u->{unit_amount} += $unit_amount,
               push @{ $u->{ext_description} }, @d;
             } else {
               $u = {
                 description     => $description,
-                #pkgpart         => $part_pkg->pkgpart,
+                pkgpart         => $pkgpart,
                 pkgnum          => $cust_bill_pkg->pkgnum,
                 amount          => $amount,
                 recur_show_zero => $cust_bill_pkg->recur_show_zero,
-                unit_amount     => $cust_bill_pkg->unitrecur,
+                unit_amount     => $unit_amount,
                 quantity        => $cust_bill_pkg->quantity,
                 %item_dates,
                 ext_description => \@d,