From 947c1f964f1304242f8a6ffabacccf040f1d505e Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 5 Oct 2009 00:49:34 +0000 Subject: [PATCH] leading summary page invoices #RT5086 --- FS/FS/Conf.pm | 42 ++++++ FS/FS/Schema.pm | 3 + FS/FS/Upgrade.pm | 3 + FS/FS/cust_bill.pm | 129 +++++++++++++++-- FS/FS/cust_bill_pkg.pm | 5 +- FS/FS/cust_bill_pkg_display.pm | 7 +- FS/FS/cust_main.pm | 37 ++++- FS/FS/part_event/Action/cust_bill_fee_percent.pm | 3 + FS/FS/part_event/Action/fee.pm | 3 + FS/FS/pkg_category.pm | 35 ++++- conf/invoice_html | 167 ++++++++++++----------- conf/invoice_htmlsummary | 74 ++++++++++ conf/invoice_latex | 133 +++++++++--------- conf/invoice_latexsummary | 45 ++++++ httemplate/browse/pkg_category.html | 6 +- httemplate/edit/pkg_category.html | 2 + 16 files changed, 525 insertions(+), 169 deletions(-) create mode 100644 conf/invoice_htmlsummary create mode 100644 conf/invoice_latexsummary diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 3b37feacc..1353df898 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -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 billing documentation 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.', @@ -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.', @@ -1005,6 +1028,25 @@ worry that config_items is freeside-specific and icky. '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', diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 4af026c27..70f32502b 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -394,6 +394,8 @@ sub tables_hashref { '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', '', '', '', @@ -2073,6 +2075,7 @@ sub tables_hashref { 'columns' => [ 'categorynum', 'serial', '', '', '', '', 'categoryname', 'varchar', '', $char_d, '', '', + 'weight', 'int', 'NULL', '', '', '', 'disabled', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'categorynum', diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm index 7aecf45d8..e5cd5d32b 100644 --- a/FS/FS/Upgrade.pm +++ b/FS/FS/Upgrade.pm @@ -135,6 +135,9 @@ sub upgrade_data { #change recur_flat and enable_prorate 'part_pkg_option' => [], + #add weights to pkg_category + 'pkg_category' => [], + ; \%hash; diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 55faa36bc..eefcc80bc 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -1968,6 +1968,7 @@ sub print_generic { 'smallfooter' => sub { map "$_", @_ }, 'returnaddress' => sub { map "$_", @_ }, 'coupon' => sub { map "$_", @_ }, + 'summary' => sub { map "$_", @_ }, }, 'html' => { 'notes' => @@ -2001,6 +2002,7 @@ sub print_generic { } @_ }, 'coupon' => sub { "" }, + 'summary' => sub { "" }, }, 'template' => { 'notes' => @@ -2031,6 +2033,7 @@ sub print_generic { } @_ }, 'coupon' => sub { "" }, + 'summary' => sub { "" }, }, ); @@ -2147,6 +2150,14 @@ sub print_generic { '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) ){ @@ -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; + $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; + 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 )) { @@ -2257,6 +2276,7 @@ sub print_generic { 'template' => '', ); my $other_money_char = $other_money_chars{$format}; + $invoice_data{'dollar'} = $other_money_char; 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), + 'summarized' => $summarypage ? 'Y' : '', }; 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', - '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 ) { - push @sections, $self->_items_sections( $late_sections ); + push @sections, + $self->_items_sections( $late_sections, $summarypage, $escape_function ); }else{ push @sections, { 'description' => '', 'subtotal' => '' }; } @@ -2330,6 +2356,10 @@ sub print_generic { 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; @@ -2346,6 +2376,7 @@ sub print_generic { $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 = { @@ -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; } @@ -2555,7 +2589,11 @@ sub print_generic { $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'}. ' '. @@ -2574,6 +2612,49 @@ sub print_generic { 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 @@ -2850,21 +2931,30 @@ sub _date_pretty { sub _items_sections { my $self = shift; my $late = shift; + my $summarypage = shift; + my $escape = shift; my %s = (); my %l = (); + my %not_tax = (); 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) { + next if ( $display->summary && $summarypage ); + 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 ); @@ -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, - } } 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 $summary_page = $opt{summary_page} || ''; my @b = (); my ($s, $r, $u) = ( undef, undef, undef ); @@ -3018,6 +3122,7 @@ sub _items_cust_bill_pkg { ? $_->section eq $section : 1 } + grep { $_->summary || !$summary_page } $cust_bill_pkg->cust_bill_pkg_display ) { diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 9d7aae23c..c8c242eb0 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -361,7 +361,10 @@ sub part_pkg { 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; } } diff --git a/FS/FS/cust_bill_pkg_display.pm b/FS/FS/cust_bill_pkg_display.pm index 93c6e87d6..cf70cbd8f 100644 --- a/FS/FS/cust_bill_pkg_display.pm +++ b/FS/FS/cust_bill_pkg_display.pm @@ -52,7 +52,12 @@ sub section { 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; } } diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index b5a8f0d2e..16ab0ee0d 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2717,6 +2717,23 @@ sub bill { $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, @@ -2724,6 +2741,7 @@ sub bill { '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, }; @@ -2761,11 +2779,24 @@ sub bill { 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 ( { - '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 ) { diff --git a/FS/FS/part_event/Action/cust_bill_fee_percent.pm b/FS/FS/part_event/Action/cust_bill_fee_percent.pm index c85339398..b0397d421 100644 --- a/FS/FS/part_event/Action/cust_bill_fee_percent.pm +++ b/FS/FS/part_event/Action/cust_bill_fee_percent.pm @@ -31,6 +31,8 @@ sub do_action { #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 ); @@ -38,6 +40,7 @@ sub do_action { 'amount' => $amount, 'pkg' => $self->option('reason'), 'taxclass' => $self->option('taxclass'), + 'classnum' => $conf->config('finance_pkgclass'), 'setuptax' => $self->option('setuptax'), ); diff --git a/FS/FS/part_event/Action/fee.pm b/FS/FS/part_event/Action/fee.pm index c700301e2..163b4fa24 100644 --- a/FS/FS/part_event/Action/fee.pm +++ b/FS/FS/part_event/Action/fee.pm @@ -26,10 +26,13 @@ sub do_action { 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'), + 'classnum' => $conf->config('finance_pkgclass'), 'setuptax' => $self->option('setuptax'), ); diff --git a/FS/FS/pkg_category.pm b/FS/FS/pkg_category.pm index 69578c9cf..0beaf1c11 100644 --- a/FS/FS/pkg_category.pm +++ b/FS/FS/pkg_category.pm @@ -1,11 +1,13 @@ 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 ); +$DEBUG = 0; +$me = '[FS::pkg_category]'; =head1 NAME @@ -95,10 +97,39 @@ sub check { $self->ut_numbern('categorynum') or $self->ut_text('categoryname') + or $self->ut_snumber('weight') 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 diff --git a/conf/invoice_html b/conf/invoice_html index d5e24b812..73dcc2e42 100644 --- a/conf/invoice_html +++ b/conf/invoice_html @@ -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_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 } @@ -11,7 +13,7 @@ .invoice_totaldesc TD { font-size: 10pt; empty-cells: show } -'; } - } - if (scalar(@sections) > 1) { - my $style = 'border-top: 3px solid #000000;'. - 'border-bottom: 3px solid #000000;'; - $OUT .= - ''. - qq(). - qq('. - qq('. - '' - ; - } - + if (scalar(@sections) > 1) { + my $style = 'border-top: 3px solid #000000;'. + 'border-bottom: 3px solid #000000;'; + $OUT .= + ''. + qq(). + qq('. + qq('. + '' + ; + } + } if ($section->{'posttotal'}) { $OUT .= '
+'; } - $OUT .= '
@@ -82,10 +84,10 @@
- + <%= $summary %> <%= - foreach my $section ( @sections ) { - if ($section->{'pretotal'}) { + foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) { + if ($section->{'pretotal'} && !$summary) { $OUT .= '
'. '

'. @@ -95,91 +97,92 @@ '

'. '

'; } - $OUT .= '
'; - if ($section->{'description'}) { - $OUT .= - '

'. uc(substr($section->{'description'},0,1)). - ''. uc(substr($section->{'description'},1)). - ''. - '

'; - }else{ - $OUT .= - '

CHARGES'. - '

'; - } - $OUT .= '

'; + unless ($section->{'summarized'}) { + $OUT .= '
'; + if ($section->{'description'}) { + $OUT .= + '

'. uc(substr($section->{'description'},0,1)). + ''. uc(substr($section->{'description'},1)). + ''. + '

'; + }else{ + $OUT .= + '

CHARGES'. + '

'; + } + $OUT .= '

'; - $OUT .= - ''. - ''. - ''. - ''. - ( $unitprices - ? ''. - '' - : '' - ). - ''. - ''; - - my $lastref = 0; - foreach my $line ( - grep { ( scalar(@sections) > 1 - ? $section->{'description'} eq $_->{'section'}->{'description'} - : 1 - ) } - @detail_items ) - { $OUT .= - ''. - ''. - ''. + '
RefDescriptionUnit PriceQuantityAmount
'. - ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ). ''. $line->{'description'}. '
'. + ''. + ''. + ''. ( $unitprices - ? ''. - '' + ? ''. + '' : '' - ). - - ''. - '' - ; - $lastref = $line->{'ref'}; - if ( @{$line->{'ext_description'} } ) { - $OUT .= '' : '>'; - $OUT .= '
RefDescription'. $line->{'unit_amount'}. ''. $line->{'quantity'}. 'Unit PriceQuantity'. $line->{'amount'}. '
'; - foreach my $ext_desc ( @{$line->{'ext_description'} } ) { - $OUT .= - ''. - ''. - '' + ). + ''. + ''; + + my $lastref = 0; + foreach my $line ( + grep { ( scalar(@sections) > 1 + ? $section->{'description'} eq $_->{'section'}->{'description'} + : 1 + ) } + @detail_items ) + { + $OUT .= + ''. + ''. + ''. + ( $unitprices + ? ''. + '' + : '' + ). + + ''. + '' + ; + $lastref = $line->{'ref'}; + if ( @{$line->{'ext_description'} } ) { + $OUT .= '' : '>'; + $OUT .= '
/i ? '' : 'colspan=99' ). '>'. - '  '. $ext_desc. - '
Amount
'. + ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ). ''. $line->{'description'}. ''. $line->{'unit_amount'}. ''. $line->{'quantity'}. ''. $line->{'amount'}. '
'; + foreach my $ext_desc ( @{$line->{'ext_description'} } ) { + $OUT .= + ''. + ''. + '' + } + $OUT .= '
/i ? '' : 'colspan=99' ). '>'. + '  '. $ext_desc. + '
 ' : '>' ). - $section->{'description'}. ' Total ). - $section->{'subtotal'}. '
 ' : '>' ). + $section->{'description'}. ' Total ). + $section->{'subtotal'}. '
