invoice cosmetic improvements
[freeside.git] / FS / FS / cust_bill.pm
index 81b874a..0906c5d 100644 (file)
@@ -798,6 +798,9 @@ single agent) or an arrayref of agentnums.
 
 INVOICE_FROM, if specified, overrides the default email invoice From: address.
 
+AMOUNT, if specified, only sends the invoice if the total amount owed on this
+invoice and all older invoices is greater than the specified amount.
+
 =cut
 
 sub queueable_send {
@@ -828,6 +831,11 @@ sub send {
       ? shift
       : ( $self->_agent_invoice_from || $conf->config('invoice_from') );
 
+  my $balance_over = ( scalar(@_) && $_[0] !~ /^\s*$/ ) ? shift : 0;
+
+  return ''
+    unless $self->cust_main->total_owed_date($self->_date) > $balance_over;
+
   my @invoicing_list = $self->cust_main->invoicing_list;
 
   $self->email($template, $invoice_from)
@@ -1740,7 +1748,7 @@ sub print_generic {
                          );
   my $escape_function = $escape_functions{$format};
 
-  my %date_formats = ( 'latex'    => '%b, %o, %Y',
+  my %date_formats = ( 'latex'    => '%b %o, %Y',
                        'html'     => '%b %o, %Y',
                        'template' => '%s',
                      );
@@ -1785,13 +1793,17 @@ sub print_generic {
           );
   } elsif ( grep /\S/, $conf->config('company_address') ) {
 
-    $returnaddress = join( "\n", $conf->config('company_address') );
-
-    $returnaddress =
-      join( '\\*'."\n", map s/( {2,})/'~' x length($1)/eg,
-                            $conf->config('company_address')
-          )
-        if $format eq 'latex';
+    my $convert_map = $convert_maps{$format}{'returnaddress'};
+    $returnaddress = join( "\n", &$convert_map(
+                                   map { s/( {2,})/'~' x length($1)/eg;
+                                         s/$/\\\\\*/;
+                                         $_
+                                       }
+                                     ( $conf->config('company_name'),
+                                       $conf->config('company_address'),
+                                     )
+                                 )
+                     );
 
   } else {
 
@@ -1939,6 +1951,14 @@ sub print_generic {
                                             sprintf('%.2f', $pr_total),
                          };
 
+  my $taxtotal = 0;
+  my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
+                      'subtotal'    => $taxtotal }; # adjusted below
+
+  my $adjusttotal = 0;
+  my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
+                         'subtotal'    => 0 }; # adjusted below
+
   my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
   if ( $multisection ) {
     push @sections, $self->_items_sections;
@@ -1946,7 +1966,11 @@ sub print_generic {
     push @sections, { 'description' => '', 'subtotal' => '' };
   }
 
-  foreach my $line_item ( $self->_items_previous ) {
+  foreach my $line_item ( $conf->exists('disable_previous_balance') 
+                            ? ()
+                            : $self->_items_previous
+                        )
+  {
     my $detail = {
       ext_description => [],
     };
@@ -1971,7 +1995,7 @@ sub print_generic {
                ];
   }
   
-  if (@pr_cust_bill) {
+  if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
     push @buf, ['','-----------'];
     push @buf, [ 'Total Previous Balance',
                  $money_char. sprintf("%10.2f", $pr_total) ];
@@ -1992,6 +2016,7 @@ sub print_generic {
 
     my %options = ();
     $options{'section'} = $section if $multisection;
+    $options{'format'} = $format;
 
     foreach my $line_item ( $self->_items_pkg(%options) ) {
       my $detail = {
@@ -2002,9 +2027,7 @@ sub print_generic {
       $detail->{'section'} = $section;
       $detail->{'description'} = &$escape_function($line_item->{'description'});
       if ( exists $line_item->{'ext_description'} ) {
-        @{$detail->{'ext_description'}} = map {
-          &$escape_function($_);
-        } @{$line_item->{'ext_description'}};
+        @{$detail->{'ext_description'}} = @{$line_item->{'ext_description'}};
       }
       {
         my $money = $old_latex ? '' : $money_char;
@@ -2032,17 +2055,29 @@ sub print_generic {
   
   }
   
-  if ( $multisection ) {
-    unshift @sections, $previous_section;
+  if ( $multisection && !$conf->exists('disable_previous_balance') ) {
+    unshift @sections, $previous_section if $pr_total;
   }
 
-  my $taxtotal = 0;
   foreach my $tax ( $self->_items_tax ) {
     my $total = {};
     $total->{'total_item'} = &$escape_function($tax->{'description'});
     $taxtotal += $tax->{'amount'};
     $total->{'total_amount'} = $other_money_char. $tax->{'amount'};
-    push @total_items, $total;
+    if ( $multisection ) {
+      my $money = $old_latex ? '' : $money_char;
+      push @detail_items, {
+        ext_description => [],
+        ref          => '',
+        quantity     => '',
+        description  => &$escape_function($tax->{'description'}),
+        amount       => $money. $tax->{'amount'},
+        product_code => '',
+        section      => $tax_section,
+      };
+    }else{
+      push @total_items, $total;
+    }
     push @buf,[ $total->{'total_item'},
                 $money_char. sprintf("%10.2f", $total->{'total_amount'}),
               ];
@@ -2051,18 +2086,26 @@ sub print_generic {
   
   if ( $taxtotal ) {
     my $total = {};
+    $total->{'total_item'} = 'Sub-total';
+    $total->{'total_amount'} =
+      $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
+
     if ( $multisection ) {
-      $total->{'total_item'} = 'New charges sub-total';
+      $tax_section->{'subtotal'} = $other_money_char.
+                                   sprintf('%.2f', $taxtotal);
+      $tax_section->{'pretotal'} = 'New charges sub-total '.
+                                   $total->{'total_amount'};
+      push @sections, $tax_section if $taxtotal;
     }else{
-      $total->{'total_item'} = 'Sub-total';
+      unshift @total_items, $total;
     }
-    $total->{'total_amount'} =
-      $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
-    unshift @total_items, $total;
   }
   
   push @buf,['','-----------'];
-  push @buf,['Total New Charges',
+  push @buf,[( $conf->exists('disable_previous_balance') 
+               ? 'Total Charges'
+               : 'Total New Charges'
+             ),
              $money_char. sprintf("%10.2f",$self->charged) ];
   push @buf,['',''];
 
@@ -2070,66 +2113,123 @@ sub print_generic {
     my $total = {};
     $total->{'total_item'} = &$embolden_function('Total');
     $total->{'total_amount'} =
-    $total->{'total_amount'} =
       &$embolden_function(
-        $other_money_char.  sprintf('%.2f', $self->charged + $pr_total )
+        $other_money_char.
+        sprintf( '%.2f',
+                 $self->charged + ( $conf->exists('disable_previous_balance')
+                                    ? 0
+                                    : $pr_total
+                                  )
+               )
       );
-    push @total_items, $total;
+    if ( $multisection ) {
+      $adjust_section->{'pretotal'} = 'New charges total '.
+                                      $total->{'total_amount'};
+    }else{
+      push @total_items, $total;
+    }
     push @buf,['','-----------'];
     push @buf,['Total Charges',
-               $money_char. sprintf("%10.2f",$self->charged + $pr_total) ];
+               $money_char.
+               sprintf( '%10.2f', $self->charged +
+                                    ( $conf->exists('disable_previous_balance')
+                                        ? 0
+                                        : $pr_total
+                                    )
+                      )
+              ];
     push @buf,['',''];
   }
   
-
-  #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
+  unless ( $conf->exists('disable_previous_balance') ) {
+    #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
   
-  # credits
-  foreach my $credit ( $self->_items_credits ) {
-    my $total;
-    $total->{'total_item'} = &$escape_function($credit->{'description'});
-    #$credittotal
-    $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
-    push @total_items, $total;
-  }
+    # credits
+    foreach my $credit ( $self->_items_credits ) {
+      my $total;
+      $total->{'total_item'} = &$escape_function($credit->{'description'});
+      #$credittotal
+      $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
+      $adjusttotal += $credit->{'amount'};
+      if ( $multisection ) {
+        my $money = $old_latex ? '' : $money_char;
+        push @detail_items, {
+          ext_description => [],
+          ref          => '',
+          quantity     => '',
+          description  => &$escape_function($credit->{'description'}),
+          amount       => $money. $credit->{'amount'},
+          product_code => '',
+          section      => $adjust_section,
+        };
+      }else{
+        push @total_items, $total;
+      }
+    }
   
-  # credits (again)
-  foreach ( $self->cust_credited ) {
-
-    #something more elaborate if $_->amount ne $_->cust_credit->credited ?
-
-    my $reason = substr($_->cust_credit->reason,0,32);
-    $reason .= '...' if length($reason) < length($_->cust_credit->reason);
-    $reason = " ($reason) " if $reason;
-    push @buf,[
-      "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")".        $reason,
-      $money_char. sprintf("%10.2f",$_->amount)
-    ];
-  }
+    # credits (again)
+    foreach ( $self->cust_credited ) {
+  
+      #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+
+      my $reason = substr($_->cust_credit->reason,0,32);
+      $reason .= '...' if length($reason) < length($_->cust_credit->reason);
+      $reason = " ($reason) " if $reason;
+      push @buf,[
+        "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")".        $reason,
+        $money_char. sprintf("%10.2f",$_->amount)
+      ];
+    }
 
-  # payments
-  foreach my $payment ( $self->_items_payments ) {
-    my $total = {};
-    $total->{'total_item'} = &$escape_function($payment->{'description'});
-    #$paymenttotal
-    $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
-    push @total_items, $total;
-    push @buf, [ $payment->{'description'},
-                 $money_char. sprintf("%10.2f", $payment->{'amount'}),
-               ];
-  }
+    # payments
+    foreach my $payment ( $self->_items_payments ) {
+      my $total = {};
+      $total->{'total_item'} = &$escape_function($payment->{'description'});
+      #$paymenttotal
+      $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
+      $adjusttotal += $payment->{'amount'};
+      if ( $multisection ) {
+        my $money = $old_latex ? '' : $money_char;
+        push @detail_items, {
+          ext_description => [],
+          ref          => '',
+          quantity     => '',
+          description  => &$escape_function($payment->{'description'}),
+          amount       => $money. $payment->{'amount'},
+          product_code => '',
+          section      => $adjust_section,
+        };
+      }else{
+        push @total_items, $total;
+      }
+      push @buf, [ $payment->{'description'},
+                   $money_char. sprintf("%10.2f", $payment->{'amount'}),
+                 ];
+    }
   
-  { 
-    my $total;
-    $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
-    $total->{'total_amount'} =
-      &$embolden_function(
-        $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
-      );
-    push @total_items, $total;
-    push @buf,['','-----------'];
-    push @buf,[$self->balance_due_msg, $money_char. 
-      sprintf("%10.2f", $balance_due ) ];
+    if ( $multisection ) {
+      $adjust_section->{'subtotal'} = $other_money_char.
+                                      sprintf('%.2f', $adjusttotal);
+      push @sections, $adjust_section;
+    }
+
+    { 
+      my $total;
+      $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
+      $total->{'total_amount'} =
+        &$embolden_function(
+          $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
+        );
+      if ( $multisection ) {
+        $adjust_section->{'posttotal'} = $total->{'total_item'}. ' '.
+                                         $total->{'total_amount'};
+      }else{
+        push @total_items, $total;
+      }
+      push @buf,['','-----------'];
+      push @buf,[$self->balance_due_msg, $money_char. 
+        sprintf("%10.2f", $balance_due ) ];
+    }
   }
 
   $invoice_data{'logo_file'} = $params{'logo_file'}
@@ -2504,6 +2604,9 @@ sub _items_tax {
 sub _items_cust_bill_pkg {
   my $self = shift;
   my $cust_bill_pkg = shift;
+  my %opt = @_;
+  my $format = $opt{format} || '';
+  my $escape_function = $opt{escape_function} || sub { shift };
 
   my @b = ();
   foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
@@ -2518,7 +2621,10 @@ sub _items_cust_bill_pkg {
         my $description = $desc;
         $description .= ' Setup' if $cust_bill_pkg->recur != 0;
         my @d = $cust_pkg->h_labels_short($self->_date);
-        push @d, $cust_bill_pkg->details if $cust_bill_pkg->recur == 0;
+        push @d, $cust_bill_pkg->details( 'format'          => $format,
+                                          'escape_function' => $escape_function,
+                                        )
+          if $cust_bill_pkg->recur == 0;
         push @b, {
           description     => $description,
           #pkgpart         => $part_pkg->pkgpart,
@@ -2545,7 +2651,8 @@ sub _items_cust_bill_pkg {
             [ $cust_pkg->h_labels_short( $self->_date ),
                                          #$cust_bill_pkg->edate,
                                          #$cust_bill_pkg->sdate),
-              $cust_bill_pkg->details,
+              $cust_bill_pkg->details( 'format'          => $format,
+                                       'escape_function' => $escape_function),
             ],
         };
       }