use HTML::Entities;
use Locale::Country;
use FS::UID qw( datasrc );
-use FS::Misc qw( send_email send_fax generate_ps do_print );
+use FS::Misc qw( send_email send_fax generate_ps generate_pdf do_print );
use FS::Record qw( qsearch qsearchs dbh );
use FS::cust_main_Mixin;
use FS::cust_main;
sub cust_bill_pkg {
my $self = shift;
- qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
+ qsearch(
+ { 'table' => 'cust_bill_pkg',
+ 'hashref' => { 'invnum' => $self->invnum },
+ 'order_by' => 'ORDER BY billpkgnum',
+ }
+ );
}
=item cust_pkg
}
-=item generate_email PARAMHASH
+=item generate_email OPTION => VALUE ...
-PARAMHASH can contain the following:
+Options:
=over 4
-=item from => sender address, required
+=item from
+
+sender address, required
+
+=item tempate
+
+alternate template name, optional
-=item tempate => alternate template name, optional
+=item print_text
-=item print_text => text attachment arrayref, optional
+text attachment arrayref, optional
-=item subject => email subject, optional
+=item subject
+
+email subject, optional
=back
INVOICE_FROM, if specified, overrides the default email invoice From: address.
+AMOUNT, if specified, only sends the invoice if the total amount owed on this
+invoice and all older invoices is greater than the specified amount.
+
=cut
sub queueable_send {
? shift
: ( $self->_agent_invoice_from || $conf->config('invoice_from') );
+ my $balance_over = ( scalar(@_) && $_[0] !~ /^\s*$/ ) ? shift : 0;
+
+ return ''
+ unless $self->cust_main->total_owed_date($self->_date) > $balance_over;
+
my @invoicing_list = $self->cust_main->invoicing_list;
+ #$self->email_invoice($template, $invoice_from)
$self->email($template, $invoice_from)
if grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list;
+ #$self->print_invoice($template)
$self->print($template)
if grep { $_ eq 'POST' } @invoicing_list; #postal
- $self->fax($template)
+ $self->fax_invoice($template)
if grep { $_ eq 'FAX' } @invoicing_list; #fax
'';
}
+#sub email_invoice {
sub email {
my $self = shift;
my $template = scalar(@_) ? shift : '';
=cut
+#sub print_invoice {
sub print {
my $self = shift;
my $template = scalar(@_) ? shift : '';
do_print $self->lpr_data($template);
}
-=item fax [ TEMPLATENAME ]
+=item fax_invoice [ TEMPLATENAME ]
Faxes this invoice.
=cut
-sub fax {
+sub fax_invoice {
my $self = shift;
my $template = scalar(@_) ? shift : '';
cid -
+unsquelch_cdr - overrides any per customer cdr squelching when true
+
=cut
sub print_generic {
'footer' => sub { map "$_", @_ },
'smallfooter' => sub { map "$_", @_ },
'returnaddress' => sub { map "$_", @_ },
+ 'coupon' => sub { map "$_", @_ },
},
'html' => {
'notes' =>
$_;
} @_
},
+ 'coupon' => sub { "" },
},
'template' => {
'notes' =>
$_;
} @_
},
+ 'coupon' => sub { "" },
},
);
);
my $escape_function = $escape_functions{$format};
- my %date_formats = ( 'latex' => '%b, %o, %Y',
+ my %date_formats = ( 'latex' => '%b %o, %Y',
'html' => '%b %o, %Y',
'template' => '%s',
);
);
} elsif ( grep /\S/, $conf->config('company_address') ) {
- $returnaddress = join( "\n", $conf->config('company_address') );
-
- $returnaddress =
- join( '\\*'."\n", map s/( {2,})/'~' x length($1)/eg,
- $conf->config('company_address')
- )
- if $format eq 'latex';
+ my $convert_map = $convert_maps{$format}{'returnaddress'};
+ $returnaddress = join( "\n", &$convert_map(
+ map { s/( {2,})/'~' x length($1)/eg;
+ s/$/\\\\\*/;
+ $_
+ }
+ ( $conf->config('company_name'),
+ $conf->config('company_address'),
+ )
+ )
+ );
} else {
'date' => time2str($date_format, $self->_date),
'today' => time2str('%b %o, %Y', $today),
'agent' => &$escape_function($cust_main->agent->agent),
+ 'agent_custid' => &$escape_function($cust_main->agent_custid),
'payname' => &$escape_function($cust_main->payname),
'company' => &$escape_function($cust_main->company),
'address1' => &$escape_function($cust_main->address1),
'state' => &$escape_function($cust_main->state),
'zip' => &$escape_function($cust_main->zip),
'returnaddress' => $returnaddress,
- 'quantity' => 1,
+ #'quantity' => 1,
'terms' => $self->terms,
'template' => $params{'template'},
#'notes' => join("\n", $conf->config('invoice_latexnotes') ),
'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
'page' => 1,
'total_pages' => 1,
+ 'current_charges' => sprintf("%.2f", $self->charged),
+ 'duedate' => $self->due_date2str('%m/%d/%Y'), #date_format?
+ 'ship_enable' => $conf->exists('invoice-ship_address'),
+ 'unitprices' => $conf->exists('invoice-unitprice'),
);
+ 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 $method = $prefix.$_;
+ $invoice_data{"ship_$_"} = _latex_escape($cust_main->$method);
+ }
+ $invoice_data{'ship_country'} = ''
+ if ( $invoice_data{'ship_country'} eq $countrydefault );
+
$invoice_data{'cid'} = $params{'cid'}
if $params{'cid'};
- my $countrydefault = $conf->config('countrydefault') || 'US';
if ( $cust_main->country eq $countrydefault ) {
$invoice_data{'country'} = '';
} else {
push @address, ''
while (scalar(@address) < 5);
+ $invoice_data{'logo_file'} = $params{'logo_file'}
+ if $params{'logo_file'};
+
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+# 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{'previous_balance'} = sprintf("%.2f", $pr_total);
+ $invoice_data{'balance'} = sprintf("%.2f", $balance_due);
+
#do variable substitution in notes, footer, smallfooter
- foreach my $include (qw( notes footer smallfooter )) {
+ foreach my $include (qw( notes footer smallfooter coupon )) {
my $inc_file = $conf->key_orbase("invoice_${format}$include", $template);
my @inc_src;
? &$escape_function("Purchase Order #". $cust_main->payinfo)
: $nbsp;
- my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
-# 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 %money_chars = ( 'latex' => '',
'html' => $conf->config('money_char') || '$',
'template' => '',
sprintf('%.2f', $pr_total),
};
+ my $taxtotal = 0;
+ my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
+ 'subtotal' => $taxtotal }; # adjusted below
+
+ my $adjusttotal = 0;
+ my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
+ 'subtotal' => 0 }; # adjusted below
+
+ my $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;
+ push @sections, $self->_items_sections( $late_sections );
}else{
push @sections, { 'description' => '', 'subtotal' => '' };
}
- foreach my $line_item ( $self->_items_previous ) {
+ foreach my $line_item ( $conf->exists('disable_previous_balance')
+ ? ()
+ : $self->_items_previous
+ )
+ {
my $detail = {
ext_description => [],
};
&$escape_function($_);
} @{$line_item->{'ext_description'}};
}
- {
- my $money = $old_latex ? '' : $money_char;
- $detail->{'amount'} = $money. $line_item->{'amount'};
- }
+ $detail->{'amount'} = ( $old_latex ? '' : $money_char).
+ $line_item->{'amount'};
$detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
push @detail_items, $detail;
];
}
- if (@pr_cust_bill) {
+ if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
push @buf, ['','-----------'];
push @buf, [ 'Total Previous Balance',
$money_char. sprintf("%10.2f", $pr_total) ];
push @buf, ['',''];
}
- foreach my $section (@sections) {
+ foreach my $section (@sections, @$late_sections) {
$section->{'subtotal'} = $other_money_char.
sprintf('%.2f', $section->{'subtotal'})
my %options = ();
$options{'section'} = $section if $multisection;
+ $options{'format'} = $format;
+ $options{'escape_function'} = $escape_function;
+ $options{'format_function'} = sub { () } unless $unsquelched;
+ $options{'unsquelched'} = $unsquelched;
foreach my $line_item ( $self->_items_pkg(%options) ) {
my $detail = {
ext_description => [],
};
$detail->{'ref'} = $line_item->{'pkgnum'};
- $detail->{'quantity'} = 1;
+ $detail->{'quantity'} = $line_item->{'quantity'};
$detail->{'section'} = $section;
$detail->{'description'} = &$escape_function($line_item->{'description'});
if ( exists $line_item->{'ext_description'} ) {
- @{$detail->{'ext_description'}} = map {
- &$escape_function($_);
- } @{$line_item->{'ext_description'}};
- }
- {
- my $money = $old_latex ? '' : $money_char;
- $detail->{'amount'} = $money. $line_item->{'amount'};
+ @{$detail->{'ext_description'}} = @{$line_item->{'ext_description'}};
}
+ $detail->{'amount'} = ( $old_latex ? '' : $money_char ).
+ $line_item->{'amount'};
+ $detail->{'unit_amount'} = ( $old_latex ? '' : $money_char ).
+ $line_item->{'unit_amount'};
$detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
push @detail_items, $detail;
}
- if ( $multisection ) {
- unshift @sections, $previous_section;
+ if ( $multisection && !$conf->exists('disable_previous_balance') ) {
+ unshift @sections, $previous_section if $pr_total;
}
- my $taxtotal = 0;
foreach my $tax ( $self->_items_tax ) {
my $total = {};
$total->{'total_item'} = &$escape_function($tax->{'description'});
$taxtotal += $tax->{'amount'};
$total->{'total_amount'} = $other_money_char. $tax->{'amount'};
- push @total_items, $total;
+ if ( $multisection ) {
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => &$escape_function($tax->{'description'}),
+ amount => $money. $tax->{'amount'},
+ product_code => '',
+ section => $tax_section,
+ };
+ }else{
+ push @total_items, $total;
+ }
push @buf,[ $total->{'total_item'},
$money_char. sprintf("%10.2f", $total->{'total_amount'}),
];
if ( $taxtotal ) {
my $total = {};
+ $total->{'total_item'} = 'Sub-total';
+ $total->{'total_amount'} =
+ $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
+
if ( $multisection ) {
- $total->{'total_item'} = 'New charges sub-total';
+ $tax_section->{'subtotal'} = $other_money_char.
+ sprintf('%.2f', $taxtotal);
+ $tax_section->{'pretotal'} = 'New charges sub-total '.
+ $total->{'total_amount'};
+ push @sections, $tax_section if $taxtotal;
}else{
- $total->{'total_item'} = 'Sub-total';
+ unshift @total_items, $total;
}
- $total->{'total_amount'} =
- $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
- unshift @total_items, $total;
}
+ $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
push @buf,['','-----------'];
- push @buf,['Total New Charges',
+ push @buf,[( $conf->exists('disable_previous_balance')
+ ? 'Total Charges'
+ : 'Total New Charges'
+ ),
$money_char. sprintf("%10.2f",$self->charged) ];
push @buf,['',''];
{
my $total = {};
$total->{'total_item'} = &$embolden_function('Total');
- $total->{'total_amount'} =
$total->{'total_amount'} =
&$embolden_function(
- $other_money_char. sprintf('%.2f', $self->charged + $pr_total )
+ $other_money_char.
+ sprintf( '%.2f',
+ $self->charged + ( $conf->exists('disable_previous_balance')
+ ? 0
+ : $pr_total
+ )
+ )
);
- push @total_items, $total;
+ if ( $multisection ) {
+ $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char.
+ sprintf('%.2f', $self->charged );
+ }else{
+ push @total_items, $total;
+ }
push @buf,['','-----------'];
push @buf,['Total Charges',
- $money_char. sprintf("%10.2f",$self->charged + $pr_total) ];
+ $money_char.
+ sprintf( '%10.2f', $self->charged +
+ ( $conf->exists('disable_previous_balance')
+ ? 0
+ : $pr_total
+ )
+ )
+ ];
push @buf,['',''];
}
-
- #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
+ unless ( $conf->exists('disable_previous_balance') ) {
+ #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
- # credits
- foreach my $credit ( $self->_items_credits ) {
- my $total;
- $total->{'total_item'} = &$escape_function($credit->{'description'});
- #$credittotal
- $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
- push @total_items, $total;
- }
+ # credits
+ my $credittotal = 0;
+ foreach my $credit ( $self->_items_credits ) {
+ my $total;
+ $total->{'total_item'} = &$escape_function($credit->{'description'});
+ $credittotal += $credit->{'amount'};
+ $total->{'total_amount'} = '-'. $other_money_char. $credit->{'amount'};
+ $adjusttotal += $credit->{'amount'};
+ if ( $multisection ) {
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => &$escape_function($credit->{'description'}),
+ amount => $money. $credit->{'amount'},
+ product_code => '',
+ section => $adjust_section,
+ };
+ }else{
+ push @total_items, $total;
+ }
+ }
+ $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal);
- # credits (again)
- foreach ( $self->cust_credited ) {
+ # credits (again)
+ foreach ( $self->cust_credited ) {
+
+ #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+
+ my $reason = substr($_->cust_credit->reason,0,32);
+ $reason .= '...' if length($reason) < length($_->cust_credit->reason);
+ $reason = " ($reason) " if $reason;
+ push @buf,[
+ "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")". $reason,
+ $money_char. sprintf("%10.2f",$_->amount)
+ ];
+ }
- #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+ # payments
+ my $paymenttotal = 0;
+ foreach my $payment ( $self->_items_payments ) {
+ my $total = {};
+ $total->{'total_item'} = &$escape_function($payment->{'description'});
+ $paymenttotal += $payment->{'amount'};
+ $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
+ $adjusttotal += $payment->{'amount'};
+ if ( $multisection ) {
+ my $money = $old_latex ? '' : $money_char;
+ push @detail_items, {
+ ext_description => [],
+ ref => '',
+ quantity => '',
+ description => &$escape_function($payment->{'description'}),
+ amount => $money. $payment->{'amount'},
+ product_code => '',
+ section => $adjust_section,
+ };
+ }else{
+ push @total_items, $total;
+ }
+ push @buf, [ $payment->{'description'},
+ $money_char. sprintf("%10.2f", $payment->{'amount'}),
+ ];
+ }
+ $invoice_data{'paymenttotal'} = sprintf('%.2f', $paymenttotal);
+
+ if ( $multisection ) {
+ $adjust_section->{'subtotal'} = $other_money_char.
+ sprintf('%.2f', $adjusttotal);
+ push @sections, $adjust_section;
+ }
- my $reason = substr($_->cust_credit->reason,0,32);
- $reason .= '...' if length($reason) < length($_->cust_credit->reason);
- $reason = " ($reason) " if $reason;
- push @buf,[
- "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")". $reason,
- $money_char. sprintf("%10.2f",$_->amount)
- ];
+ {
+ my $total;
+ $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
+ $total->{'total_amount'} =
+ &$embolden_function(
+ $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
+ );
+ if ( $multisection ) {
+ $adjust_section->{'posttotal'} = $total->{'total_item'}. ' '.
+ $total->{'total_amount'};
+ }else{
+ push @total_items, $total;
+ }
+ push @buf,['','-----------'];
+ push @buf,[$self->balance_due_msg, $money_char.
+ sprintf("%10.2f", $balance_due ) ];
+ }
}
- # payments
- foreach my $payment ( $self->_items_payments ) {
- my $total = {};
- $total->{'total_item'} = &$escape_function($payment->{'description'});
- #$paymenttotal
- $total->{'total_amount'} = '-'. $other_money_char. $payment->{'amount'};
- push @total_items, $total;
- push @buf, [ $payment->{'description'},
- $money_char. sprintf("%10.2f", $payment->{'amount'}),
- ];
- }
-
- {
- my $total;
- $total->{'total_item'} = &$embolden_function($self->balance_due_msg);
- $total->{'total_amount'} =
- &$embolden_function(
- $other_money_char. sprintf('%.2f', $self->owed + $pr_total )
- );
- push @total_items, $total;
- push @buf,['','-----------'];
- push @buf,[$self->balance_due_msg, $money_char.
- sprintf("%10.2f", $balance_due ) ];
+ if ( $multisection ) {
+ push @sections, @$late_sections
+ if $unsquelched;
}
- $invoice_data{'logo_file'} = $params{'logo_file'}
- if $params{'logo_file'};
-
$invoice_lines = 0;
my $wasfunc = 0;
foreach ( grep /invoice_lines\(\d*\)/, @invoice_template ) { #kludgy
my ($file, $lfile) = $self->print_latex(@_);
my $ps = generate_ps($file);
unlink($lfile);
- $ps;
+ $ps;
}
=item print_pdf [ TIME [ , TEMPLATE ] ]
my $self = shift;
my ($file, $lfile) = $self->print_latex(@_);
+ my $pdf = generate_pdf($file);
+ unlink($lfile);
- my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
- chdir($dir);
-
- #system('pdflatex', "$file.tex");
- #system('pdflatex', "$file.tex");
- #! LaTeX Error: Unknown graphics extension: .eps.
-
- my $sfile = shell_quote $file;
-
- system("pslatex $sfile.tex >/dev/null 2>&1") == 0
- or die "pslatex $file.tex failed; see $file.log for details?\n";
- system("pslatex $sfile.tex >/dev/null 2>&1") == 0
- or die "pslatex $file.tex failed; see $file.log for details?\n";
-
- #system('dvipdf', "$file.dvi", "$file.pdf" );
- system(
- "dvips -q -t letter -f $sfile.dvi ".
- "| gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$sfile.pdf ".
- " -c save pop -"
- ) == 0
- or die "dvips | gs failed: $!";
-
- open(PDF, "<$file.pdf")
- or die "can't open $file.pdf: $! (error in LaTeX template?)\n";
-
- unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
- unlink("$lfile");
-
- my $pdf = '';
- while (<PDF>) {
- $pdf .= $_;
- }
-
- close PDF;
-
- return $pdf;
-
+ $pdf;
}
=item print_html [ TIME [ , TEMPLATE [ , CID ] ] ]
=cut
sub print_html {
- my( $self, $today, $template, $cid ) = @_;
+ my $self = shift;
+ my %params;
+ if ( ref $_[0] ) {
+ %params = %{ shift() };
+ }else{
+ $params{'time'} = shift;
+ $params{'template'} = shift;
+ $params{'cid'} = shift;
+ }
- my %params = ( 'format' => 'html' );
- $params{'time'} = $today if $today;
- $params{'template'} = $template if $template;
- $params{'cid'} = $cid if $cid;
+ $params{'format'} = 'html';
$self->print_generic( %params );
}
$msg;
}
+sub balance_due_date {
+ my $self = shift;
+ my $duedate = '';
+ if ( $conf->exists('invoice_default_terms')
+ && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $duedate = time2str("%m/%d/%Y", $self->_date + ($1*86400) );
+ }
+ $duedate;
+}
+
+=item invnum_date_pretty
+
+Returns a string with the invoice number and date, for example:
+"Invoice #54 (3/20/2008)"
+
+=cut
+
+sub invnum_date_pretty {
+ my $self = shift;
+ 'Invoice #'. $self->invnum. ' ('. time2str('%x', $self->_date). ')';
+}
+
sub _items_sections {
my $self = shift;
+ my $late = shift;
my %s = ();
- foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+ my %l = ();
+
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg )
+ {
if ( $cust_bill_pkg->pkgnum > 0 ) {
- my $desc = $cust_bill_pkg->part_pkg->classname;
+ my $desc = $cust_bill_pkg->section;
+ my $dup_desc = $cust_bill_pkg->duplicate_section;
+
+ if ($cust_bill_pkg->duplicate) {
+ $s{$dup_desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
- $s{$desc} += $cust_bill_pkg->setup
- if ( $cust_bill_pkg->setup != 0 );
+ $s{$dup_desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
- $s{$desc} += $cust_bill_pkg->recur
- if ( $cust_bill_pkg->recur != 0 );
+ if ( $cust_bill_pkg->post_total ) {
+ $l{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+
+ $l{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+
+ } else {
+ $s{$desc} += $cust_bill_pkg->setup
+ if ( $cust_bill_pkg->setup != 0 );
+
+ $s{$desc} += $cust_bill_pkg->recur
+ if ( $cust_bill_pkg->recur != 0 );
+ }
}
}
+ push @$late, map { { 'description' => $_,
+ 'subtotal' => $l{$_},
+ 'post_total' => 1,
+ } } sort keys %l;
+
map { {'description' => $_, 'subtotal' => $s{$_}} } sort keys %s;
}
sub _items {
my $self = shift;
- my @display = scalar(@_)
- ? @_
- : qw( _items_previous _items_pkg );
- #: qw( _items_pkg );
- #: qw( _items_previous _items_pkg _items_tax _items_credits _items_payments );
+
+ #my @display = scalar(@_)
+ # ? @_
+ # : qw( _items_previous _items_pkg );
+ # #: qw( _items_pkg );
+ # #: qw( _items_previous _items_pkg _items_tax _items_credits _items_payments );
+ my @display = qw( _items_previous _items_pkg );
+
my @b = ();
foreach my $display ( @display ) {
push @b, $self->$display(@_);
sub _items_pkg {
my $self = shift;
my %options = @_;
- my $section = delete $options{'section'};
+ my $section = $options{'section'};
+ my $desc = $section->{'description'};
my @cust_bill_pkg =
grep { $_->pkgnum &&
( defined($section)
- ? $_->part_pkg->classname eq $section->{'description'}
+ ? ( $_->section eq $desc || $_->duplicate_section eq $desc )
: 1
)
} $self->cust_bill_pkg;
sub _items_cust_bill_pkg {
my $self = shift;
my $cust_bill_pkg = shift;
+ my %opt = @_;
+
+ my $format = $opt{format} || '';
+ my $escape_function = $opt{escape_function} || sub { shift };
+ my $format_function = $opt{format_function} || '';
+ my $unsquelched = $opt{unsquelched} || '';
my @b = ();
- foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
+ my $last_pkgnum = '';
+ foreach my $cust_bill_pkg ( @$cust_bill_pkg )
+ {
my $cust_pkg = $cust_bill_pkg->cust_pkg;
my $desc = $cust_bill_pkg->desc;
+ my %details_opt = ( 'format' => $format,
+ 'escape_function' => $escape_function,
+ 'format_function' => $format_function,
+ );
+
if ( $cust_bill_pkg->pkgnum > 0 ) {
if ( $cust_bill_pkg->setup != 0 ) {
+
my $description = $desc;
$description .= ' Setup' if $cust_bill_pkg->recur != 0;
- my @d = $cust_pkg->h_labels_short($self->_date);
- push @d, $cust_bill_pkg->details if $cust_bill_pkg->recur == 0;
+
+ my @d = map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date);
+ push @d, $cust_bill_pkg->details(%details_opt)
+ if $cust_bill_pkg->recur == 0;
+
push @b, {
description => $description,
#pkgpart => $part_pkg->pkgpart,
pkgnum => $cust_bill_pkg->pkgnum,
amount => sprintf("%.2f", $cust_bill_pkg->setup),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitsetup),
+ quantity => $cust_bill_pkg->quantity,
ext_description => \@d,
};
+
+ $last_pkgnum = '';
+
}
if ( $cust_bill_pkg->recur != 0 ) {
- push @b, {
- description => $desc .
- ( $conf->exists('disable_line_item_date_ranges')
- ? ''
- : " (" .time2str("%x", $cust_bill_pkg->sdate).
- " - ".time2str("%x", $cust_bill_pkg->edate).")"
- ),
- #pkgpart => $part_pkg->pkgpart,
- pkgnum => $cust_bill_pkg->pkgnum,
- amount => sprintf("%.2f", $cust_bill_pkg->recur),
- ext_description =>
- #at least until cust_bill_pkg has "past" ranges in addition to
- #the "future" sdate/edate ones... see #3032
- [ $cust_pkg->h_labels_short( $self->_date ),
- #$cust_bill_pkg->edate,
- #$cust_bill_pkg->sdate),
- $cust_bill_pkg->details,
- ],
- };
+
+ my $is_summary =
+ ( $cust_bill_pkg->duplicate &&
+ $opt{section}->{description} ne $cust_bill_pkg->section
+ );
+ my $description = $is_summary ? "Usage charges" : $desc;
+
+ unless ( $conf->exists('disable_line_item_date_ranges') ) {
+ $description .= " (" . time2str("%x", $cust_bill_pkg->sdate).
+ " - ". time2str("%x", $cust_bill_pkg->edate). ")";
+ }
+
+ #at least until cust_bill_pkg has "past" ranges in addition to
+ #the "future" sdate/edate ones... see #3032
+ my @d = ();
+ push @d, map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date)
+ #$cust_bill_pkg->edate,
+ #$cust_bill_pkg->sdate),
+ unless ($cust_bill_pkg->pkgnum eq $last_pkgnum);
+
+ @d = () if ($cust_bill_pkg->itemdesc || $is_summary);
+ push @d, $cust_bill_pkg->details(%details_opt)
+ unless $is_summary;
+
+ if ($cust_bill_pkg->pkgnum eq $last_pkgnum) {
+
+ $b[$#b]->{amount} =
+ sprintf("%.2f", $b[$#b]->{amount} + $cust_bill_pkg->recur);
+ push @{$b[$#b]->{ext_description}}, @d;
+
+ }else{
+
+ push @b, {
+ description => $description,
+ #pkgpart => $part_pkg->pkgpart,
+ pkgnum => $cust_bill_pkg->pkgnum,
+ amount => sprintf("%.2f", $cust_bill_pkg->recur),
+ unit_amount => sprintf("%.2f", $cust_bill_pkg->unitrecur),
+ quantity => $cust_bill_pkg->quantity,
+ ext_description => \@d,
+ };
+
+ }
+
+ if ($conf->exists('separate_usage') && $cust_bill_pkg->type ne 'U') {
+ $last_pkgnum = '';
+ }else{
+ $last_pkgnum = $cust_bill_pkg->pkgnum;
+ }
}
} else { #pkgnum tax or one-shot line item (??)
};
}
+ $last_pkgnum = '';
+
}
}