'; $OUT .= @@ -218,7 +221,7 @@


-<%= $notes %> +<%= length($summary) ? '' : $notes %>

<%= $footer %> diff --git a/conf/invoice_htmlsummary b/conf/invoice_htmlsummary new file mode 100644 index 000000000..b158478e1 --- /dev/null +++ b/conf/invoice_htmlsummary @@ -0,0 +1,74 @@ + + + + + +
+ + +
<%= $notes %>
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + <%= + 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 .= ''; + my $celltype = ($last == $section) ? 'th' : 'td'; + $OUT .= qq(<$celltype align="right">). $section->{'subtotal'}. ""; + } + %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +


Summary of Previous Balance and Payments
Previous Balance<%= $dollar.$true_previous_balance %>
Payments<%= $dollar.$balance_adjustments %>
Balance Outstanding<%= $dollar.sprintf('%.2f', $true_previous_balance - $balance_adjustments) %>


Summary of New Charges

'. ($section->{'description'} ? $section->{'description'} : 'Charges' ). '
New Charges Total<%= $dollar.$current_less_finance %>


Invoice Summary

Previous Past Due Charges<%= $dollar.sprintf('%.2f', $true_previous_balance - $balance_adjustments) %>
Finance charges on overdue amount<%= $dollar.$finance_amount %>
New Charges<%= $dollar.$current_less_finance %>
Total Amount Due<%= $dollar.sprintf('%.2f', $true_previous_balance + $current_charges - $balance_adjustments) %>

