add a per-package disable_line_item_date_ranges option, RT13200
[freeside.git] / FS / FS / cust_bill.pm
index 3bec6dd..2d7341e 100644 (file)
@@ -5,6 +5,7 @@ use vars qw( @ISA $DEBUG $me $conf
              $money_char $date_format $rdate_format $date_format_long );
 use vars qw( $invoice_lines @buf ); #yuck
 use Fcntl qw(:flock); #for spool_csv
+use Cwd;
 use List::Util qw(min max);
 use Date::Format;
 use Text::Template 1.20;
@@ -38,7 +39,8 @@ use FS::part_bill_event;
 use FS::payby;
 use FS::bill_batch;
 use FS::cust_bill_batch;
-use Cwd;
+use FS::cust_bill_pay_pkg;
+use FS::cust_credit_bill_pkg;
 
 @ISA = qw( FS::cust_main_Mixin FS::Record );
 
@@ -243,6 +245,7 @@ sub delete {
     cust_pay_batch
     cust_bill_pay_batch
     cust_bill_pkg
+    cust_bill_batch
   )) {
 
     foreach my $linked ( $self->$table() ) {
@@ -620,44 +623,97 @@ sub cust_credit_bill {
   shift->cust_credited(@_);
 }
 
-=item cust_bill_pay_pkgnum PKGNUM
+#=item cust_bill_pay_pkgnum PKGNUM
+#
+#Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice
+#with matching pkgnum.
+#
+#=cut
+#
+#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,
+#                              }
+#           );
+#}
+
+=item cust_bill_pay_pkg PKGNUM
 
 Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice
-with matching pkgnum.
+applied against the matching pkgnum.
 
 =cut
 
-sub cust_bill_pay_pkgnum {
+sub cust_bill_pay_pkg {
   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,
-                              }
-           );
+
+  qsearch({
+    'select'    => 'cust_bill_pay_pkg.*',
+    'table'     => 'cust_bill_pay_pkg',
+    'addl_from' => ' LEFT JOIN cust_bill_pay USING ( billpaynum ) '.
+                   ' LEFT JOIN cust_bill_pkg USING ( billpkgnum ) ',
+    'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum.
+                   "   AND cust_bill_pkg.pkgnum = $pkgnum",
+  });
+
 }
 
-=item cust_credited_pkgnum 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.
+#
+#=cut
+#
+#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,
+#                                 }
+#           );
+#}
+#
+#sub cust_credit_bill_pkgnum {
+#  shift->cust_credited_pkgnum(@_);
+#}
 
-=item cust_credit_bill_pkgnum PKGNUM
+=item cust_credit_bill_pkg PKGNUM
 
-Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice
-with matching pkgnum.
+Returns all credit applications (see L<FS::cust_credit_bill>) for this invoice
+applied against the matching pkgnum.
 
 =cut
 
-sub cust_credited_pkgnum {
+sub cust_credit_bill_pkg {
   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,
-                                 }
-           );
+
+  qsearch({
+    'select'    => 'cust_credit_bill_pkg.*',
+    'table'     => 'cust_credit_bill_pkg',
+    'addl_from' => ' LEFT JOIN cust_credit_bill USING ( creditbillnum ) '.
+                   ' LEFT JOIN cust_bill_pkg    USING ( billpkgnum    ) ',
+    'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum.
+                   "   AND cust_bill_pkg.pkgnum = $pkgnum",
+  });
+
 }
 
