leading summary page invoices #RT5086
authorjeff <jeff>
Mon, 5 Oct 2009 00:49:34 +0000 (00:49 +0000)
committerjeff <jeff>
Mon, 5 Oct 2009 00:49:34 +0000 (00:49 +0000)
16 files changed:
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/Upgrade.pm
FS/FS/cust_bill.pm
FS/FS/cust_bill_pkg.pm
FS/FS/cust_bill_pkg_display.pm
FS/FS/cust_main.pm
FS/FS/part_event/Action/cust_bill_fee_percent.pm
FS/FS/part_event/Action/fee.pm
FS/FS/pkg_category.pm
conf/invoice_html
conf/invoice_htmlsummary [new file with mode: 0644]
conf/invoice_latex
conf/invoice_latexsummary [new file with mode: 0644]
httemplate/browse/pkg_category.html
httemplate/edit/pkg_category.html

index 3b37fea..1353df8 100644 (file)
@@ -892,6 +892,13 @@ worry that config_items is freeside-specific and icky.
   },
 
   {
   },
 
   {
+    'key'         => 'invoice_usesummary',
+    'section'     => 'billing',
+    'description' => 'Indicates that html and latex invoices should be in summary style and make use of invoice_latexsummary.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'invoice_template',
     'section'     => 'billing',
     'description' => 'Text template file for invoices.  Used if no invoice_html template is defined, and also seen by users using non-HTML capable mail clients.  See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Plaintext_invoice_templates">billing documentation</a> for details.',
     'key'         => 'invoice_template',
     'section'     => 'billing',
     'description' => 'Text template file for invoices.  Used if no invoice_html template is defined, and also seen by users using non-HTML capable mail clients.  See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Plaintext_invoice_templates">billing documentation</a> for details.',
@@ -923,6 +930,14 @@ worry that config_items is freeside-specific and icky.
   },
 
   {
   },
 
   {
+    'key'         => 'invoice_htmlsummary',
+    'section'     => 'billing',
+    'description' => 'Summary initial page for HTML invoices.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'invoice_htmlreturnaddress',
     'section'     => 'billing',
     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
     'key'         => 'invoice_htmlreturnaddress',
     'section'     => 'billing',
     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
@@ -953,6 +968,14 @@ worry that config_items is freeside-specific and icky.
   },
 
   {
   },
 
   {
+    'key'         => 'invoice_latexsummary',
+    'section'     => 'billing',
+    'description' => 'Summary initial page for LaTeX typeset PostScript invoices.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'invoice_latexcoupon',
     'section'     => 'billing',
     'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
     'key'         => 'invoice_latexcoupon',
     'section'     => 'billing',
     'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
@@ -1005,6 +1028,25 @@ worry that config_items is freeside-specific and icky.
     'type'        => 'checkbox',
   },
 
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'finance_pkgclass',
+    'section'     => 'billing',
+    'description' => 'The package class for finance charges',
+    'type'        => 'select-sub',
+    'options_sub' => sub { require FS::Record;
+                           require FS::pkg_class;
+                           map { $_->classnum => $_->classname }
+                               FS::Record::qsearch('pkg_class', {} );
+                        },
+    'option_sub'  => sub { require FS::Record;
+                           require FS::pkg_class;
+                           my $pkg_class = FS::Record::qsearchs(
+                            'pkg_class', { 'classnum'=>shift }
+                          );
+                           $pkg_class ? $pkg_class->classname : '';
+                        },
+  },
+
   { 
     'key'         => 'separate_usage',
     'section'     => 'billing',
   { 
     'key'         => 'separate_usage',
     'section'     => 'billing',
index 4af026c..70f3250 100644 (file)
@@ -394,6 +394,8 @@ sub tables_hashref {
         'custnum',      'int',     '', '', '', '', 
         '_date',        @date_type,        '', '', 
         'charged',      @money_type,       '', '', 
         'custnum',      'int',     '', '', '', '', 
         '_date',        @date_type,        '', '', 
         'charged',      @money_type,       '', '', 
+        'previous_balance',   @money_typen, '', '',  #eventually not nullable
+        'billing_balance',    @money_typen, '', '',  #eventually not nullable
         'printed',      'int',     '', '', '', '', 
         'closed',      'char', 'NULL',  1, '', '', 
         'statementnum', 'int', 'NULL', '', '', '',
         'printed',      'int',     '', '', '', '', 
         'closed',      'char', 'NULL',  1, '', '', 
         'statementnum', 'int', 'NULL', '', '', '',
@@ -2073,6 +2075,7 @@ sub tables_hashref {
       'columns' => [
         'categorynum',   'serial',  '', '', '', '', 
         'categoryname',  'varchar', '', $char_d, '', '', 
       'columns' => [
         'categorynum',   'serial',  '', '', '', '', 
         'categoryname',  'varchar', '', $char_d, '', '', 
+        'weight',         'int', 'NULL',  '', '', '',
         'disabled',      'char', 'NULL',   1, '', '', 
       ],
       'primary_key' => 'categorynum',
         'disabled',      'char', 'NULL',   1, '', '', 
       ],
       'primary_key' => 'categorynum',
index 7aecf45..e5cd5d3 100644 (file)
@@ -135,6 +135,9 @@ sub upgrade_data {
     #change recur_flat and enable_prorate
     'part_pkg_option' => [],
 
     #change recur_flat and enable_prorate
     'part_pkg_option' => [],
 
+    #add weights to pkg_category
+    'pkg_category' => [],
+
   ;
 
   \%hash;
   ;
 
   \%hash;
index 55faa36..eefcc80 100644 (file)
@@ -1968,6 +1968,7 @@ sub print_generic {
                  'smallfooter'   => sub { map "$_", @_ },
                  'returnaddress' => sub { map "$_", @_ },
                  'coupon'        => sub { map "$_", @_ },
                  'smallfooter'   => sub { map "$_", @_ },
                  'returnaddress' => sub { map "$_", @_ },
                  'coupon'        => sub { map "$_", @_ },
+                 'summary'       => sub { map "$_", @_ },
                },
     'html'  => {
                  'notes' =>
                },
     'html'  => {
                  'notes' =>
@@ -2001,6 +2002,7 @@ sub print_generic {
                      }  @_
                    },
                  'coupon'        => sub { "" },
                      }  @_
                    },
                  'coupon'        => sub { "" },
+                 'summary'       => sub { "" },
                },
     'template' => {
                  'notes' =>
                },
     'template' => {
                  'notes' =>
@@ -2031,6 +2033,7 @@ sub print_generic {
                      }  @_
                    },
                  'coupon'        => sub { "" },
                      }  @_
                    },
                  'coupon'        => sub { "" },
