X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=acca765ba6a1f4469845120b132f85448c44d2fd;hp=7669479eb4f17455eef8224ca6252a3aaa01e314;hb=3b331dce1edea02e0b60cb2f763b1d5df9f58c36;hpb=982ded2d929bdcdfa72efa810273f3bc753bf036 diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 7669479eb..acca765ba 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -1,13 +1,15 @@ package FS::cust_bill; use strict; -use vars qw( @ISA $DEBUG $me $conf +use vars qw( @ISA $DEBUG $me $money_char $date_format $rdate_format $date_format_long ); + # but NOT $conf use vars qw( $invoice_lines @buf ); #yuck use Fcntl qw(:flock); #for spool_csv use Cwd; -use List::Util qw(min max); +use List::Util qw(min max sum); use Date::Format; +use Date::Language; use Text::Template 1.20; use File::Temp 0.14; use String::ShellQuote; @@ -41,6 +43,7 @@ use FS::bill_batch; use FS::cust_bill_batch; use FS::cust_bill_pay_pkg; use FS::cust_credit_bill_pkg; +use FS::L10N; @ISA = qw( FS::cust_main_Mixin FS::Record ); @@ -49,7 +52,7 @@ $me = '[FS::cust_bill]'; #ask FS::UID to run this stuff for us later FS::UID->install_callback( sub { - $conf = new FS::Conf; + my $conf = new FS::Conf; #global $money_char = $conf->config('money_char') || '$'; $date_format = $conf->config('date_format') || '%x'; #/YY $rdate_format = $conf->config('date_format') || '%m/%d/%Y'; #/YYYY @@ -364,6 +367,7 @@ cust_bill-default_agent_invid is set and it has a value, invnum otherwise. sub display_invnum { my $self = shift; + my $conf = $self->conf; if ( $conf->exists('cust_bill-default_agent_invid') && $self->agent_invid ){ return $self->agent_invid; } else { @@ -807,6 +811,7 @@ If there is an error, returns the error, otherwise returns false. sub apply_payments_and_credits { my( $self, %options ) = @_; + my $conf = $self->conf; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -955,6 +960,7 @@ sub generate_email { my $self = shift; my %args = @_; + my $conf = $self->conf; my $me = '[FS::cust_bill::generate_email]'; @@ -989,7 +995,7 @@ sub generate_email { my $alternative = build MIME::Entity 'Type' => 'multipart/alternative', - 'Encoding' => '7bit', + #'Encoding' => '7bit', 'Disposition' => 'inline' ; @@ -1017,8 +1023,8 @@ sub generate_email { $alternative->attach( 'Type' => 'text/plain', - #'Encoding' => 'quoted-printable', - 'Encoding' => '7bit', + 'Encoding' => 'quoted-printable', + #'Encoding' => '7bit', 'Data' => $data, 'Disposition' => 'inline', ); @@ -1240,6 +1246,7 @@ sub queueable_send { sub send { my $self = shift; + my $conf = $self->conf; my( $template, $invoice_from, $notice_name ); my $agentnums = ''; @@ -1329,6 +1336,7 @@ sub queueable_email { #sub email_invoice { sub email { my $self = shift; + my $conf = $self->conf; my( $template, $invoice_from, $notice_name, $no_coupon ); if ( ref($_[0]) ) { @@ -1378,6 +1386,7 @@ sub email { sub email_subject { my $self = shift; + my $conf = $self->conf; #my $template = scalar(@_) ? shift : ''; #per-template? @@ -1409,6 +1418,7 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub lpr_data { my $self = shift; + my $conf = $self->conf; my( $template, $notice_name ); if ( ref($_[0]) ) { my $opt = shift; @@ -1444,6 +1454,7 @@ I, if specified, overrides "Invoice" as the name of the sent docume #sub print_invoice { sub print { my $self = shift; + my $conf = $self->conf; my( $template, $notice_name ); if ( ref($_[0]) ) { my $opt = shift; @@ -1483,6 +1494,7 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub fax_invoice { my $self = shift; + my $conf = $self->conf; my( $template, $notice_name ); if ( ref($_[0]) ) { my $opt = shift; @@ -1538,6 +1550,7 @@ enabled) sub get_open_bill_batch { my $self = shift; + my $conf = $self->conf; my $hashref = { status => 'O' }; $hashref->{'agentnum'} = $conf->exists('invoice_print_pdf-spoolagent') ? $self->cust_main->agentnum @@ -1560,6 +1573,7 @@ TEMPLATENAME is unused? sub ftp_invoice { my $self = shift; + my $conf = $self->conf; my $template = scalar(@_) ? shift : ''; $self->send_csv( @@ -1582,6 +1596,7 @@ TEMPLATENAME is unused? sub spool_invoice { my $self = shift; + my $conf = $self->conf; my $template = scalar(@_) ? shift : ''; $self->spool_csv( @@ -2091,6 +2106,7 @@ sub realtime_lec { sub realtime_bop { my( $self, $method ) = (shift,shift); + my $conf = $self->conf; my %opt = @_; my $cust_main = $self->cust_main; @@ -2219,6 +2235,7 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub print_latex { my $self = shift; + my $conf = $self->conf; my( $today, $template, %opt ); if ( ref($_[0]) ) { %opt = %{ shift() }; @@ -2284,6 +2301,7 @@ sub print_latex { SUFFIX => '.tex', UNLINK => 0, ) or die "can't open temp file: $!\n"; + binmode($fh, ':utf8'); # language support print $fh join('', @filled_in ); close $fh; @@ -2351,8 +2369,8 @@ notice_name - overrides "Invoice" as the name of the sent document (templates fr # (alignment in text invoice?) problems to change them all to '%.2f' ? # yes: fixed width (dot matrix) text printing will be borked sub print_generic { - my( $self, %params ) = @_; + my $conf = $self->conf; my $today = $params{today} ? $params{today} : time; warn "$me print_generic called on $self with suffix $params{template}\n" if $DEBUG; @@ -2636,7 +2654,17 @@ sub print_generic { 'total_pages' => 1, ); - + + #localization + my $lh = FS::L10N->get_handle($cust_main->locale); + $invoice_data{'emt'} = sub { &$escape_function($self->mt(@_)) }; + my %info = FS::Locales->locale_info($cust_main->locale || 'en_US'); + # eval to avoid death for unimplemented languages + my $dh = eval { Date::Language->new($info{'name'}) } || + Date::Language->new(); # fall back to English + $invoice_data{'time2str'} = sub { $dh->time2str(@_) }; + # eventually use this date handle everywhere in here, too + my $min_sdate = 999999999999; my $max_edate = 0; foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { @@ -2713,11 +2741,30 @@ 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; + + # the customer's current balance as shown on the invoice before this one $invoice_data{'true_previous_balance'} = sprintf("%.2f", ($self->previous_balance || 0) ); + + # the change in balance from that invoice to this one $invoice_data{'balance_adjustments'} = sprintf("%.2f", ($self->previous_balance || 0) - ($self->billing_balance || 0) ); + + # the sum of amount owed on all previous invoices $invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total); + + # the sum of amount owed on all invoices $invoice_data{'balance'} = sprintf("%.2f", $balance_due); + # info from customer's last invoice before this one, for some + # summary formats + $invoice_data{'last_bill'} = {}; + my $last_bill = $pr_cust_bill[-1]; + if ( $last_bill ) { + $invoice_data{'last_bill'} = { + '_date' => $last_bill->_date, #unformatted + # all we need for now + }; + } + my $summarypage = ''; if ( $conf->exists('invoice_usesummary', $agentnum) ) { $summarypage = 1; @@ -2771,9 +2818,12 @@ sub print_generic { if ($format eq 'latex'); } - $invoice_data{'po_line'} = + # let invoices use either of these as needed + $invoice_data{'po_num'} = ($cust_main->payby eq 'BILL') + ? $cust_main->payinfo : ''; + $invoice_data{'po_line'} = ( $cust_main->payby eq 'BILL' && $cust_main->payinfo ) - ? &$escape_function("Purchase Order #". $cust_main->payinfo) + ? &$escape_function($self->mt("Purchase Order #").$cust_main->payinfo) : $nbsp; my %money_chars = ( 'latex' => '', @@ -2802,7 +2852,7 @@ sub print_generic { warn "$me generating sections\n" if $DEBUG > 1; - my $previous_section = { 'description' => 'Previous Charges', + my $previous_section = { 'description' => $self->mt('Previous Charges'), 'subtotal' => $other_money_char. sprintf('%.2f', $pr_total), 'summarized' => $summarypage ? 'Y' : '', @@ -2814,7 +2864,7 @@ sub print_generic { if $conf->exists('invoice_include_aging'); my $taxtotal = 0; - my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees', + my $tax_section = { 'description' => $self->mt('Taxes, Surcharges, and Fees'), 'subtotal' => $taxtotal, # adjusted below 'summarized' => $summarypage ? 'Y' : '', }; @@ -2826,7 +2876,8 @@ sub print_generic { my $adjusttotal = 0; - my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments', + my $adjust_section = { 'description' => + $self->mt('Credits, Payments, and Adjustments'), 'subtotal' => 0, # adjusted below 'summarized' => $summarypage ? 'Y' : '', }; @@ -2871,8 +2922,24 @@ sub print_generic { push @detail_items, @$accountcode_lines; } } - }else{ + } else {# not multisection + # make a default section push @sections, { 'description' => '', 'subtotal' => '' }; + # and calculate the finance charge total, since it won't get done otherwise. + # XXX possibly other totals? + # XXX possibly finance_pkgclass should not be used in this manner? + if ( $conf->exists('finance_pkgclass') ) { + my @finance_charges; + foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { + if ( grep { $_->section eq $invoice_data{finance_section} } + $cust_bill_pkg->cust_bill_pkg_display ) { + # I think these are always setup fees, but just to be sure... + push @finance_charges, $cust_bill_pkg->recur + $cust_bill_pkg->setup; + } + } + $invoice_data{finance_amount} = + sprintf('%.2f', sum( @finance_charges ) || 0); + } } unless ( $conf->exists('disable_previous_balance') @@ -2911,7 +2978,7 @@ sub print_generic { if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) { push @buf, ['','-----------']; - push @buf, [ 'Total Previous Balance', + push @buf, [ $self->mt('Total Previous Balance'), $money_char. sprintf("%10.2f", $pr_total) ]; push @buf, ['','']; } @@ -2997,6 +3064,9 @@ sub print_generic { $detail->{'unit_amount'} = ( $old_latex ? '' : $money_char ). $line_item->{'unit_amount'}; $detail->{'product_code'} = $line_item->{'pkgpart'} || 'N/A'; + + $detail->{'sdate'} = $line_item->{'sdate'}; + $detail->{'edate'} = $line_item->{'edate'}; push @detail_items, $detail; push @buf, ( [ $detail->{'description'}, @@ -3009,7 +3079,7 @@ sub print_generic { if ( $section->{'description'} ) { push @buf, ( ['','-----------'], [ $section->{'description'}. ' sub-total', - $money_char. sprintf("%10.2f", $section->{'subtotal'}) + $section->{'subtotal'} # already formatted this ], [ '', '' ], [ '', '' ], @@ -3067,7 +3137,7 @@ sub print_generic { if ( $taxtotal ) { my $total = {}; - $total->{'total_item'} = 'Sub-total'; + $total->{'total_item'} = $self->mt('Sub-total'); $total->{'total_amount'} = $other_money_char. sprintf('%.2f', $self->charged - $taxtotal ); @@ -3084,7 +3154,8 @@ sub print_generic { $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal); push @buf,['','-----------']; - push @buf,[( $conf->exists('disable_previous_balance') + push @buf,[$self->mt( + $conf->exists('disable_previous_balance') ? 'Total Charges' : 'Total New Charges' ), @@ -3103,16 +3174,16 @@ sub print_generic { ? 0 : $pr_total ); - $total->{'total_item'} = &$embolden_function($item); + $total->{'total_item'} = &$embolden_function($self->mt($item)); $total->{'total_amount'} = &$embolden_function( $other_money_char. sprintf( '%.2f', $amount ) ); if ( $multisection ) { if ( $adjust_section->{'sort_weight'} ) { - $adjust_section->{'posttotal'} = 'Balance Forward '. $other_money_char. - sprintf("%.2f", ($self->billing_balance || 0) ); + $adjust_section->{'posttotal'} = $self->mt('Balance Forward').' '. + $other_money_char. sprintf("%.2f", ($self->billing_balance || 0) ); } else { - $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char. - sprintf('%.2f', $self->charged ); + $adjust_section->{'pretotal'} = $self->mt('New charges total').' '. + $other_money_char. sprintf('%.2f', $self->charged ); } }else{ push @total_items, $total; @@ -3317,28 +3388,28 @@ sub print_generic { } #setup subroutine for the template - sub FS::cust_bill::_template::invoice_lines { - my $lines = shift || scalar(@FS::cust_bill::_template::buf); + #sub FS::cust_bill::_template::invoice_lines { # good god, no + $invoice_data{invoice_lines} = sub { # much better + my $lines = shift || scalar(@buf); map { - scalar(@FS::cust_bill::_template::buf) - ? shift @FS::cust_bill::_template::buf + scalar(@buf) + ? shift @buf : [ '', '' ]; } ( 1 .. $lines ); - } + }; my $lines; my @collect; while (@buf) { push @collect, split("\n", - $text_template->fill_in( HASH => \%invoice_data, - PACKAGE => 'FS::cust_bill::_template' - ) + $text_template->fill_in( HASH => \%invoice_data ) ); - $FS::cust_bill::_template::page++; + $invoice_data{'page'}++; } map "$_\n", @collect; }else{ + # this is where we actually create the invoice warn "filling in template for invoice ". $self->invnum. "\n" if $DEBUG; warn join("\n", map " $_ => ". $invoice_data{$_}, keys %invoice_data). "\n" @@ -3547,6 +3618,7 @@ sub _translate_old_latex_format { sub terms { my $self = shift; + my $conf = $self->conf; #check for an invoice-specific override return $self->invoice_terms if $self->invoice_terms; @@ -3575,10 +3647,11 @@ sub due_date2str { sub balance_due_msg { my $self = shift; - my $msg = 'Balance Due'; + my $msg = $self->mt('Balance Due'); return $msg unless $self->terms; if ( $self->due_date ) { - $msg .= ' - Please pay by '. $self->due_date2str($date_format); + $msg .= ' - ' . $self->mt('Please pay by'). ' '. + $self->due_date2str($date_format); } elsif ( $self->terms ) { $msg .= ' - '. $self->terms; } @@ -3587,6 +3660,7 @@ sub balance_due_msg { sub balance_due_date { my $self = shift; + my $conf = $self->conf; my $duedate = ''; if ( $conf->exists('invoice_default_terms') && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) { @@ -3595,7 +3669,10 @@ sub balance_due_date { $duedate; } -sub credit_balance_msg { 'Credit Balance Remaining' } +sub credit_balance_msg { + my $self = shift; + $self->mt('Credit Balance Remaining') +} =item invnum_date_pretty @@ -3606,7 +3683,7 @@ Returns a string with the invoice number and date, for example: sub invnum_date_pretty { my $self = shift; - 'Invoice #'. $self->invnum. ' ('. $self->_date_pretty. ')'; + $self->mt('Invoice #'). $self->invnum. ' ('. $self->_date_pretty. ')'; } =item _date_pretty @@ -3620,6 +3697,57 @@ sub _date_pretty { time2str($date_format, $self->_date); } +# I like how _date_pretty was documented but this one wasn't. + +=item _items_sections LATE SUMMARYPAGE ESCAPE EXTRA_SECTIONS FORMAT + +Generate section information for all items appearing on this invoice. +This will only be called for multi-section invoices. + +For each line item (L record), this will fetch all +related display records (L) and organize +them into two groups ("early" and "late" according to whether they come +before or after the total), then into sections. A subtotal is calculated +for each section. + +Section descriptions are returned in sort weight order. Each consists +of a hash containing: + +description: the package category name, escaped +subtotal: the total charges in that section +tax_section: a flag indicating that the section contains only tax charges +summarized: same as tax_section, for some reason +sort_weight: the package category's sort weight + +If 'condense' is set on the display record, it also contains everything +returned from C<_condense_section()>, i.e. C<_condensed_foo_generator> +coderefs to generate parts of the invoice. This is not advised. + +Takes way too many arguments, all mandatory: + +LATE: an arrayref to push the "late" section hashes onto. The "early" +group is simply returned from the method. Yes, I know. Don't ask. + +SUMMARYPAGE: a flag indicating whether this is a summary-format invoice. +Turning this on has the following effects: +- Ignores display items with the 'summary' flag. +- Combines all items into the "early" group. +- Creates sections for all non-disabled package categories, even if they +have no charges on this invoice, as well as a section with no name. + +ESCAPE: an escape function to use for section titles. Why not just +let the calling environment escape things itself? Beats the heck out +of me. + +EXTRA_SECTIONS: an arrayref of additional sections to return after the +sorted list. If there are any of these, section subtotals exclude +usage charges. + +FORMAT: 'latex', 'html', or 'template' (i.e. text). Not used, but +passed through to C<_condense_section()>. + +=cut + use vars qw(%pkg_category_cache); sub _items_sections { my $self = shift; @@ -4012,6 +4140,7 @@ sub _condensed_total_line_generator { sub _items_extra_usage_sections { my $self = shift; + my $conf = $self->conf; my $escape = shift; my $format = shift; @@ -4222,21 +4351,25 @@ sub _items_accountcode_cdr { quantity => '', product_code => 'N/A', section => $section, - ext_description => [], + ext_description => [ $section->{'header'} ], + detail_temp => [], }; $section->{'amount'} += $amount; $accountcodes{$accountcode}{'amount'} += $amount; $accountcodes{$accountcode}{calls}++; $accountcodes{$accountcode}{duration} += $detail->duration; - push @{$accountcodes{$accountcode}{ext_description}}, - $detail->formatted('format' => $format); + push @{$accountcodes{$accountcode}{detail_temp}}, $detail; } } foreach my $l ( values %accountcodes ) { $l->{amount} = sprintf( "%.2f", $l->{amount} ); - unshift @{$l->{ext_description}}, $section->{'header'}; + my @sorted_detail = sort { $a->startdate <=> $b->startdate } @{$l->{detail_temp}}; + foreach my $sorted_detail ( @sorted_detail ) { + push @{$l->{ext_description}}, $sorted_detail->formatted('format'=>$format); + } + delete $l->{detail_temp}; push @lines, $l; } @@ -4247,6 +4380,7 @@ sub _items_accountcode_cdr { sub _items_svc_phone_sections { my $self = shift; + my $conf = $self->conf; my $escape = shift; my $format = shift; @@ -4475,7 +4609,7 @@ sub _items_svc_phone_sections { } -sub _items { +sub _items { # seems to be unused my $self = shift; #my @display = scalar(@_) @@ -4494,6 +4628,7 @@ sub _items { sub _items_previous { my $self = shift; + my $conf = $self->conf; my $cust_main = $self->cust_main; my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance my @b = (); @@ -4502,7 +4637,7 @@ sub _items_previous { ? 'due '. $_->due_date2str($date_format) : time2str($date_format, $_->_date); push @b, { - 'description' => 'Previous Balance, Invoice #'. $_->invnum. " ($date)", + 'description' => $self->mt('Previous Balance, Invoice #'). $_->invnum. " ($date)", #'pkgpart' => 'N/A', 'pkgnum' => 'N/A', 'amount' => sprintf("%.2f", $_->owed), @@ -4524,6 +4659,21 @@ sub _items_previous { #}; } +=item _items_pkg [ OPTIONS ] + +Return line item hashes for each package item on this invoice. Nearly +equivalent to + +$self->_items_cust_bill_pkg([ $self->cust_bill_pkg ]) + +The only OPTIONS accepted is 'section', which may point to a hashref +with a key named 'condensed', which may have a true value. If it +does, this method tries to merge identical items into items with +'quantity' equal to the number of items (not the sum of their +separate quantities, for some reason). + +=cut + sub _items_pkg { my $self = shift; my %options = @_; @@ -4583,17 +4733,48 @@ sub _items_tax { $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_); } +=item _items_cust_bill_pkg CUST_BILL_PKGS OPTIONS + +Takes an arrayref of L objects, and returns a +list of hashrefs describing the line items they generate on the invoice. + +OPTIONS may include: + +format: the invoice format. + +escape_function: the function used to escape strings. + +format_function: the function used to format CDRs. + +section: a hashref containing 'description'; if this is present, +cust_bill_pkg_display records not belonging to this section are +ignored. + +multisection: a flag indicating that this is a multisection invoice, +which does something complicated. + +multilocation: a flag to display the location label for the package. + +Returns a list of hashrefs, each of which may contain: + +pkgnum, description, amount, unit_amount, quantity, _is_setup, and +ext_description, which is an arrayref of detail lines to show below +the package line. + +=cut + sub _items_cust_bill_pkg { my $self = shift; + my $conf = $self->conf; my $cust_bill_pkgs = 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 $unsquelched = $opt{unsquelched} || ''; #unused my $section = $opt{section}->{description} if $opt{section}; - my $summary_page = $opt{summary_page} || ''; + my $summary_page = $opt{summary_page} || ''; #unused my $multilocation = $opt{multilocation} || ''; my $multisection = $opt{multisection} || ''; my $discount_show_always = 0; @@ -4605,6 +4786,21 @@ sub _items_cust_bill_pkg { foreach my $cust_bill_pkg ( @$cust_bill_pkgs ) { + foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { + if ( $_ && !$cust_bill_pkg->hidden ) { + $_->{amount} = sprintf( "%.2f", $_->{amount} ), + $_->{amount} =~ s/^\-0\.00$/0.00/; + $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ), + push @b, { %$_ } + if $_->{amount} != 0 + || $discount_show_always + || ( ! $_->{_is_setup} && $_->{recur_show_zero} ) + || ( $_->{_is_setup} && $_->{setup_show_zero} ) + ; + $_ = undef; + } + } + warn "$me _items_cust_bill_pkg considering cust_bill_pkg ". $cust_bill_pkg->billpkgnum. ", pkgnum ". $cust_bill_pkg->pkgnum. "\n" if $DEBUG > 1; @@ -4619,7 +4815,8 @@ sub _items_cust_bill_pkg { ) { - warn "$me _items_cust_bill_pkg considering display item $display\n" + warn "$me _items_cust_bill_pkg considering cust_bill_pkg_display ". + $display->billpkgdisplaynum. "\n" if $DEBUG > 1; my $type = $display->type; @@ -4640,6 +4837,10 @@ sub _items_cust_bill_pkg { my $cust_pkg = $cust_bill_pkg->cust_pkg; + # start/end dates for invoice formats that do nonstandard + # things with them + my %item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate'); + if ( (!$type || $type eq 'S') && ( $cust_bill_pkg->setup != 0 || $cust_bill_pkg->setup_show_zero @@ -4688,6 +4889,7 @@ sub _items_cust_bill_pkg { #pkgpart => $part_pkg->pkgpart, pkgnum => $cust_bill_pkg->pkgnum, amount => $cust_bill_pkg->setup, + setup_show_zero => $cust_bill_pkg->setup_show_zero, unit_amount => $cust_bill_pkg->unitsetup, quantity => $cust_bill_pkg->quantity, ext_description => \@d, @@ -4794,8 +4996,10 @@ sub _items_cust_bill_pkg { #pkgpart => $part_pkg->pkgpart, pkgnum => $cust_bill_pkg->pkgnum, amount => $amount, + recur_show_zero => $cust_bill_pkg->recur_show_zero, unit_amount => $cust_bill_pkg->unitrecur, quantity => $cust_bill_pkg->quantity, + %item_dates, ext_description => \@d, }; } @@ -4815,8 +5019,10 @@ sub _items_cust_bill_pkg { #pkgpart => $part_pkg->pkgpart, pkgnum => $cust_bill_pkg->pkgnum, amount => $amount, + recur_show_zero => $cust_bill_pkg->recur_show_zero, unit_amount => $cust_bill_pkg->unitrecur, quantity => $cust_bill_pkg->quantity, + %item_dates, ext_description => \@d, }; } @@ -4851,33 +5057,20 @@ sub _items_cust_bill_pkg { $discount_show_always = ($cust_bill_pkg->cust_bill_pkg_discount && $conf->exists('discount-show-always')); - foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { - if ( $_ && !$cust_bill_pkg->hidden ) { - $_->{amount} = sprintf( "%.2f", $_->{amount} ), - $_->{amount} =~ s/^\-0\.00$/0.00/; - $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ), - push @b, { %$_ } - if $_->{amount} != 0 - || $discount_show_always - || ( ! $_->{_is_setup} && $cust_bill_pkg->recur_show_zero ) - || ( $_->{_is_setup} && $cust_bill_pkg->setup_show_zero ) - ; - $_ = undef; - } - } - } - #foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { - # if ( $_ ) { - # $_->{amount} = sprintf( "%.2f", $_->{amount} ), - # $_->{amount} =~ s/^\-0\.00$/0.00/; - # $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ), - # push @b, { %$_ } - # if $_->{amount} != 0 - # || $discount_show_always - # } - #} + foreach ( $s, $r, ($opt{skip_usage} ? () : $u ) ) { + if ( $_ ) { + $_->{amount} = sprintf( "%.2f", $_->{amount} ), + $_->{amount} =~ s/^\-0\.00$/0.00/; + $_->{unit_amount} = sprintf( "%.2f", $_->{unit_amount} ), + push @b, { %$_ } + if $_->{amount} != 0 + || $discount_show_always + || ( ! $_->{_is_setup} && $_->{recur_show_zero} ) + || ( $_->{_is_setup} && $_->{setup_show_zero} ) + } + } warn "$me _items_cust_bill_pkg done considering cust_bill_pkgs\n" if $DEBUG > 1; @@ -4904,7 +5097,7 @@ sub _items_credits { #'description' => 'Credit ref\#'. $_->crednum. # " (". time2str("%x",$_->cust_credit->_date) .")". # $reason, - 'description' => 'Credit applied '. + 'description' => $self->mt('Credit applied').' '. time2str($date_format,$_->cust_credit->_date). $reason, 'amount' => sprintf("%.2f",$_->amount), }; @@ -4924,7 +5117,7 @@ sub _items_payments { #something more elaborate if $_->amount ne ->cust_pay->paid ? push @b, { - 'description' => "Payment received ". + 'description' => $self->mt('Payment received').' '. time2str($date_format,$_->cust_pay->_date ), 'amount' => sprintf("%.2f", $_->amount ) }; @@ -5150,6 +5343,7 @@ Currently only supported on PostgreSQL. =cut sub due_date_sql { + my $conf = new FS::Conf; 'COALESCE( SUBSTRING( COALESCE( @@ -5218,6 +5412,11 @@ sub search_sql_where { push @search, "cust_main.agentnum = $1"; } + #agentnum + if ( $param->{'custnum'} =~ /^(\d+)$/ ) { + push @search, "cust_bill.custnum = $1"; + } + #_date if ( $param->{_date} ) { my($beginning, $ending) = @{$param->{_date}};