-sub cust_credit_bill_pkgnum {
-  shift->cust_credited_pkgnum(@_);
+=item cust_bill_batch
+
+Returns all invoice batch records (L<FS::cust_bill_batch>) for this invoice.
+
+=cut
+
+sub cust_bill_batch {
+  my $self = shift;
+  qsearch('cust_bill_batch', { 'invnum' => $self->invnum });
 }
 
 =item tax
@@ -700,8 +756,8 @@ sub owed_pkgnum {
   my $balance = 0;
   $balance += $_->setup + $_->recur for $self->cust_bill_pkg_pkgnum($pkgnum);
 
-  $balance -= $_->amount            for $self->cust_bill_pay_pkgnum($pkgnum);
-  $balance -= $_->amount            for $self->cust_credited_pkgnum($pkgnum);
+  $balance -= $_->amount            for $self->cust_bill_pay_pkg($pkgnum);
+  $balance -= $_->amount            for $self->cust_credit_bill_pkg($pkgnum);
 
   $balance = sprintf( "%.2f", $balance);
   $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
@@ -2473,6 +2529,7 @@ sub print_generic {
     #invoice from info
     'company_name'    => scalar( $conf->config('company_name', $agentnum) ),
     'company_address' => join("\n", $conf->config('company_address', $agentnum) ). "\n",
+    'company_phonenum'=> scalar( $conf->config('company_phonenum', $agentnum) ),
     'returnaddress'   => $returnaddress,
     'agent'           => &$escape_function($cust_main->agent->agent),
 
@@ -2690,7 +2747,7 @@ sub print_generic {
                                             sprintf('%.2f', $pr_total),
                            'summarized'  => $summarypage ? 'Y' : '',
                          };
-  $previous_section->{posttotal} = '0 / 30 / 60/ 90 days overdue '. 
+  $previous_section->{posttotal} = '0 / 30 / 60 / 90 days overdue '. 
     join(' / ', map { $cust_main->balance_date_range(@$_) }
                 $self->_prior_month30s
         )
@@ -2783,7 +2840,7 @@ sub print_generic {
     }
 
   }
-
+  
   if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
     push @buf, ['','-----------'];
     push @buf, [ 'Total Previous Balance',
@@ -2796,12 +2853,11 @@ sub print_generic {
         if $DEBUG > 1;
 
       my ($didsummary,$minutes) = $self->_did_summary;
-      my $didsummary_desc = 'DID Activity Summary (Past 30 days)';
+      my $didsummary_desc = 'DID Activity Summary (since last invoice)';
       push @detail_items, 
        { 'description' => $didsummary_desc,
            'ext_description' => [ $didsummary, $minutes ],
-       }
-       if !$multisection;
+       };
   }
 
   foreach my $section (@sections, @$late_sections) {
@@ -3262,7 +3318,7 @@ sub print_ps {
   my ($file, $logofile, $barcodefile) = $self->print_latex(@_);
   my $ps = generate_ps($file);
   unlink($logofile);
-  unlink($barcodefile);
+  unlink($barcodefile) if $barcodefile;
 
   $ps;
 }
@@ -3291,7 +3347,7 @@ sub print_pdf {
   my ($file, $logofile, $barcodefile) = $self->print_latex(@_);
   my $pdf = generate_pdf($file);
   unlink($logofile);
-  unlink($barcodefile);
+  unlink($barcodefile) if $barcodefile;
 
   $pdf;
 }
@@ -3614,7 +3670,7 @@ sub _items_sections {
                     }
                   } @sections;
   push @early, @$extra_sections if $extra_sections;
+
   sort { $a->{sort_weight} <=> $b->{sort_weight} } @early;
 
 }
@@ -3979,7 +4035,12 @@ sub _items_extra_usage_sections {
 sub _did_summary {
     my $self = shift;
     my $end = $self->_date;
-    my $start = $end - 2592000; # 30 days
+
+    # start at date of previous invoice + 1 second or 0 if no previous invoice
+    my $start = $self->scalar_sql("SELECT max(_date) FROM cust_bill WHERE custnum = ? and invnum != ?",$self->custnum,$self->invnum);
+    $start = 0 if !$start;
+    $start++;
+
     my $cust_main = $self->cust_main;
     my @pkgs = $cust_main->all_pkgs;
     my($num_activated,$num_deactivated,$num_portedin,$num_portedout,$minutes)
@@ -3993,7 +4054,7 @@ sub _did_summary {
 
            my $inserted = $h_cust_svc->date_inserted;
            my $deleted = $h_cust_svc->date_deleted;
-           my $phone_inserted = $h_cust_svc->h_svc_x($inserted);
+           my $phone_inserted = $h_cust_svc->h_svc_x($inserted+5);
            my $phone_deleted;
            $phone_deleted =  $h_cust_svc->h_svc_x($deleted) if $deleted;
            
@@ -4026,10 +4087,13 @@ sub _did_summary {
            }
 
            # increment usage minutes
-           my @cdrs = $phone_inserted->get_cdrs('begin'=>$start,'end'=>$end);
-           foreach my $cdr ( @cdrs ) {
-               $minutes += $cdr->billsec/60;
-           }
+        if ( $phone_inserted ) {
+            my @cdrs = $phone_inserted->get_cdrs('begin'=>$start,'end'=>$end,'billsec_sum'=>1);
+            $minutes = $cdrs[0]->billsec_sum if scalar(@cdrs) == 1;
+        }
+        else {
+            warn "WARNING: no matching h_svc_phone insert record for insert time $inserted, svcnum " . $h_cust_svc->svcnum;
+        }
 
            # don't look at this service again
            push @seen, $h_cust_svc->svcnum;
@@ -4398,23 +4462,10 @@ sub _items_cust_bill_pkg {
   foreach my $cust_bill_pkg ( @$cust_bill_pkgs )
   {
 
-    warn "$me _items_cust_bill_pkg considering cust_bill_pkg $cust_bill_pkg\n"
+    warn "$me _items_cust_bill_pkg considering cust_bill_pkg ".
+         $cust_bill_pkg->billpkgnum. ", pkgnum ". $cust_bill_pkg->pkgnum. "\n"
       if $DEBUG > 1;
 
-    $discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount
-                               && $conf->exists('discount-show-always'));
-
-    foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) {
-      if ( $_ && !$cust_bill_pkg->hidden ) {
-        $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
-        $_->{amount}      =~ s/^\-0\.00$/0.00/;
-        $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
-        push @b, { %$_ }
-          unless ( $_->{amount} == 0 && !$discount_show_always );
-        $_ = undef;
-      }
-    }
-
     foreach my $display ( grep { defined($section)
                                  ? $_->section eq $section
                                  : 1
@@ -4493,9 +4544,13 @@ sub _items_cust_bill_pkg {
 
         }
 
-        if ( ( $cust_bill_pkg->recur != 0  || $cust_bill_pkg->setup == 0 || 
-               ($discount_show_always && $cust_bill_pkg->recur == 0) ) &&
-             ( !$type || $type eq 'R' || $type eq 'U' )
+        if (    ( !$type || $type eq 'R' || $type eq 'U' )
+             && (
+                     $cust_bill_pkg->recur != 0
+                  || $cust_bill_pkg->setup == 0
+                  || $discount_show_always
+                  || $cust_bill_pkg->recur_show_zero
+                )
            )
         {
 
@@ -4509,7 +4564,8 @@ sub _items_cust_bill_pkg {
           $description .= " (" . time2str($date_format, $cust_bill_pkg->sdate).
                           " - ". time2str($date_format, $cust_bill_pkg->edate).
                           ")"
-            unless $conf->exists('disable_line_item_date_ranges');
+            unless $conf->exists('disable_line_item_date_ranges')
+                || $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1);
 
           my @d = ();
 
@@ -4641,21 +4697,38 @@ sub _items_cust_bill_pkg {
 
     }
 
+    $discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount
+                                && $conf->exists('discount-show-always'));
+
+    foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) {
+      if ( $_ && !$cust_bill_pkg->hidden ) {
+        $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
+        $_->{amount}      =~ s/^\-0\.00$/0.00/;
+        $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
+        push @b, { %$_ }
+          if $_->{amount} != 0
+          || $discount_show_always
+          || $cust_bill_pkg->recur_show_zero;
+        $_ = undef;
+      }
+    }
+
   }
 
+  #foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) {
+  #  if ( $_  ) {
+  #    $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
+  #    $_->{amount}      =~ s/^\-0\.00$/0.00/;
+  #    $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
+  #    push @b, { %$_ }
+  #      if $_->{amount} != 0
+  #      || $discount_show_always
+  #  }
+  #}
+
   warn "$me _items_cust_bill_pkg done considering cust_bill_pkgs\n"
     if $DEBUG > 1;
 
-  foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) {
-    if ( $_  ) {
-      $_->{amount}      = sprintf( "%.2f", $_->{amount} ),
-      $_->{amount}      =~ s/^\-0\.00$/0.00/;
-      $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ),
-      push @b, { %$_ }
-        unless ( $_->{amount} == 0 && !$discount_show_always );
-    }
-  }
-
   @b;
 
 }