diff options
Diffstat (limited to 'FS')
| -rw-r--r-- | FS/FS/Conf.pm | 42 | ||||
| -rw-r--r-- | FS/FS/Schema.pm | 3 | ||||
| -rw-r--r-- | FS/FS/Upgrade.pm | 3 | ||||
| -rw-r--r-- | FS/FS/cust_bill.pm | 129 | ||||
| -rw-r--r-- | FS/FS/cust_bill_pkg.pm | 5 | ||||
| -rw-r--r-- | FS/FS/cust_bill_pkg_display.pm | 7 | ||||
| -rw-r--r-- | FS/FS/cust_main.pm | 37 | ||||
| -rw-r--r-- | FS/FS/part_event/Action/cust_bill_fee_percent.pm | 3 | ||||
| -rw-r--r-- | FS/FS/part_event/Action/fee.pm | 3 | ||||
| -rw-r--r-- | FS/FS/pkg_category.pm | 35 | 
10 files changed, 248 insertions, 19 deletions
| 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 <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.', @@ -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 | 
