show more detail about payments on invoice, #21949
[freeside.git] / FS / FS / cust_bill.pm
index 8cd508c..b50f352 100644 (file)
@@ -388,13 +388,29 @@ sub previous {
   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 } ) 
+    grep { $_->owed != 0 }
+      qsearch( 'cust_bill', { 'custnum'  => $self->custnum,
+                              #'_date'   => { op=>'<', value=>$self->_date },
+                              'invnum'   => { op=>'<', value=>$self->invnum },
+                            } ) 
   ;
   foreach ( @cust_bill ) { $total += $_->owed; }
   $total, @cust_bill;
 }
 
+=item enable_previous
+
+Whether to show the 'Previous Charges' section when printing this invoice.
+The negation of the 'disable_previous_balance' config setting.
+
+=cut
+
+sub enable_previous {
+  my $self = shift;
+  my $agentnum = $self->cust_main->agentnum;
+  !$self->conf->exists('disable_previous_balance', $agentnum);
+}
+
 =item cust_bill_pkg
 
 Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
@@ -1525,7 +1541,10 @@ sub print {
     $self->batch_invoice(\%opt);
   }
   else {
-    do_print $self->lpr_data(\%opt);
+    do_print(
+      $self->lpr_data(\%opt),
+      'agentnum' => $self->cust_main->agentnum,
+    );
   }
 }
 