+
diff --git a/conf/invoice_latex b/conf/invoice_latex index 7facc19da..cf684ef75 100644 --- a/conf/invoice_latex +++ b/conf/invoice_latex @@ -19,7 +19,7 @@ \documentclass[letterpaper]{article} -\usepackage{fancyhdr,lastpage,ifthen,fslongtable,afterpage,caption,multirow,bigstrut} +\usepackage{fancyhdr,lastpage,ifthen,array,fslongtable,afterpage,caption,multirow,bigstrut} \usepackage{graphicx} % required for logo graphic \addtolength{\voffset}{-0.0cm} % top margin to top of header @@ -232,83 +232,86 @@ Terms: [@-- $terms --@]\\ \end{minipage}} \vspace{1.5cm} % +[@-- $summary --@] +% \section*{} [@-- - foreach my $section ( @sections ) { - if ($section->{'pretotal'}) { + foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) { + if ($section->{'pretotal'} && !$summary) { $OUT .= '\begin{flushright}'; $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\'; $OUT .= '\\end{flushright}'; } $OUT .= '\pagebreak' if $section{'post_total'}; - $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}'; - $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}' - if $coupon; - $OUT .= '\begin{longtable}{cllllllr}'; - $OUT .= '\caption*{ '; - $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges'; - $OUT .= '}\\\\'; - $OUT .= '\FShead'; - $OUT .= '\endfirsthead'; - $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\'; - $OUT .= '\FShead'; - $OUT .= '\endhead'; - $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\'; - $OUT .= '\endfoot'; - $OUT .= '\hline'; - - if (scalar(@sections) > 1) { - $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' . - '{' . $section->{'subtotal'} . '}' . "\n"; - } - - #if ($section == $sections[$#sections]) { - foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) { - $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' . - '{' . $line->{'total_amount'} . '}' . "\n"; + unless ($section->{'summarized'} ) { + $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}'; + $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}' + if $coupon; + $OUT .= '\begin{longtable}{cllllllr}'; + $OUT .= '\caption*{ '; + $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges'; + $OUT .= '}\\\\'; + $OUT .= '\FShead'; + $OUT .= '\endfirsthead'; + $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\'; + $OUT .= '\FShead'; + $OUT .= '\endhead'; + $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\'; + $OUT .= '\endfoot'; + $OUT .= '\hline'; + + if (scalar(@sections) > 1) { + $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' . + '{' . $section->{'subtotal'} . '}' . "\n"; } - #} - - $OUT .= '\hline'; - $OUT .= '\endlastfoot'; - - my $lastref = 0; - foreach my $line ( - grep { ( scalar( @sections ) > 1 - ? $section->{'description'} eq $_->{'section'}->{'description'} - : 1 - ) } - @detail_items ) - { - my $ext_description = $line->{'ext_description'}; + + #if ($section == $sections[$#sections]) { + foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) { + $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' . + '{' . $line->{'total_amount'} . '}' . "\n"; + } + #} + + $OUT .= '\hline'; + $OUT .= '\endlastfoot'; + + my $lastref = 0; + foreach my $line ( + grep { ( scalar( @sections ) > 1 + ? $section->{'description'} eq $_->{'section'}->{'description'} + : 1 + ) } + @detail_items ) + { + my $ext_description = $line->{'ext_description'}; - # Don't break-up small packages. - my $rowbreak = @$ext_description < 5 ? '*' : ''; + # Don't break-up small packages. + my $rowbreak = @$ext_description < 5 ? '*' : ''; - $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref); - $OUT .= '\FSdesc'. - '{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'. - '{' . $line->{'description'} . '}' . - '{' . ( $unitprices ? $line->{'unit_amount'} : '' ) . '}'. - '{' . ( $unitprices ? $line->{'quantity'} : '' ) . '}' . - '{' . $line->{'amount'} . "}${rowbreak}\n"; - $lastref = $line->{'ref'}; - - foreach my $ext_desc (@$ext_description) { - if ( $ext_desc !~ /[^\\]&/ ) { - $ext_desc = substr($ext_desc, 0, 80) . '...' - if (length($ext_desc) > 80); - $ext_desc = '\multicolumn{6}{l}{\small{~~~'. $ext_desc. '}}'; - }else{ - $ext_desc = "~~~$ext_desc"; + $OUT .= "\\hline\n" if ($line->{'ref'} && $line->{'ref'} ne $lastref); + $OUT .= '\FSdesc'. + '{' . ( $line->{'ref'} ne $lastref ? $line->{'ref'} : '' ) . '}'. + '{' . $line->{'description'} . '}' . + '{' . ( $unitprices ? $line->{'unit_amount'} : '' ) . '}'. + '{' . ( $unitprices ? $line->{'quantity'} : '' ) . '}' . + '{' . $line->{'amount'} . "}${rowbreak}\n"; + $lastref = $line->{'ref'}; + + foreach my $ext_desc (@$ext_description) { + if ( $ext_desc !~ /[^\\]&/ ) { + $ext_desc = substr($ext_desc, 0, 80) . '...' + if (length($ext_desc) > 80); + $ext_desc = '\multicolumn{6}{l}{\small{~~~'. $ext_desc. '}}'; + }else{ + $ext_desc = "~~~$ext_desc"; + } + $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n"; } - $OUT .= '\FSextdesc{' . $ext_desc . '}' . "${rowbreak}\n"; + } + $OUT .= '\end{longtable}'; } - - $OUT .= '\end{longtable}'; - if ($section->{'posttotal'}) { $OUT .= '\begin{flushright}'; $OUT .= '\normalfont\large\bfseries\textsc{'. $section->{'posttotal'}. '}\\\\'; @@ -319,7 +322,7 @@ Terms: [@-- $terms --@]\\ --@] \vfill \begin{minipage}[t]{\textwidth} - [@-- $notes --@] + [@-- length($summary) ? '' : $notes --@] [@-- $coupon ? '\ifthenelse{\equal{\thepage}{1}}{\rule{0pt}{\extracouponspace}}{}' : '' --@] \end{minipage} \end{document} diff --git a/conf/invoice_latexsummary b/conf/invoice_latexsummary new file mode 100644 index 000000000..a181ee435 --- /dev/null +++ b/conf/invoice_latexsummary @@ -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} diff --git a/httemplate/browse/pkg_category.html b/httemplate/browse/pkg_category.html index 20bf1a8df..e85c0dd9c 100644 --- a/httemplate/browse/pkg_category.html +++ b/httemplate/browse/pkg_category.html @@ -9,9 +9,9 @@ '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 ], ) %> diff --git a/httemplate/edit/pkg_category.html b/httemplate/edit/pkg_category.html index fdc8da638..a07dc5842 100644 --- a/httemplate/edit/pkg_category.html +++ b/httemplate/edit/pkg_category.html @@ -3,11 +3,13 @@ 'table' => 'pkg_category', 'fields' => [ 'categoryname', + 'weight', { field=>'disabled', type=>'checkbox', value=>'Y', }, ], 'labels' => { 'categorynum' => 'Category number', 'categoryname' => 'Category name', + 'weight' => 'Weight', 'disabled' => 'Disable category', }, 'viewall_dir' => 'browse', -- 2.11.0