[freeside-commits] branch master updated. 84bd1a42758d23b5068f664bad602664954ddf83

Mark Wells mark at 420.am
Wed Feb 4 21:39:20 PST 2015


The branch, master has been updated
       via  84bd1a42758d23b5068f664bad602664954ddf83 (commit)
      from  553e304b45d3f8467c43754ad2e15d01758ac1fe (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 84bd1a42758d23b5068f664bad602664954ddf83
Author: Mark Wells <mark at freeside.biz>
Date:   Wed Feb 4 21:39:12 2015 -0800

    allow fees to be grouped with taxes on the invoice, #32223

diff --git a/FS/FS/Template_Mixin.pm b/FS/FS/Template_Mixin.pm
index d8e46c5..95d001e 100644
--- a/FS/FS/Template_Mixin.pm
+++ b/FS/FS/Template_Mixin.pm
@@ -7,7 +7,7 @@ use vars qw( $DEBUG $me
            );
              # but NOT $conf
 use vars qw( $invoice_lines @buf ); #yuck
-use List::Util qw(sum);
+use List::Util qw(sum first);
 use Date::Format;
 use Date::Language;
 use Text::Template 1.20;
@@ -908,29 +908,6 @@ sub print_generic {
   warn "$me generating sections\n"
     if $DEBUG > 1;
 
-  my $taxtotal = 0;
-  my $tax_section = { 'description' => $self->mt('Taxes, Surcharges, and Fees'),
-                      'subtotal'    => $taxtotal,   # adjusted below
-                      'tax_section' => 1,
-                    };
-  my $tax_weight = _pkg_category($tax_section->{description})
-                        ? _pkg_category($tax_section->{description})->weight
-                        : 0;
-  $tax_section->{'summarized'} = ''; #why? $summarypage && !$tax_weight ? 'Y' : '';
-  $tax_section->{'sort_weight'} = $tax_weight;
-
-  my $adjusttotal = 0;
-  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;
-  $adjust_section->{'summarized'} = ''; #why? $summarypage && !$adjust_weight ? 'Y' : '';
-  $adjust_section->{'sort_weight'} = $adjust_weight;
-
   my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
   my $multisection = $conf->exists($tc.'sections', $cust_main->agentnum) ||
                      $conf->exists($tc.'sections_by_location', $cust_main->agentnum);
@@ -971,6 +948,21 @@ sub print_generic {
     $previous_section = $default_section;
   }
 
+  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;
+  $adjust_section->{'summarized'} = ''; #why? $summarypage && !$adjust_weight ? 'Y' : '';
+  # Note: 'sort_weight' here is actually a flag telling whether there is an
+  # explicit package category for the adjust section. If so, certain behavior
+  # happens.
+  $adjust_section->{'sort_weight'} = $adjust_weight;
+
+
   if ( $multisection ) {
     ($extra_sections, $extra_lines) =
       $self->_items_extra_usage_sections($escape_function_nonbsp, $format)
@@ -1220,6 +1212,26 @@ sub print_generic {
   warn "$me adding taxes\n"
     if $DEBUG > 1;
 
+  # create a tax section if we don't yet have one
+  my $tax_description = 'Taxes, Surcharges, and Fees';
+  my $tax_section = first { $_->{description} eq $tax_description } @sections;
+  if (!$tax_section) {
+    $tax_section = { 'description' => $tax_description };
+    push @sections, $tax_section if $multisection;
+  }
+  $tax_section->{tax_section} = 1; # mark this section as containing taxes
+  # if this is an existing tax section, we're merging the tax items into it.
+  # grab the taxtotal that's already there, strip the money symbol if any
+  my $taxtotal = $tax_section->{'subtotal'} || 0;
+  $taxtotal =~ s/^\Q$other_money_char\E//;
+
+  # this does nothing
+  #my $tax_weight = _pkg_category($tax_section->{description})
+  #                      ? _pkg_category($tax_section->{description})->weight
+  #                      : 0;
+  #$tax_section->{'summarized'} = ''; #why? $summarypage && !$tax_weight ? 'Y' : '';
+  #$tax_section->{'sort_weight'} = $tax_weight;
+
   my @items_tax = $self->_items_tax;
   foreach my $tax ( @items_tax ) {
 
@@ -1262,14 +1274,20 @@ sub print_generic {
       $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
 
     if ( $multisection ) {
-      $tax_section->{'subtotal'} = $other_money_char.
-                                   sprintf('%.2f', $taxtotal);
-      $tax_section->{'pretotal'} = 'New charges sub-total '.
-                                   $total->{'total_amount'};
-      if ( $taxtotal ) {
-        push @sections, $tax_section;
-        push @summary_subtotals, $tax_section;
+      if ( $taxtotal > 0 ) {
+        $tax_section->{'subtotal'} = $other_money_char.
+                                     sprintf('%.2f', $taxtotal);
+        $tax_section->{'pretotal'} = 'New charges sub-total '.
+                                     $total->{'total_amount'};
+        $tax_section->{'description'} = $self->mt($tax_description);
+
+        # append it if it's not already there
+        if ( !grep $tax_section, @sections ) {
+          push @sections, $tax_section;
+          push @summary_subtotals, $tax_section;
+        }
       }
+
     } else {
       unshift @total_items, $total;
     }
@@ -1285,7 +1303,6 @@ sub print_generic {
              $money_char. sprintf("%10.2f",$self->charged) ];
   push @buf,['',''];
 
-
   ###
   # Totals
   ###
@@ -1361,7 +1378,6 @@ sub print_generic {
         $total->{'total_item'} = &$escape_function($credit->{'description'});
         $credittotal += $credit->{'amount'};
         $total->{'total_amount'} = $minus.$other_money_char.$credit->{'amount'};
-        $adjusttotal += $credit->{'amount'};
         if ( $multisection ) {
           push @detail_items, {
             ext_description => [],
@@ -1395,7 +1411,6 @@ sub print_generic {
         $total->{'total_item'} = &$escape_function($payment->{'description'});
         $paymenttotal += $payment->{'amount'};
         $total->{'total_amount'} = $minus.$other_money_char.$payment->{'amount'};
-        $adjusttotal += $payment->{'amount'};
         if ( $multisection ) {
           push @detail_items, {
             ext_description => [],
@@ -1417,7 +1432,10 @@ sub print_generic {
     
       if ( $multisection ) {
         $adjust_section->{'subtotal'} = $other_money_char.
-                                        sprintf('%.2f', $adjusttotal);
+                                        sprintf('%.2f', $credittotal + $paymenttotal);
+
+        #why this? because {sort_weight} forces the adjust_section to appear
+        #in @extra_sections instead of @sections. obviously.
         push @sections, $adjust_section
           unless $adjust_section->{sort_weight};
         # do not summarize; adjustments there are shown according to 
@@ -2794,11 +2812,16 @@ equivalent to
 
 $self->_items_cust_bill_pkg([ $self->cust_bill_pkg ])
 
-The only OPTIONS accepted is 'section', which may point to a hashref 
-with a key named 'condensed', which may have a true value.  If it 
-does, this method tries to merge identical items into items with 
-'quantity' equal to the number of items (not the sum of their 
-separate quantities, for some reason).
+OPTIONS are passed through to _items_cust_bill_pkg, and should include
+'format' and 'escape_function' at minimum.
+
+To produce items for a specific invoice section, OPTIONS should include
+'section', a hashref containing 'category' and/or 'locationnum' keys.
+
+'section' may also contain a key named 'condensed'. If this is present
+and has a true value, _items_pkg will try to merge identical items into items
+with 'quantity' equal to the number of items (not the sum of their separate
+quantities, for some reason).
 
 =cut
 
@@ -2866,13 +2889,14 @@ sub _items_fee {
     }
     foreach (sort keys(%base_invnums)) {
       next if $_ == $self->invnum;
+      # per convention, we must escape ext_description lines
       push @ext_desc,
         &{$escape_function}(
           $self->mt('from invoice #[_1] on [_2]', $_, $base_invnums{$_})
         );
     }
     my $desc = $part_fee->itemdesc_locale($self->cust_main->locale);
-    $desc = &{$escape_function}($desc);
+    # but not escape the base description line
 
     push @items,
       { feepart     => $cust_bill_pkg->feepart,
diff --git a/FS/FS/cust_bill_pkg_tax_location.pm b/FS/FS/cust_bill_pkg_tax_location.pm
index 468e6ae..2ffc273 100644
--- a/FS/FS/cust_bill_pkg_tax_location.pm
+++ b/FS/FS/cust_bill_pkg_tax_location.pm
@@ -122,7 +122,7 @@ sub check {
     || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
     || $self->ut_number('taxnum') #cust_bill_pkg/tax_rate key, based on taxtype
     || $self->ut_enum('taxtype', [ qw( FS::cust_main_county FS::tax_rate ) ] )
-    || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum' )
+    || $self->ut_number('pkgnum', 'cust_pkg', 'pkgnum' )
     || $self->ut_foreign_key('locationnum', 'cust_location', 'locationnum' )
     || $self->ut_money('amount')
     || $self->ut_foreign_key('taxable_billpkgnum', 'cust_bill_pkg', 'billpkgnum')
diff --git a/FS/FS/part_fee.pm b/FS/FS/part_fee.pm
index 370005c..ef14b4f 100644
--- a/FS/FS/part_fee.pm
+++ b/FS/FS/part_fee.pm
@@ -2,11 +2,11 @@ package FS::part_fee;
 
 use strict;
 use base qw( FS::o2m_Common FS::Record );
-use vars qw( $DEBUG );
 use FS::Record qw( qsearch qsearchs );
 use FS::cust_bill_pkg_display;
 
-$DEBUG = 0;
+our $DEBUG = 0;
+our $default_class;
 
 =head1 NAME
 
@@ -50,6 +50,9 @@ the invoice
 =item disabled - 'Y' if the fee is disabled
 
 =item classnum - the L<FS::pkg_class> that the fee belongs to, for reporting
+and placement on multisection invoices. Unlike packages, fees I<must> be 
+assigned to a class; they will default to class named "Fees", which belongs 
+to the same invoice section that normally contains taxes.
 
 =item taxable - 'Y' if this fee should be considered a taxable sale.  
 Currently, taxable fees will be treated like they exist at the customer's
@@ -130,6 +133,13 @@ sub check {
   $self->set('amount', 0) unless $self->amount;
   $self->set('percent', 0) unless $self->percent;
 
+  $default_class ||= qsearchs('pkg_class', { classname => 'Fees' })
+    or die "default package fee class not found; run freeside-upgrade to continue.\n";
+
+  if (!$self->get('classnum')) {
+    $self->set('classnum', $default_class->classnum);
+  }
+
   my $error = 
     $self->ut_numbern('feepart')
     || $self->ut_textn('comment')
diff --git a/FS/FS/pkg_category.pm b/FS/FS/pkg_category.pm
index adfadd7..c2361cc 100644
--- a/FS/FS/pkg_category.pm
+++ b/FS/FS/pkg_category.pm
@@ -3,7 +3,7 @@ use base qw( FS::category_Common );
 
 use strict;
 use vars qw( @ISA $me $DEBUG );
-use FS::Record qw( qsearch );
+use FS::Record qw( qsearch qsearchs );
 use FS::pkg_class;
 use FS::part_pkg;
 
@@ -145,6 +145,40 @@ sub _upgrade_data {
       $weight += 10;
     }
   }
+
+  # create default category for package fees
+  my $tax_category_name = 'Taxes, Surcharges, and Fees';
+  my $tax_category = qsearchs('pkg_category', 
+    { categoryname => $tax_category_name }
+  );
+  if (!$tax_category) {
+    $tax_category = FS::pkg_category->new({
+        categoryname => $tax_category_name,
+        weight       => 1000, # doesn't really matter
+    });
+    my $error = $tax_category->insert;
+    die "error creating tax category: $error\n" if $error;
+  }
+
+  my $fee_class_name = 'Fees'; # does not appear on invoice
+  my $fee_class = qsearchs('pkg_class', { classname => $fee_class_name });
+  if (!$fee_class) {
+    $fee_class = FS::pkg_class->new({
+        classname   => $fee_class_name,
+        categorynum => $tax_category->categorynum,
+    });
+    my $error = $fee_class->insert;
+    die "error creating fee class: $error\n" if $error;
+  }
+
+  # assign it to all fee defs that don't otherwise have a class
+  foreach my $part_fee (qsearch('part_fee', { classnum => '' })) {
+    $part_fee->set('classnum', $fee_class->classnum);
+    my $error = $part_fee->replace;
+    die "error assigning default class to fee def#".$part_fee->feepart .
+      ":$error\n" if $error;
+  }
+
   '';
 }
 
diff --git a/conf/invoice_html b/conf/invoice_html
index e9b0bdf..06ee775 100644
--- a/conf/invoice_html
+++ b/conf/invoice_html
@@ -166,15 +166,12 @@
                       &{$section->{description_generator}}($line);
             } else {
               my $class = 'invoice_desc_more';
-              if ( $line->{'ref'} and $line->{'ref'} ne $lastref ) {
+              if ( ($line->{'ref'} || 0) ne $lastref ) {
                 # then it's a new package (not a continuation)
                 $class = 'invoice_desc';
               }
               $OUT .= '<tr class="'.$class.'">
                        <td align="center">';
-              #if ( $line->{'ref'} ne $lastref ) {
-              #  $OUT .= $line->{'ref'};
-              #}
               $OUT .= '</td>
                        <td align="left">'. $line->{'description'}. '</td>';
               if ( $unitprices ) {
@@ -185,7 +182,7 @@
               $OUT .= '<td align="right">'. $line->{'amount'}. '</td>';
             }
             $OUT .= '</tr>';
-            $lastref = $line->{'ref'};
+            $lastref = $line->{'ref'} || 0;
             if ( @{$line->{'ext_description'} } ) {
               unless ( $section->{description_generator} ) {
                 $OUT .= '<tr class="invoice_extdesc"><td></td><td';
diff --git a/conf/invoice_latex b/conf/invoice_latex
index 822afcb..40ec703 100644
--- a/conf/invoice_latex
+++ b/conf/invoice_latex
@@ -327,12 +327,12 @@
         # Don't break-up small packages.
         my $rowbreak = @$ext_description < 5 ? '*' : '';
   
-        $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref);
+        $OUT .= "\\hline\n" if (($line->{'ref'} || 0) ne $lastref);
         if ($section->{description_generator}) {
           $OUT .= &{$section->{description_generator}}($line);
         } else {
           $OUT .= '\FSdesc'.
-                  '{}'. #'{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'.
+                  '{}'.
                   '{' . $line->{'description'} . '}' ;
           if ( $unitprices and length($line->{'unit_amount'}) ) {
             # then show the unit amount and quantity
@@ -345,7 +345,7 @@
           }
           $OUT .= '{\\dollar' . $line->{'amount'} . "}${rowbreak}\n";
         }
-        $lastref = $line->{'ref'};
+        $lastref = $line->{'ref'} || 0;
 
         foreach my $ext_desc (@$ext_description) {
           if ($section->{extended_description_generator}) {

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Template_Mixin.pm             |  106 +++++++++++++++++++++--------------
 FS/FS/cust_bill_pkg_tax_location.pm |    2 +-
 FS/FS/part_fee.pm                   |   14 ++++-
 FS/FS/pkg_category.pm               |   36 +++++++++++-
 conf/invoice_html                   |    7 +--
 conf/invoice_latex                  |    6 +-
 6 files changed, 118 insertions(+), 53 deletions(-)




More information about the freeside-commits mailing list