+                 'summary'       => sub { "" },
                },
   );
 
                },
   );
 
@@ -2147,6 +2150,14 @@ sub print_generic {
     'unitprices'      => $conf->exists('invoice-unitprice'),
   );
 
     'unitprices'      => $conf->exists('invoice-unitprice'),
   );
 
+  $invoice_data{finance_section} = '';
+  if ( $conf->config('finance_pkgclass') ) {
+    my $pkg_class =
+      qsearchs('pkg_class', { classnum => $conf->config('finance_pkgclass') });
+    $invoice_data{finance_section} = $pkg_class->categoryname;
+  } 
+ $invoice_data{finance_amount} = '0.00';
+
   my $countrydefault = $conf->config('countrydefault') || 'US';
   my $prefix = $cust_main->has_ship_address ? 'ship_' : '';
   foreach ( qw( contact company address1 address2 city state zip country fax) ){
   my $countrydefault = $conf->config('countrydefault') || 'US';
   my $prefix = $cust_main->has_ship_address ? 'ship_' : '';
   foreach ( qw( contact company address1 address2 city state zip country fax) ){
@@ -2193,11 +2204,19 @@ sub print_generic {
 #  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
   #my $balance_due = $self->owed + $pr_total - $cr_total;
   my $balance_due = $self->owed + $pr_total;
 #  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
   #my $balance_due = $self->owed + $pr_total - $cr_total;
   my $balance_due = $self->owed + $pr_total;
+  $invoice_data{'true_previous_balance'} = sprintf("%.2f", $self->previous_balance);
+  $invoice_data{'balance_adjustments'} = sprintf("%.2f", $self->previous_balance - $self->billing_balance);
   $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
   $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
 
   my $agentnum = $self->cust_main->agentnum;
 
   $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
   $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
 
   my $agentnum = $self->cust_main->agentnum;
 
+  my $summarypage = '';
+  if ( $conf->exists('invoice_usesummary', $agentnum) ) {
+    $summarypage = 1;
+  }
+  $invoice_data{'summarypage'} = $summarypage;
+
   #do variable substitution in notes, footer, smallfooter
   foreach my $include (qw( notes footer smallfooter coupon )) {
 
   #do variable substitution in notes, footer, smallfooter
   foreach my $include (qw( notes footer smallfooter coupon )) {
 
@@ -2257,6 +2276,7 @@ sub print_generic {
                             'template' => '',
                           );
   my $other_money_char = $other_money_chars{$format};
                             'template' => '',
                           );
   my $other_money_char = $other_money_chars{$format};
+  $invoice_data{'dollar'} = $other_money_char;
 
   my @detail_items = ();
   my @total_items = ();
 
   my @detail_items = ();
   my @total_items = ();
@@ -2271,21 +2291,27 @@ sub print_generic {
   my $previous_section = { 'description' => 'Previous Charges',
                            'subtotal'    => $other_money_char.
                                             sprintf('%.2f', $pr_total),
   my $previous_section = { 'description' => 'Previous Charges',
                            'subtotal'    => $other_money_char.
                                             sprintf('%.2f', $pr_total),
+                           'summarized'  => $summarypage ? 'Y' : '',
                          };
 
   my $taxtotal = 0;
   my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
                          };
 
   my $taxtotal = 0;
   my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
-                      'subtotal'    => $taxtotal }; # adjusted below
+                      'subtotal'    => $taxtotal,   # adjusted below
+                      'summarized'  => $summarypage ? 'Y' : '',
+                    };
 
   my $adjusttotal = 0;
   my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
 
   my $adjusttotal = 0;
   my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
-                         'subtotal'    => 0 }; # adjusted below
+                         'subtotal'    => 0,   # adjusted below
+                         'summarized'  => $summarypage ? 'Y' : '',
+                       };
 
   my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
   my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
   my $late_sections = [];
   if ( $multisection ) {
 
   my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y';
   my $multisection = $conf->exists('invoice_sections', $cust_main->agentnum);
   my $late_sections = [];
   if ( $multisection ) {
-    push @sections, $self->_items_sections( $late_sections );
+    push @sections,
+      $self->_items_sections( $late_sections, $summarypage, $escape_function );
   }else{
     push @sections, { 'description' => '', 'subtotal' => '' };
   }
   }else{
     push @sections, { 'description' => '', 'subtotal' => '' };
   }
@@ -2330,6 +2356,10 @@ sub print_generic {
 
   foreach my $section (@sections, @$late_sections) {
 
 
   foreach my $section (@sections, @$late_sections) {
 
+    $invoice_data{finance_amount} = sprintf('%.2f', $section->{'subtotal'} )
+      if ( $invoice_data{finance_section} &&
+           $section->{'description'} eq $invoice_data{finance_section} );
+
     $section->{'subtotal'} = $other_money_char.
                              sprintf('%.2f', $section->{'subtotal'})
       if $multisection;
     $section->{'subtotal'} = $other_money_char.
                              sprintf('%.2f', $section->{'subtotal'})
       if $multisection;
@@ -2346,6 +2376,7 @@ sub print_generic {
     $options{'escape_function'} = $escape_function;
     $options{'format_function'} = sub { () } unless $unsquelched;
     $options{'unsquelched'} = $unsquelched;
     $options{'escape_function'} = $escape_function;
     $options{'format_function'} = sub { () } unless $unsquelched;
     $options{'unsquelched'} = $unsquelched;
+    $options{'summary_page'} = $summarypage;
 
     foreach my $line_item ( $self->_items_pkg(%options) ) {
       my $detail = {
 
     foreach my $line_item ( $self->_items_pkg(%options) ) {
       my $detail = {
@@ -2384,6 +2415,9 @@ sub print_generic {
   
   }
   
   
   }
   
+  $invoice_data{current_less_finance} =
+    sprintf('%.2f', $self->charged - $invoice_data{finance_amount} );
+
   if ( $multisection && !$conf->exists('disable_previous_balance') ) {
     unshift @sections, $previous_section if $pr_total;
   }
   if ( $multisection && !$conf->exists('disable_previous_balance') ) {
     unshift @sections, $previous_section if $pr_total;
   }
@@ -2555,7 +2589,11 @@ sub print_generic {
       $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
       $total->{'total_amount'} =
         &$embolden_function(
       $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
       $total->{'total_amount'} =
         &$embolden_function(
-          $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
+          $other_money_char. sprintf('%.2f', $summarypage 
+                                               ? $self->charged +
+                                                 $self->billing_balance
+                                               : $self->owed + $pr_total
+                                    )
         );
       if ( $multisection ) {
         $adjust_section->{'posttotal'} = $total->{'total_item'}. ' '.
         );
       if ( $multisection ) {
         $adjust_section->{'posttotal'} = $total->{'total_item'}. ' '.
@@ -2574,6 +2612,49 @@ sub print_generic {
       if $unsquelched;
   }
 
       if $unsquelched;
   }
 
+  my @includelist = ();
+  push @includelist, 'summary' if $summarypage;
+  foreach my $include ( @includelist ) {
+
+    my $inc_file = $conf->key_orbase("invoice_${format}$include", $template);
+    my @inc_src;
+
+    if ( length( $conf->config($inc_file, $agentnum) ) ) {
+
+      @inc_src = $conf->config($inc_file, $agentnum);
+
+    } else {
+
+      $inc_file = $conf->key_orbase("invoice_latex$include", $template);
+
+      my $convert_map = $convert_maps{$format}{$include};
+
+      @inc_src = map { s/\[\@--/$delimiters{$format}[0]/g;
+                       s/--\@\]/$delimiters{$format}[1]/g;
+                       $_;
+                     } 
+                 &$convert_map( $conf->config($inc_file, $agentnum) );
+
+    }
+
+    my $inc_tt = new Text::Template (
+      TYPE       => 'ARRAY',
+      SOURCE     => [ map "$_\n", @inc_src ],
+      DELIMITERS => $delimiters{$format},
+    ) or die "Can't create new Text::Template object: $Text::Template::ERROR";
+
+    unless ( $inc_tt->compile() ) {
+      my $error = "Can't compile $inc_file template: $Text::Template::ERROR\n";
+      warn $error. "Template:\n". join('', map "$_\n", @inc_src);
+      die $error;
+    }
+
+    $invoice_data{$include} = $inc_tt->fill_in( HASH => \%invoice_data );
+
+    $invoice_data{$include} =~ s/\n+$//
+      if ($format eq 'latex');
+  }
+
   $invoice_lines = 0;
   my $wasfunc = 0;
   foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
   $invoice_lines = 0;
   my $wasfunc = 0;
   foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
@@ -2850,21 +2931,30 @@ sub _date_pretty {
 sub _items_sections {
   my $self = shift;
   my $late = shift;
 sub _items_sections {
   my $self = shift;
   my $late = shift;
+  my $summarypage = shift;
+  my $escape = shift;
 
   my %s = ();
   my %l = ();
 
   my %s = ();
   my %l = ();
+  my %not_tax = ();
 
   foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
   {
 
 
   foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
   {
 
-    if ( $cust_bill_pkg->pkgnum > 0 ) {
+
       my $usage = $cust_bill_pkg->usage;
 
       foreach my $display ($cust_bill_pkg->cust_bill_pkg_display) {
       my $usage = $cust_bill_pkg->usage;
 
       foreach my $display ($cust_bill_pkg->cust_bill_pkg_display) {
+        next if ( $display->summary && $summarypage );
+
         my $desc = $display->section;
         my $type = $display->type;
 
         my $desc = $display->section;
         my $type = $display->type;
 
-        if ( $display->post_total ) {
+        if ( $cust_bill_pkg->pkgnum > 0 ) {
+          $not_tax{$desc} = 1;
+        }
+
+        if ( $display->post_total && !$summarypage ) {
           if (! $type || $type eq 'S') {
             $l{$desc} += $cust_bill_pkg->setup
               if ( $cust_bill_pkg->setup != 0 );
           if (! $type || $type eq 'S') {
             $l{$desc} += $cust_bill_pkg->setup
               if ( $cust_bill_pkg->setup != 0 );
@@ -2908,16 +2998,29 @@ sub _items_sections {
 
       }
 
 
       }
 
-    }
-
   }
 
   }
 
-  push @$late, map { { 'description' => $_,
+  my %cache = map { $_->categoryname => $_ }
+              qsearch( 'pkg_category', {disabled => 'Y'} );
+  $cache{$_->categoryname} = $_
+    foreach qsearch( 'pkg_category', {disabled => ''} );
+
+  push @$late, map { { 'description' => &{$escape}($_),
                        'subtotal'    => $l{$_},
                        'post_total'  => 1,
                        'subtotal'    => $l{$_},
                        'post_total'  => 1,
-                   } } sort keys %l;
-
-  map { {'description' => $_, 'subtotal' => $s{$_}} } sort keys %s;
+                   } }
+                 sort { $cache{$a}->weight <=> $cache{$b}->weight } keys %l;
+
+  map { { 'description' => &{$escape}($_),
+          'subtotal'    => $s{$_},
+          'summarized'  => $not_tax{$_} ? '' : 'Y',
+          'tax_section' => $not_tax{$_} ? '' : 'Y',
+      } }
+    sort { $cache{$a}->weight <=> $cache{$b}->weight }
+    ( $summarypage
+        ? ( grep { exists($s{$_}) || !$cache{$_}->disabled } keys %cache )
+        : ( keys %s )
+    );
 
 }
 
 
 }
 
@@ -2999,6 +3102,7 @@ sub _items_cust_bill_pkg {
   my $format_function = $opt{format_function} || '';
   my $unsquelched = $opt{unsquelched} || '';
   my $section = $opt{section}->{description} if $opt{section};
   my $format_function = $opt{format_function} || '';
   my $unsquelched = $opt{unsquelched} || '';
   my $section = $opt{section}->{description} if $opt{section};
+  my $summary_page = $opt{summary_page} || '';
 
   my @b = ();
   my ($s, $r, $u) = ( undef, undef, undef );
 
   my @b = ();
   my ($s, $r, $u) = ( undef, undef, undef );
@@ -3018,6 +3122,7 @@ sub _items_cust_bill_pkg {
                                  ? $_->section eq $section
                                  : 1
                                }
                                  ? $_->section eq $section
                                  : 1
                                }
+                          grep { $_->summary || !$summary_page }
                           $cust_bill_pkg->cust_bill_pkg_display
                         )
     {
                           $cust_bill_pkg->cust_bill_pkg_display
                         )
     {
index 9d7aae2..c8c242e 100644 (file)
@@ -361,7 +361,10 @@ sub part_pkg {
   if ( $self->pkgpart_override ) {
     qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart_override } );
   } else {
   if ( $self->pkgpart_override ) {
     qsearchs('part_pkg', { 'pkgpart' => $self->pkgpart_override } );
   } else {
-    $self->cust_pkg->part_pkg;
+    my $part_pkg;
+    my $cust_pkg = $self->cust_pkg;
+    $part_pkg = $cust_pkg->part_pkg if $cust_pkg;
+    $part_pkg;
   }
 }
 
   }
 }
 
index 93c6e87..cf70cbd 100644 (file)
@@ -52,7 +52,12 @@ sub section {
   if ( defined($value) ) {
     $self->setfield('section', $value);
   } else {
   if ( defined($value) ) {
     $self->setfield('section', $value);
   } else {
-    $self->getfield('section') || $self->cust_bill_pkg->part_pkg->categoryname;
+    my $section = $self->getfield('section');
+    unless ($section) {
+      my $part_pkg = $self->cust_bill_pkg->part_pkg;
+      $section = $part_pkg->categoryname if $part_pkg;
+    }
+    $section;
   }
 }
 
   }
 }
 
index b5a8f0d..16ab0ee 100644 (file)
@@ -2717,6 +2717,23 @@ sub bill {
     $tax = sprintf('%.2f', $tax );
     $total_setup = sprintf('%.2f', $total_setup+$tax );
   
     $tax = sprintf('%.2f', $tax );
     $total_setup = sprintf('%.2f', $total_setup+$tax );
   
+    my $pkg_category = qsearchs( 'pkg_category', { 'categoryname' => $taxname,
+                                                   'disabled'     => '',
+                                                 },
+                               );
+
+    my @display = ();
+    if ( $pkg_category and
+         $conf->config('invoice_latexsummary') ||
+         $conf->config('invoice_htmlsummary')
+       )
+    {
+
+      my %hash = (  'section' => $pkg_category->categoryname );
+      push @display, new FS::cust_bill_pkg_display { type => 'S', %hash };
+
+    }
+
     push @cust_bill_pkg, new FS::cust_bill_pkg {
       'pkgnum'   => 0,
       'setup'    => $tax,
     push @cust_bill_pkg, new FS::cust_bill_pkg {
       'pkgnum'   => 0,
       'setup'    => $tax,
@@ -2724,6 +2741,7 @@ sub bill {
       'sdate'    => '',
       'edate'    => '',
       'itemdesc' => $taxname,
       'sdate'    => '',
       'edate'    => '',
       'itemdesc' => $taxname,
+      'display'  => \@display,
       'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location,
       'cust_bill_pkg_tax_rate_location' => \@cust_bill_pkg_tax_rate_location,
     };
       'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location,
       'cust_bill_pkg_tax_rate_location' => \@cust_bill_pkg_tax_rate_location,
     };
@@ -2761,11 +2779,24 @@ sub bill {
 
   my $charged = sprintf('%.2f', $total_setup + $total_recur );
 
 
   my $charged = sprintf('%.2f', $total_setup + $total_recur );
 
+  my @cust_bill = $self->cust_bill;
+  my $balance = $self->balance;
+  my $previous_balance = scalar(@cust_bill)
+                           ?  $cust_bill[$#cust_bill]->billing_balance
+                           :  0;
+
+  $previous_balance += $cust_bill[$#cust_bill]->charged
+    if scalar(@cust_bill);
+  #my $balance_adjustments =
+  #  sprintf('%.2f', $balance - $prior_prior_balance - $prior_charged);
+
   #create the new invoice
   my $cust_bill = new FS::cust_bill ( {
   #create the new invoice
   my $cust_bill = new FS::cust_bill ( {
-    'custnum' => $self->custnum,
-    '_date'   => ( $invoice_time ),
-    'charged' => $charged,
+    'custnum'             => $self->custnum,
+    '_date'               => ( $invoice_time ),
+    'charged'             => $charged,
+    'billing_balance'     => $balance,
+    'previous_balance'    => $previous_balance,
   } );
   $error = $cust_bill->insert;
   if ( $error ) {
   } );
   $error = $cust_bill->insert;
   if ( $error ) {
index c853393..b0397d4 100644 (file)
@@ -31,6 +31,8 @@ sub do_action {
   #my $cust_main = $self->cust_main($cust_bill);
   my $cust_main = $cust_bill->cust_main;
 
   #my $cust_main = $self->cust_main($cust_bill);
   my $cust_main = $cust_bill->cust_main;
 
+  my $conf = new FS::Conf;
+
   my $amount =
     sprintf('%.2f', $cust_bill->owed * $self->option('percent') / 100 );
 
   my $amount =
     sprintf('%.2f', $cust_bill->owed * $self->option('percent') / 100 );
 
@@ -38,6 +40,7 @@ sub do_action {
     'amount'   => $amount,
     'pkg'      => $self->option('reason'),
     'taxclass' => $self->option('taxclass'),
     'amount'   => $amount,
     'pkg'      => $self->option('reason'),
     'taxclass' => $self->option('taxclass'),
+    'classnum'   => $conf->config('finance_pkgclass'),
     'setuptax' => $self->option('setuptax'),
   );
 
     'setuptax' => $self->option('setuptax'),
   );
 
index c700301..163b4fa 100644 (file)
@@ -26,10 +26,13 @@ sub do_action {
 
   my $cust_main = $self->cust_main($cust_object);
 
 
   my $cust_main = $self->cust_main($cust_object);
 
+  my $conf = new FS::Conf;
+
   my %charge = (
     'amount'   => $self->option('charge'),
     'pkg'      => $self->option('reason'),
     'taxclass' => $self->option('taxclass'),
   my %charge = (
     'amount'   => $self->option('charge'),
     'pkg'      => $self->option('reason'),
     'taxclass' => $self->option('taxclass'),
+    'classnum' => $conf->config('finance_pkgclass'),
     'setuptax' => $self->option('setuptax'),
   );
 
     'setuptax' => $self->option('setuptax'),
   );
 
index 69578c9..0beaf1c 100644 (file)
@@ -1,11 +1,13 @@
 package FS::pkg_category;
 
 use strict;
 package FS::pkg_category;
 
 use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch );
+use vars qw( @ISA $me $DEBUG );
+use FS::Record qw( qsearch dbh );
 use FS::part_pkg;
 
 @ISA = qw( FS::Record );
 use FS::part_pkg;
 
 @ISA = qw( FS::Record );
+$DEBUG = 0;
+$me = '[FS::pkg_category]';
 
 =head1 NAME
 
 
 =head1 NAME
 
@@ -95,10 +97,39 @@ sub check {
 
   $self->ut_numbern('categorynum')
   or $self->ut_text('categoryname')
 
   $self->ut_numbern('categorynum')
   or $self->ut_text('categoryname')
+  or $self->ut_snumber('weight')
   or $self->SUPER::check;
 
 }
 
   or $self->SUPER::check;
 
 }
 
+# _ upgrade_data
+#
+# Used by FS::Upgrade to migrate to a new database.
+#
+#
+
+sub _upgrade_data {
+  my ($class, %opts) = @_;
+  my $dbh = dbh;
+
+  warn "$me upgrading $class\n" if $DEBUG;
+
+  my @pkg_category =
+    qsearch('pkg_category', { weight => { op => '!=', value => '' } } );
+
+  unless( scalar(@pkg_category) ) {
+    my @pkg_category = qsearch('pkg_category', {} );
+    my $weight = 0;
+    foreach ( sort { $a->description cmp $b->description } @pkg_category ) {
+      $_->weight($weight);
+      my $error = $_->replace;
+      die "error setting pkg_category weight: $error\n" if $error;
+      $weight += 10;
+    }
+  }
+  '';
+}
+
 =back
 
 =head1 BUGS
 =back
 
 =head1 BUGS
index d5e24b8..73dcc2e 100644 (file)
@@ -3,6 +3,8 @@
 .invoice_header { font-size: 10pt }
 .invoice_headerright TH { border-top: 2px solid #000000; border-bottom: 2px solid #000000 }
 .invoice_headerright TD { font-size: 10pt; empty-cells: show }
 .invoice_header { font-size: 10pt }
 .invoice_headerright TH { border-top: 2px solid #000000; border-bottom: 2px solid #000000 }
 .invoice_headerright TD { font-size: 10pt; empty-cells: show }
+.invoice_summary TH { border-bottom: 2px solid #000000 }
+.invoice_summary TD { font-size: 10pt; empty-cells: show }
 .invoice_longtable table { cellspacing: none }
 .invoice_longtable TH { border-top: 2px solid #000000; border-bottom: 1px solid #000000; padding-left: none; padding-right: none; font-size: 10pt }
 .invoice_desc TD { border-top: 2px solid #000000; font-weight: bold; font-size: 10pt }
 .invoice_longtable table { cellspacing: none }
 .invoice_longtable TH { border-top: 2px solid #000000; border-bottom: 1px solid #000000; padding-left: none; padding-right: none; font-size: 10pt }
 .invoice_desc TD { border-top: 2px solid #000000; font-weight: bold; font-size: 10pt }
@@ -11,7 +13,7 @@
 .invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
 </STYLE>
 
 .invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
 </STYLE>
 
-<table class="invoice" bgcolor="#ffffff" WIDTH=625 CELLSPACING=8><tr><td>
+<table class="invoice" bgcolor="#ffffff" WIDTH=768 CELLSPACING=8><tr><td>
 
   <table class="invoice_header" width="100%">
     <tr>
 
   <table class="invoice_header" width="100%">
     <tr>
     </tr>
 
   </table>
     </tr>
 
   </table>
-
+  <%= $summary %>
   <%=
   <%=
-      foreach my $section ( @sections ) {
-        if ($section->{'pretotal'}) {
+      foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) {
+        if ($section->{'pretotal'} && !$summary) {
           $OUT .=
             '<table width="100%"><tr><td>'.
             '<p align="right"><b><font size="+1">'.
           $OUT .=
             '<table width="100%"><tr><td>'.
             '<p align="right"><b><font size="+1">'.
             '<p>'.
             '</td></tr></table>';
         }
             '<p>'.
             '</td></tr></table>';
         }
-        $OUT .= '<table><tr><td>';
-        if ($section->{'description'}) {
-          $OUT .=
-            '<p><b><font size="+1">'. uc(substr($section->{'description'},0,1)).
-            '</font><font size="+0">'. uc(substr($section->{'description'},1)).
-            '</font></b>'.
-            '<p>';
-        }else{
-          $OUT .=
-            '<p><b><font size="+1">C</font><font size="+0">HARGES</font></b>'.
-            '<p>';
-        }
-        $OUT .= '</td></tr></table>';
+        unless ($section->{'summarized'}) {
+          $OUT .= '<table><tr><td>';
+          if ($section->{'description'}) {
+            $OUT .=
+              '<p><b><font size="+1">'. uc(substr($section->{'description'},0,1)).
+              '</font><font size="+0">'. uc(substr($section->{'description'},1)).
+              '</font></b>'.
+              '<p>';
+          }else{
+            $OUT .=
+              '<p><b><font size="+1">C</font><font size="+0">HARGES</font></b>'.
+              '<p>';
+          }
+          $OUT .= '</td></tr></table>';
 
 
-        $OUT .=
-          '<table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">'.
-          '<tr>'.
-            '<th align="center">Ref</th>'.
-            '<th align="left">Description</th>'.
-            ( $unitprices 
-                ? '<th align="left">Unit Price</th>'.
-                  '<th align="left">Quantity</th>'
-                : ''
-              ).
-            '<th align="right">Amount</th>'.
-          '</tr>';
-
-        my $lastref = 0;
-        foreach my $line (
-          grep { ( scalar(@sections) > 1 
-                 ? $section->{'description'} eq $_->{'section'}->{'description'}
-                 : 1
-               ) }
-          @detail_items )
-        {
           $OUT .=
           $OUT .=
-            '<tr class="invoice_desc'.
-              ( ($line->{'ref'} && $line->{'ref'} ne $lastref) ? '' : '_more' ).
-              '">'.
-              '<td align="center">'. 
-              ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ). '</td>'.
-              '<td align="left">'. $line->{'description'}. '</td>'.
+            '<table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">'.
+            '<tr>'.
+              '<th align="center">Ref</th>'.
+              '<th align="left">Description</th>'.
               ( $unitprices 
               ( $unitprices 
-                  ? '<td align="left">'. $line->{'unit_amount'}. '</td>'.
-                    '<td align="left">'. $line->{'quantity'}. '</td>'
+                  ? '<th align="left">Unit Price</th>'.
+                    '<th align="left">Quantity</th>'
                   : ''
                   : ''
-              ).
-
-              '<td align="right">'. $line->{'amount'}. '</td>'.
-            '</tr>'
-          ;
-          $lastref = $line->{'ref'};
-          if ( @{$line->{'ext_description'} } ) {
-            $OUT .= '<tr class="invoice_extdesc"><td></td><td';
-            $OUT .= $unitprices ? ' colspan=3>' : '>';
-            $OUT .= '<table width="100%">';
-            foreach my $ext_desc ( @{$line->{'ext_description'} } ) {
-              $OUT .=
-                '<tr class="invoice_extdesc">'.
-                  '<td align="left" '.
-                       ( $ext_desc =~ /<\/?TD>/i ? '' : 'colspan=99' ). '>'.
-                    '&nbsp;&nbsp;'. $ext_desc.
-                  '</td>'.
-                '</tr>'
+                ).
+              '<th align="right">Amount</th>'.
+            '</tr>';
+
+          my $lastref = 0;
+          foreach my $line (
+            grep { ( scalar(@sections) > 1 
+                   ? $section->{'description'} eq $_->{'section'}->{'description'}
+                   : 1
+                 ) }
+            @detail_items )
+          {
+            $OUT .=
+              '<tr class="invoice_desc'.
+                ( ($line->{'ref'} && $line->{'ref'} ne $lastref) ? '' : '_more' ).
+                '">'.
+                '<td align="center">'. 
+                ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ). '</td>'.
+                '<td align="left">'. $line->{'description'}. '</td>'.
+                ( $unitprices 
+                    ? '<td align="left">'. $line->{'unit_amount'}. '</td>'.
+                      '<td align="left">'. $line->{'quantity'}. '</td>'
+                    : ''
+                ).
+
+                '<td align="right">'. $line->{'amount'}. '</td>'.
+              '</tr>'
+            ;
+            $lastref = $line->{'ref'};
+            if ( @{$line->{'ext_description'} } ) {
+              $OUT .= '<tr class="invoice_extdesc"><td></td><td';
+              $OUT .= $unitprices ? ' colspan=3>' : '>';
+              $OUT .= '<table width="100%">';
+              foreach my $ext_desc ( @{$line->{'ext_description'} } ) {
+                $OUT .=
+                  '<tr class="invoice_extdesc">'.
+                    '<td align="left" '.
+                         ( $ext_desc =~ /<\/?TD>/i ? '' : 'colspan=99' ). '>'.
+                      '&nbsp;&nbsp;'. $ext_desc.
+                    '</td>'.
+                  '</tr>'
+              }
+              $OUT .= '</table></td><td></td></tr>';
             }
             }
-            $OUT .= '</table></td><td></td></tr>';
           }
           }
-        }
 
 
 
 
-        if (scalar(@sections) > 1) {
-          my $style = 'border-top: 3px solid #000000;'.
-                      'border-bottom: 3px solid #000000;';
-          $OUT .=
-            '<tr class="invoice_totaldesc">'.
-              qq(<td style="$style">&nbsp;</td>).
-              qq(<td align="left" style="$style"). 
-                ( $unitprices ? ' colspan=3>' : '>' ).
-                $section->{'description'}. ' Total </td>'.
-              qq(<td align="right" style="$style">).
-                $section->{'subtotal'}. '</td>'.
-            '</tr>'
-        ;
-        }
-
+          if (scalar(@sections) > 1) {
+            my $style = 'border-top: 3px solid #000000;'.
+                        'border-bottom: 3px solid #000000;';
+            $OUT .=
+              '<tr class="invoice_totaldesc">'.
+                qq(<td style="$style">&nbsp;</td>).
+                qq(<td align="left" style="$style"). 
+                  ( $unitprices ? ' colspan=3>' : '>' ).
+                  $section->{'description'}. ' Total </td>'.
+                qq(<td align="right" style="$style">).
+                  $section->{'subtotal'}. '</td>'.
+              '</tr>'
+          ;
+          }
+        } 
         if ($section->{'posttotal'}) {
           $OUT .= '<tr><td align="right" colspan=5>';
           $OUT .=
         if ($section->{'posttotal'}) {
           $OUT .= '<tr><td align="right" colspan=5>';
           $OUT .=
   </table>
   <br><br>
 
   </table>
   <br><br>
 
-<%= $notes %>
+<%= length($summary) ? '' : $notes %>
 
   <hr NOSHADE SIZE=2 COLOR="#000000">
   <p align="center"><%= $footer %>
 
   <hr NOSHADE SIZE=2 COLOR="#000000">
   <p align="center"><%= $footer %>
diff --git a/conf/invoice_htmlsummary b/conf/invoice_htmlsummary
new file mode 100644 (file)
index 0000000..b158478
--- /dev/null
@@ -0,0 +1,74 @@
+<table>
+  <tr>
+    <td>
+      <table>
+        <tr><td><%= $notes %></td></tr>
+      </table>
+    </td>
+    <td>
+      <table class="invoice_summary">
+        <tr><th colspan=2><br></th></tr>
+        <tr>
+          <td><b><u><br>Summary of Previous Balance and Payments<br></u></b></td>
+          <td></td>
+        </tr>
+        <tr>
+          <td><b>Previous Balance</b></td>
+          <td align="right"><b><%= $dollar.$true_previous_balance %></b></td>
+        </tr>
+        <tr>
+          <td><b>Payments</b></td>
+          <th align="right"><b><%= $dollar.$balance_adjustments %></b></th>
+        </tr>
+        <tr>
+          <td><b>Balance Outstanding</b></td>
+          <td align="right"><b><%= $dollar.sprintf('%.2f', $true_previous_balance - $balance_adjustments) %></b></td>
+        </tr>
+        <tr><th colspan=2><br></th></tr>
+        <tr><td colspan=2><br></td></tr>
+        <tr>
+          <td><b><u>Summary of New Charges</u></b></td>
+          <td></td>
+        </tr>
+        <tr><td colspan=2><br></td></tr>
+        <%= 
+          my ($last) = grep { $_->{tax_section} || !$_->{summarized} and !($finance_section && $_->{'description'} eq $finance_section)} reverse @sections;
+          
+          foreach my $section ( grep { $_->{tax_section} || !$_->{summarized} and !($finance_section && $_->{'description'} eq $finance_section)} @sections ) {
+            $OUT .= '<tr><td><b>'. ($section->{'description'} ? $section->{'description'} : 'Charges' ). '</b></td>';
+            my $celltype = ($last == $section) ? 'th' : 'td';
+            $OUT .= qq(<$celltype align="right"><b>). $section->{'subtotal'}. "</b></$celltype></tr>";
+          }
+        %>
+        <tr>
+          <td><b>New Charges Total</b></td>
+          <td align="right"><b><%= $dollar.$current_less_finance %></b></td>
+        </tr>
+        <tr><th colspan=2><br></th></tr>
+        <tr><td colspan=2><br></td></tr>
+        <tr>
+          <td><b><u>Invoice Summary</u></b></td>
+          <td></td>
+        </tr>
+        <tr><td colspan=2><br></td></tr>
+        <tr>
+          <td><b>Previous Past Due Charges</b></td>
+          <td align="right"><b><%= $dollar.sprintf('%.2f', $true_previous_balance - $balance_adjustments) %></b></td>
+        </tr>
+        <tr>
+          <td><b>Finance charges on overdue amount</b></td>
+          <td align="right"><b><%= $dollar.$finance_amount %></b></td>
+        </tr>
+        <tr>
+          <td><b>New Charges</b></td>
+          <th align="right"><b><%= $dollar.$current_less_finance %></b></th>
+        </tr>
+        <tr>
+          <td><b>Total Amount Due</b></td>
+          <td align="right"><b><%= $dollar.sprintf('%.2f', $true_previous_balance + $current_charges - $balance_adjustments) %></b></td>
+        </tr>
+        <tr><th colspan=2><br></th></tr>
+      </table>
+    </td>
+  </tr>
+</table>
index 7facc19..cf684ef 100644 (file)
@@ -19,7 +19,7 @@
 \r
 \documentclass[letterpaper]{article}\r
 \r
 \r
 \documentclass[letterpaper]{article}\r
 \r
-\usepackage{fancyhdr,lastpage,ifthen,fslongtable,afterpage,caption,multirow,bigstrut}\r
+\usepackage{fancyhdr,lastpage,ifthen,array,fslongtable,afterpage,caption,multirow,bigstrut}\r
 \usepackage{graphicx}                  % required for logo graphic\r
 \r
 \addtolength{\voffset}{-0.0cm}         % top margin to top of header\r
 \usepackage{graphicx}                  % required for logo graphic\r
 \r
 \addtolength{\voffset}{-0.0cm}         % top margin to top of header\r
@@ -232,83 +232,86 @@ Terms: [@-- $terms --@]\\
 \end{minipage}}\r
 \vspace{1.5cm}\r
 %\r
 \end{minipage}}\r
 \vspace{1.5cm}\r
 %\r
+[@-- $summary --@]\r
+%\r
 \section*{}\r
 [@--\r
 \section*{}\r
 [@--\r
-  foreach my $section ( @sections ) {\r
-    if ($section->{'pretotal'}) {\r
+  foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) {\r
+    if ($section->{'pretotal'} && !$summary) {\r
       $OUT .= '\begin{flushright}';\r
       $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\';\r
       $OUT .= '\\end{flushright}';\r
     }\r
     $OUT .= '\pagebreak' if $section{'post_total'};\r
       $OUT .= '\begin{flushright}';\r
       $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\';\r
       $OUT .= '\\end{flushright}';\r
     }\r
     $OUT .= '\pagebreak' if $section{'post_total'};\r
-    $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}';\r
-    $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}'\r
-      if $coupon;\r
-    $OUT .= '\begin{longtable}{cllllllr}';\r
-    $OUT .= '\caption*{ ';\r
-    $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges';\r
-    $OUT .= '}\\\\';\r
-    $OUT .= '\FShead';\r
-    $OUT .= '\endfirsthead';\r
-    $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\';\r
-    $OUT .= '\FShead';\r
-    $OUT .= '\endhead';\r
-    $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\';\r
-    $OUT .= '\endfoot';\r
-    $OUT .= '\hline';\r
-\r
-    if (scalar(@sections) > 1) {\r
-      $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .\r
-              '{' . $section->{'subtotal'} . '}' . "\n";\r
-    }\r
-\r
-    #if ($section == $sections[$#sections]) {\r
-      foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) {\r
-        $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .\r
-                '{' . $line->{'total_amount'} . '}' . "\n";\r
+    unless ($section->{'summarized'} ) {\r
+      $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}';\r
+      $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}'\r
+        if $coupon;\r
+      $OUT .= '\begin{longtable}{cllllllr}';\r
+      $OUT .= '\caption*{ ';\r
+      $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges';\r
+      $OUT .= '}\\\\';\r
+      $OUT .= '\FShead';\r
+      $OUT .= '\endfirsthead';\r
+      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\';\r
+      $OUT .= '\FShead';\r
+      $OUT .= '\endhead';\r
+      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\';\r
+      $OUT .= '\endfoot';\r
+      $OUT .= '\hline';\r
+\r
+      if (scalar(@sections) > 1) {\r
+        $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .\r
+                '{' . $section->{'subtotal'} . '}' . "\n";\r
       }\r
       }\r
-    #}\r
-\r
-    $OUT .= '\hline';\r
-    $OUT .= '\endlastfoot';\r
-\r
-    my $lastref = 0;\r
-    foreach my $line (\r
-      grep { ( scalar( @sections ) > 1 \r
-             ? $section->{'description'} eq $_->{'section'}->{'description'}\r
-             : 1\r
-           ) }\r
-      @detail_items )\r
-    {\r
-      my $ext_description = $line->{'ext_description'};\r
+\r
+      #if ($section == $sections[$#sections]) {\r
+        foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) {\r
+          $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .\r
+                  '{' . $line->{'total_amount'} . '}' . "\n";\r
+        }\r
+      #}\r
+\r
+      $OUT .= '\hline';\r
+      $OUT .= '\endlastfoot';\r
+\r
+      my $lastref = 0;\r
+      foreach my $line (\r
+        grep { ( scalar( @sections ) > 1 \r
+               ? $section->{'description'} eq $_->{'section'}->{'description'}\r
+               : 1\r
+             ) }\r
+        @detail_items )\r
+      {\r
+        my $ext_description = $line->{'ext_description'};\r
   \r
   \r
-      # Don't break-up small packages.\r
-      my $rowbreak = @$ext_description < 5 ? '*' : '';\r
+        # Don't break-up small packages.\r
+        my $rowbreak = @$ext_description < 5 ? '*' : '';\r
   \r
   \r
-      $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref);\r
-      $OUT .= '\FSdesc'.\r
-              '{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'.\r
-              '{' . $line->{'description'} . '}' .\r
-              '{' . ( $unitprices ? $line->{'unit_amount'} : '' ) . '}'.\r
-              '{' . ( $unitprices ? $line->{'quantity'} : ''  ) . '}' .\r
-              '{' . $line->{'amount'} . "}${rowbreak}\n";\r
-      $lastref = $line->{'ref'};\r
-\r
-      foreach my $ext_desc (@$ext_description) {\r
-        if ( $ext_desc !~ /[^\\]&/ ) {\r
-          $ext_desc = substr($ext_desc, 0, 80) . '...'\r
-            if (length($ext_desc) > 80);\r
-          $ext_desc = '\multicolumn{6}{l}{\small{~~~'. $ext_desc. '}}';\r
-        }else{\r
-          $ext_desc = "~~~$ext_desc";\r
+        $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref);\r
+        $OUT .= '\FSdesc'.\r
+                '{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'.\r
+                '{' . $line->{'description'} . '}' .\r
+                '{' . ( $unitprices ? $line->{'unit_amount'} : '' ) . '}'.\r
+                '{' . ( $unitprices ? $line->{'quantity'} : ''  ) . '}' .\r
+                '{' . $line->{'amount'} . "}${rowbreak}\n";\r
+        $lastref = $line->{'ref'};\r
+\r
+        foreach my $ext_desc (@$ext_description) {\r
+          if ( $ext_desc !~ /[^\\]&/ ) {\r
+            $ext_desc = substr($ext_desc, 0, 80) . '...'\r
+              if (length($ext_desc) > 80);\r
+            $ext_desc = '\multicolumn{6}{l}{\small{~~~'. $ext_desc. '}}';\r
+          }else{\r
+            $ext_desc = "~~~$ext_desc";\r
+          }\r
+          $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n";\r
         }\r
         }\r
-        $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n";\r
+\r
       }\r
 \r
       }\r
 \r
+      $OUT .= '\end{longtable}';\r
     }\r
     }\r
-\r
-    $OUT .= '\end{longtable}';\r
-\r
     if ($section->{'posttotal'}) {\r
       $OUT .= '\begin{flushright}';\r
       $OUT .= '\normalfont\large\bfseries\textsc{'. $section->{'posttotal'}. '}\\\\';\r
     if ($section->{'posttotal'}) {\r
       $OUT .= '\begin{flushright}';\r
       $OUT .= '\normalfont\large\bfseries\textsc{'. $section->{'posttotal'}. '}\\\\';\r
@@ -319,7 +322,7 @@ Terms: [@-- $terms --@]\\
 --@]\r
 \vfill\r
 \begin{minipage}[t]{\textwidth}\r
 --@]\r
 \vfill\r
 \begin{minipage}[t]{\textwidth}\r
-  [@-- $notes --@]\r
+  [@-- length($summary) ? '' : $notes --@]\r
   [@-- $coupon ? '\ifthenelse{\equal{\thepage}{1}}{\rule{0pt}{\extracouponspace}}{}' : '' --@]\r
 \end{minipage}\r
 \end{document}\r
   [@-- $coupon ? '\ifthenelse{\equal{\thepage}{1}}{\rule{0pt}{\extracouponspace}}{}' : '' --@]\r
 \end{minipage}\r
 \end{document}\r
diff --git a/conf/invoice_latexsummary b/conf/invoice_latexsummary
new file mode 100644 (file)
index 0000000..a181ee4
--- /dev/null
@@ -0,0 +1,45 @@
+\begin{tabular}{ll}
+\begin{minipage}{6.4cm}
+\begin{tabular}{m{0cm}m{6.4cm}}
+\rule{0cm}{10cm}&\begin{minipage}{6cm}[@-- $notes --@]\end{minipage}\\
+\end{tabular}
+\end{minipage} &
+\rule{2cm}{0cm}
+\begin{minipage}{12.8cm}
+\begin{tabular}{lr}
+\hline
+&\\
+\textbf{\underline{Summary of Previous Balance and Payments}} & \\
+&\\
+\textbf{Previous Balance}&\textbf{\dollar[@-- $true_previous_balance --@]}\\
+\textbf{Payments}&\textbf{\dollar[@-- $balance_adjustments --@]}\\
+\cline{2-2}
+\textbf{Balance Outstanding}&\textbf{\dollar[@-- sprintf('%.2f', $true_previous_balance -$balance_adjustments) --@]}\\
+&\\
+\hline
+&\\
+\textbf{\underline{Summary of New Charges}} & \\
+&\\
+[@--
+  foreach my $section ( grep { $_->{tax_section} || !$_->{summarized} and !($finance_section && $_->{'description'} eq $finance_section)} @sections ) {
+    $OUT .= '\textbf{'. ($section->{'description'} ? $section->{'description'} : 'Charges' ). '}';
+    $OUT .= '&\textbf{'. $section->{'subtotal'}. '}\\\\';
+  }
+  $OUT .= '\cline{2-2}';
+--@]
+\textbf{New Charges Total}&\textbf{\dollar[@-- $current_less_finance --@]}\\
+&\\
+\hline
+&\\
+\textbf{\underline{Invoice Summary}} & \\
+& \\
+\textbf{Previous Past Due Charges}&\textbf{\dollar[@-- sprintf('%.2f', $true_previous_balance - $balance_adjustments) --@]}\\
+\textbf{Finance charges on overdue amount}&\textbf{\dollar[@-- $finance_amount --@]}\\
+\textbf{New Charges}&\textbf{\dollar[@-- $current_less_finance --@]}\\
+\cline{2-2}
+\textbf{Total Amount Due}&\textbf{\dollar[@-- sprintf('%.2f', $true_previous_balance + $current_charges - $balance_adjustments) --@]}\\
+&\\
+\hline
+\end{tabular}
+\end{minipage} \\
+\end{tabular}
index 20bf1a8..e85c0dd 100644 (file)
@@ -9,9 +9,9 @@
                                     'extra_sql' => 'ORDER BY categorynum',
                                   },
                  'count_query' => $count_query,
                                     'extra_sql' => 'ORDER BY categorynum',
                                   },
                  'count_query' => $count_query,
-                 'header'      => [ '#', 'Category' ],
-                 'fields'      => [ 'categorynum', 'categoryname' ],
-                 'links'       => [ $link, $link ],
+                 'header'      => [ '#', 'Category', 'Weight' ],
+                 'fields'      => [ 'categorynum', 'categoryname', 'weight' ],
+                 'links'       => [ $link, $link, $link ],
              )
 %>
 
              )
 %>
 
index fdc8da6..a07dc58 100644 (file)
@@ -3,11 +3,13 @@
               'table'  => 'pkg_category',
               'fields' => [
                             'categoryname',
               'table'  => 'pkg_category',
               'fields' => [
                             'categoryname',
+                            'weight',
                             { field=>'disabled', type=>'checkbox', value=>'Y', },
                           ],
               'labels' => { 
                             'categorynum'  => 'Category number',
                             'categoryname' => 'Category name',
                             { field=>'disabled', type=>'checkbox', value=>'Y', },
                           ],
               'labels' => { 
                             'categorynum'  => 'Category number',
                             'categoryname' => 'Category name',
+                            'weight'       => 'Weight',
                             'disabled'  => 'Disable category',
                           },
               'viewall_dir' => 'browse',
                             'disabled'  => 'Disable category',
                           },
               'viewall_dir' => 'browse',