@@ -2011,12 +2030,16 @@ sub print_csv {
   } elsif ( lc($opt{'format'}) eq 'oneline' ) { #name?
   
     my ($previous_balance) = $self->previous; 
+    $previous_balance = sprintf('%.2f', $previous_balance);
     my $totaldue = sprintf('%.2f', $self->owed + $previous_balance);
     my @items = map {
-      ($_->{pkgnum} || ''),
-      $_->{description},
-      $_->{amount}
-    } $self->_items_pkg;
+                      $_->{pkgnum},
+                      $_->{description},
+                      $_->{amount}
+                    }
+                  $self->_items_pkg, #_items_nontax?  no sections or anything
+                                     # with this format
+                  $self->_items_tax;
 
     $csv->combine(
       $cust_main->agentnum,
@@ -2035,6 +2058,8 @@ sub print_csv {
       $self->invnum,
       $self->charged,
       $totaldue,
+      $previous_balance,
+      $self->due_date2str("%x"),
 
       @items,
     );
@@ -2965,10 +2990,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;
@@ -2981,6 +3007,12 @@ sub print_generic {
   my $late_sections = [];
   my $extra_sections = [];
   my $extra_lines = ();
+
+  my $default_section = { 'description' => '',
+                          'subtotal'    => '', 
+                          'no_subtotal' => 1,
+                        };
+
   if ( $multisection ) {
     ($extra_sections, $extra_lines) =
       $self->_items_extra_usage_sections($escape_function_nonbsp, $format)
@@ -3012,8 +3044,7 @@ sub print_generic {
     }
   } else {# not multisection
     # make a default section
-    push @sections, { 'description' => '', 'subtotal' => '', 
-      'no_subtotal' => 1 };
+    push @sections, $default_section;
     # and calculate the finance charge total, since it won't get done otherwise.
     # XXX possibly other totals?
     # XXX possibly finance_pkgclass should not be used in this manner?
@@ -3031,10 +3062,11 @@ sub print_generic {
     }
   }
 
-  unless (    $conf->exists('disable_previous_balance', $agentnum)
-           || $conf->exists('previous_balance-summary_only')
-         )
-  {
+  # 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;
@@ -3045,8 +3077,10 @@ sub print_generic {
         ext_description => [],
       };
       $detail->{'ref'} = $line_item->{'pkgnum'};
+      $detail->{'pkgpart'} = $line_item->{'pkgpart'};
       $detail->{'quantity'} = 1;
-      $detail->{'section'} = $previous_section;
+      $detail->{'section'} = $multisection ? $previous_section
+                                           : $default_section;
       $detail->{'description'} = &$escape_function($line_item->{'description'});
       if ( exists $line_item->{'ext_description'} ) {
         @{$detail->{'ext_description'}} = map {
@@ -3064,9 +3098,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) ];
@@ -3143,6 +3176,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'});
@@ -3158,6 +3192,7 @@ sub print_generic {
       $detail->{'sdate'} = $line_item->{'sdate'};
       $detail->{'edate'} = $line_item->{'edate'};
       $detail->{'seconds'} = $line_item->{'seconds'};
+      $detail->{'svc_label'} = $line_item->{'svc_label'};
   
       push @detail_items, $detail;
       push @buf, ( [ $detail->{'description'},
@@ -3182,7 +3217,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;
@@ -3246,25 +3283,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 ) );
@@ -3286,8 +3324,13 @@ sub print_generic {
               ];
     push @buf,['',''];
   }
-  
-  unless ( $conf->exists('disable_previous_balance', $agentnum) ) {
+
+  # 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
@@ -3363,10 +3406,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} ) {
@@ -4957,9 +5001,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
@@ -4978,13 +5027,16 @@ sub _items_cust_bill_pkg {
             || $cust_bill_pkg->recur_show_zero;
 
           my @d = ();
+          my $svc_label;
           unless ( $cust_pkg->part_pkg->hide_svc_detail
                 || $cust_bill_pkg->hidden )
           {
 
-            push @d, map &{$escape_function}($_),
-                         $cust_pkg->h_labels_short($self->_date, undef, 'I')
+            my @svc_labels = map &{$escape_function}($_),
+                        $cust_pkg->h_labels_short($self->_date, undef, 'I');
+            push @d, @svc_labels
               unless $cust_bill_pkg->pkgpart_override; #don't redisplay services
+            $svc_label = $svc_labels[0];
 
             if ( $multilocation ) {
               my $loc = $cust_pkg->location_label;
@@ -5006,13 +5058,14 @@ 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,
               unit_amount     => $cust_bill_pkg->unitsetup,
               quantity        => $cust_bill_pkg->quantity,
               ext_description => \@d,
+              svc_label       => ($svc_label || ''),
             };
           };
 
@@ -5035,14 +5088,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)
+              || ! $cust_bill_pkg->sdate
+              || ! $cust_bill_pkg->edate
           ) {
             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_monthly',
+                                         $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' ) {
@@ -5062,6 +5124,7 @@ sub _items_cust_bill_pkg {
 
           my @d = ();
           my @seconds = (); # for display of usage info
+          my $svc_label = '';
 
           #at least until cust_bill_pkg has "past" ranges in addition to
           #the "future" sdate/edate ones... see #3032
@@ -5079,11 +5142,11 @@ sub _items_cust_bill_pkg {
             warn "$me _items_cust_bill_pkg adding service details\n"
               if $DEBUG > 1;
 
-            push @d, map &{$escape_function}($_),
-                         $cust_pkg->h_labels_short(@dates, 'I')
-                                                   #$cust_bill_pkg->edate,
-                                                   #$cust_bill_pkg->sdate)
+            my @svc_labels = map &{$escape_function}($_),
+                        $cust_pkg->h_labels_short($self->_date, undef, 'I');
+            push @d, @svc_labels
               unless $cust_bill_pkg->pkgpart_override; #don't redisplay services
+            $svc_label = $svc_labels[0];
 
             warn "$me _items_cust_bill_pkg done adding service details\n"
               if $DEBUG > 1;
@@ -5154,7 +5217,7 @@ sub _items_cust_bill_pkg {
             } 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,
@@ -5162,6 +5225,7 @@ sub _items_cust_bill_pkg {
                 quantity        => $cust_bill_pkg->quantity,
                 %item_dates,
                 ext_description => \@d,
+                svc_label       => ($svc_label || ''),
               };
               $r->{'seconds'} = \@seconds if grep {defined $_} @seconds;
             }
@@ -5178,7 +5242,7 @@ sub _items_cust_bill_pkg {
             } 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,
@@ -5278,11 +5342,16 @@ sub _items_payments {
 
     #something more elaborate if $_->amount ne ->cust_pay->paid ?
 
+    my $desc = $self->mt('Payment received').' '.
+               time2str($date_format,$_->cust_pay->_date );
+    $desc   .= $self->mt(' via ' . $_->cust_pay->payby_payinfo_pretty)
+      if ( $self->conf->exists('invoice_payment_details') );
     push @b, {
-      'description' => $self->mt('Payment received').' '.
-                       time2str($date_format,$_->cust_pay->_date ),
+      'description' => $desc,
       'amount'      => sprintf("%.2f", $_->amount )
     };
+
   }
 
   @b;
@@ -5619,11 +5688,25 @@ sub search_sql_where {
     push @search, "cust_main.agentnum = $1";
   }
 
-  #agentnum
+  #refnum
+  if ( $param->{'refnum'} =~ /^(\d+)$/ ) {
+    push @search, "cust_main.refnum = $1";
+  }
+
+  #custnum
   if ( $param->{'custnum'} =~ /^(\d+)$/ ) {
     push @search, "cust_bill.custnum = $1";
   }
 
+  #customer classnum
+  if ( $param->{'cust_classnum'} ) {
+    my $classnums = $param->{'cust_classnum'};
+    $classnums = [ $classnums ] if !ref($classnums);
+    $classnums = [ grep /^\d+$/, @$classnums ];
+    push @search, 'cust_main.classnum in ('.join(',',@$classnums).')'
+      if @$classnums;
+  }
+
   #_date
   if ( $param->{_date} ) {
     my($beginning, $ending) = @{$param->{_date}};