X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=7669479eb4f17455eef8224ca6252a3aaa01e314;hp=ca35a443aab909645fcf98a0eba036c16aa77c22;hb=982ded2d929bdcdfa72efa810273f3bc753bf036;hpb=f9a181e4c2e505df84de16190ee3b75011326f3f diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index ca35a443a..7669479eb 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -5,6 +5,7 @@ use vars qw( @ISA $DEBUG $me $conf $money_char $date_format $rdate_format $date_format_long ); use vars qw( $invoice_lines @buf ); #yuck use Fcntl qw(:flock); #for spool_csv +use Cwd; use List::Util qw(min max); use Date::Format; use Text::Template 1.20; @@ -38,7 +39,8 @@ use FS::part_bill_event; use FS::payby; use FS::bill_batch; use FS::cust_bill_batch; -use Cwd; +use FS::cust_bill_pay_pkg; +use FS::cust_credit_bill_pkg; @ISA = qw( FS::cust_main_Mixin FS::Record ); @@ -243,6 +245,7 @@ sub delete { cust_pay_batch cust_bill_pay_batch cust_bill_pkg + cust_bill_batch )) { foreach my $linked ( $self->$table() ) { @@ -649,44 +652,97 @@ sub cust_credit_bill { shift->cust_credited(@_); } -=item cust_bill_pay_pkgnum PKGNUM +#=item cust_bill_pay_pkgnum PKGNUM +# +#Returns all payment applications (see L) for this invoice +#with matching pkgnum. +# +#=cut +# +#sub cust_bill_pay_pkgnum { +# my( $self, $pkgnum ) = @_; +# map { $_ } #return $self->num_cust_bill_pay_pkgnum($pkgnum) unless wantarray; +# sort { $a->_date <=> $b->_date } +# qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum, +# 'pkgnum' => $pkgnum, +# } +# ); +#} + +=item cust_bill_pay_pkg PKGNUM Returns all payment applications (see L) for this invoice -with matching pkgnum. +applied against the matching pkgnum. =cut -sub cust_bill_pay_pkgnum { +sub cust_bill_pay_pkg { my( $self, $pkgnum ) = @_; - map { $_ } #return $self->num_cust_bill_pay_pkgnum($pkgnum) unless wantarray; - sort { $a->_date <=> $b->_date } - qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum, - 'pkgnum' => $pkgnum, - } - ); + + qsearch({ + 'select' => 'cust_bill_pay_pkg.*', + 'table' => 'cust_bill_pay_pkg', + 'addl_from' => ' LEFT JOIN cust_bill_pay USING ( billpaynum ) '. + ' LEFT JOIN cust_bill_pkg USING ( billpkgnum ) ', + 'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum. + " AND cust_bill_pkg.pkgnum = $pkgnum", + }); + } -=item cust_credited_pkgnum PKGNUM +#=item cust_credited_pkgnum PKGNUM +# +#=item cust_credit_bill_pkgnum PKGNUM +# +#Returns all applied credits (see L) for this invoice +#with matching pkgnum. +# +#=cut +# +#sub cust_credited_pkgnum { +# my( $self, $pkgnum ) = @_; +# map { $_ } #return $self->num_cust_credit_bill_pkgnum($pkgnum) unless wantarray; +# sort { $a->_date <=> $b->_date } +# qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum, +# 'pkgnum' => $pkgnum, +# } +# ); +#} +# +#sub cust_credit_bill_pkgnum { +# shift->cust_credited_pkgnum(@_); +#} -=item cust_credit_bill_pkgnum PKGNUM +=item cust_credit_bill_pkg PKGNUM -Returns all applied credits (see L) for this invoice -with matching pkgnum. +Returns all credit applications (see L) for this invoice +applied against the matching pkgnum. =cut -sub cust_credited_pkgnum { +sub cust_credit_bill_pkg { my( $self, $pkgnum ) = @_; - map { $_ } #return $self->num_cust_credit_bill_pkgnum($pkgnum) unless wantarray; - sort { $a->_date <=> $b->_date } - qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum, - 'pkgnum' => $pkgnum, - } - ); + + qsearch({ + 'select' => 'cust_credit_bill_pkg.*', + 'table' => 'cust_credit_bill_pkg', + 'addl_from' => ' LEFT JOIN cust_credit_bill USING ( creditbillnum ) '. + ' LEFT JOIN cust_bill_pkg USING ( billpkgnum ) ', + 'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum. + " AND cust_bill_pkg.pkgnum = $pkgnum", + }); + } -sub cust_credit_bill_pkgnum { - shift->cust_credited_pkgnum(@_); +=item cust_bill_batch + +Returns all invoice batch records (L) for this invoice. + +=cut + +sub cust_bill_batch { + my $self = shift; + qsearch('cust_bill_batch', { 'invnum' => $self->invnum }); } =item tax @@ -729,8 +785,8 @@ sub owed_pkgnum { my $balance = 0; $balance += $_->setup + $_->recur for $self->cust_bill_pkg_pkgnum($pkgnum); - $balance -= $_->amount for $self->cust_bill_pay_pkgnum($pkgnum); - $balance -= $_->amount for $self->cust_credited_pkgnum($pkgnum); + $balance -= $_->amount for $self->cust_bill_pay_pkg($pkgnum); + $balance -= $_->amount for $self->cust_credit_bill_pkg($pkgnum); $balance = sprintf( "%.2f", $balance); $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp @@ -911,6 +967,7 @@ sub generate_email { 'unsquelch_cdr' => $conf->exists('voip-cdr_email'), 'template' => $args{'template'}, 'notice_name' => ( $args{'notice_name'} || 'Invoice' ), + 'no_coupon' => $args{'no_coupon'}, ); my $cust_main = $self->cust_main; @@ -1259,11 +1316,12 @@ sub queueable_email { my $self = qsearchs('cust_bill', { 'invnum' => $opt{invnum} } ) or die "invalid invoice number: " . $opt{invnum}; - my @args = ( $opt{template} ); - push @args, $opt{invoice_from} - if exists($opt{invoice_from}) && $opt{invoice_from}; + my %args = ( 'template' => $opt{template} ); + $args{$_} = $opt{$_} + foreach grep { exists($opt{$_}) && $opt{$_} } + qw( invoice_from notice_name no_coupon ); - my $error = $self->email( @args ); + my $error = $self->email( \%args ); die $error if $error; } @@ -1272,16 +1330,18 @@ sub queueable_email { sub email { my $self = shift; - my( $template, $invoice_from, $notice_name ); + my( $template, $invoice_from, $notice_name, $no_coupon ); if ( ref($_[0]) ) { my $opt = shift; $template = $opt->{'template'} || ''; $invoice_from = $opt->{'invoice_from'}; $notice_name = $opt->{'notice_name'} || 'Invoice'; + $no_coupon = $opt->{'no_coupon'} || 0; } else { $template = scalar(@_) ? shift : ''; $invoice_from = shift if scalar(@_); $notice_name = 'Invoice'; + $no_coupon = 0; } $invoice_from ||= $self->_agent_invoice_from || #XXX should go away @@ -1308,6 +1368,7 @@ sub email { 'subject' => $subject, 'template' => $template, 'notice_name' => $notice_name, + 'no_coupon' => $no_coupon, ) ); die "can't email invoice: $error\n" if $error; @@ -1459,14 +1520,36 @@ isn't an open batch, one will be created. sub batch_invoice { my ($self, $opt) = @_; - my $batch = FS::bill_batch->get_open_batch; + my $bill_batch = $self->get_open_bill_batch; my $cust_bill_batch = FS::cust_bill_batch->new({ - batchnum => $batch->batchnum, + batchnum => $bill_batch->batchnum, invnum => $self->invnum, }); return $cust_bill_batch->insert($opt); } +=item get_open_batch + +Returns the currently open batch as an FS::bill_batch object, creating a new +one if necessary. (A per-agent batch if invoice_print_pdf-spoolagent is +enabled) + +=cut + +sub get_open_bill_batch { + my $self = shift; + my $hashref = { status => 'O' }; + $hashref->{'agentnum'} = $conf->exists('invoice_print_pdf-spoolagent') + ? $self->cust_main->agentnum + : ''; + my $batch = qsearchs('bill_batch', $hashref); + return $batch if $batch; + $batch = FS::bill_batch->new($hashref); + my $error = $batch->insert; + die $error if $error; + return $batch; +} + =item ftp_invoice [ TEMPLATENAME ] Sends this invoice data via FTP. @@ -2107,7 +2190,7 @@ sub print_text { $params{'time'} = $today if $today; $params{'template'} = $template if $template; $params{$_} = $opt{$_} - foreach grep $opt{$_}, qw( unsquealch_cdr notice_name ); + foreach grep $opt{$_}, qw( unsquelch_cdr notice_name ); $self->print_generic( %params ); } @@ -2149,7 +2232,7 @@ sub print_latex { $params{'time'} = $today if $today; $params{'template'} = $template if $template; $params{$_} = $opt{$_} - foreach grep $opt{$_}, qw( unsquealch_cdr notice_name ); + foreach grep $opt{$_}, qw( unsquelch_cdr notice_name ); $template ||= $self->_agent_template; @@ -2295,7 +2378,7 @@ sub print_generic { my $template = $params{template} ? $params{template} : $self->_agent_template; my $templatefile = "invoice_$format"; $templatefile .= "_$template" - if length($template); + if length($template) && $conf->exists($templatefile."_$template"); my @invoice_template = map "$_\n", $conf->config($templatefile) or die "cannot load config data $templatefile"; @@ -2504,6 +2587,7 @@ sub print_generic { #invoice from info 'company_name' => scalar( $conf->config('company_name', $agentnum) ), 'company_address' => join("\n", $conf->config('company_address', $agentnum) ). "\n", + 'company_phonenum'=> scalar( $conf->config('company_phonenum', $agentnum) ), 'returnaddress' => $returnaddress, 'agent' => &$escape_function($cust_main->agent->agent), @@ -2643,7 +2727,9 @@ sub print_generic { warn "$me substituting variables in notes, footer, smallfooter\n" if $DEBUG > 1; - foreach my $include (qw( notes footer smallfooter coupon )) { + my @include = (qw( notes footer smallfooter )); + push @include, 'coupon' unless $params{'no_coupon'}; + foreach my $include (@include) { my $inc_file = $conf->key_orbase("invoice_${format}$include", $template); my @inc_src; @@ -2777,6 +2863,14 @@ sub print_generic { push @{$late_sections}, @$phone_sections; push @detail_items, @$phone_lines; } + if ($conf->exists('voip-cust_accountcode_cdr') && $cust_main->accountcode_cdr) { + my ($accountcode_section, $accountcode_lines) = + $self->_items_accountcode_cdr($escape_function_nonbsp,$format); + if ( scalar(@$accountcode_lines) ) { + push @{$late_sections}, $accountcode_section; + push @detail_items, @$accountcode_lines; + } + } }else{ push @sections, { 'description' => '', 'subtotal' => '' }; } @@ -2814,7 +2908,7 @@ sub print_generic { } } - + if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) { push @buf, ['','-----------']; push @buf, [ 'Total Previous Balance', @@ -2827,12 +2921,11 @@ sub print_generic { if $DEBUG > 1; my ($didsummary,$minutes) = $self->_did_summary; - my $didsummary_desc = 'DID Activity Summary (Past 30 days)'; + my $didsummary_desc = 'DID Activity Summary (since last invoice)'; push @detail_items, { 'description' => $didsummary_desc, 'ext_description' => [ $didsummary, $minutes ], - } - if !$multisection; + }; } foreach my $section (@sections, @$late_sections) { @@ -3293,7 +3386,7 @@ sub print_ps { my ($file, $logofile, $barcodefile) = $self->print_latex(@_); my $ps = generate_ps($file); unlink($logofile); - unlink($barcodefile); + unlink($barcodefile) if $barcodefile; $ps; } @@ -3322,7 +3415,7 @@ sub print_pdf { my ($file, $logofile, $barcodefile) = $self->print_latex(@_); my $pdf = generate_pdf($file); unlink($logofile); - unlink($barcodefile); + unlink($barcodefile) if $barcodefile; $pdf; } @@ -3645,7 +3738,7 @@ sub _items_sections { } } @sections; push @early, @$extra_sections if $extra_sections; - + sort { $a->{sort_weight} <=> $b->{sort_weight} } @early; } @@ -3926,6 +4019,8 @@ sub _items_extra_usage_sections { my %classnums = (); my %lines = (); + my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50; + my %usage_class = map { $_->classnum => $_ } qsearch( 'usage_class', {} ); foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { next unless $cust_bill_pkg->pkgnum > 0; @@ -3945,8 +4040,8 @@ sub _items_extra_usage_sections { my $desc = $detail->regionname; my $description = $desc; - $description = substr($desc, 0, 50). '...' - if $format eq 'latex' && length($desc) > 50; + $description = substr($desc, 0, $maxlength). '...' + if $format eq 'latex' && length($desc) > $maxlength; $lines{$section}{$desc} ||= { description => &{$escape}($description), @@ -4010,7 +4105,12 @@ sub _items_extra_usage_sections { sub _did_summary { my $self = shift; my $end = $self->_date; - my $start = $end - 2592000; # 30 days + + # start at date of previous invoice + 1 second or 0 if no previous invoice + my $start = $self->scalar_sql("SELECT max(_date) FROM cust_bill WHERE custnum = ? and invnum != ?",$self->custnum,$self->invnum); + $start = 0 if !$start; + $start++; + my $cust_main = $self->cust_main; my @pkgs = $cust_main->all_pkgs; my($num_activated,$num_deactivated,$num_portedin,$num_portedout,$minutes) @@ -4024,7 +4124,7 @@ sub _did_summary { my $inserted = $h_cust_svc->date_inserted; my $deleted = $h_cust_svc->date_deleted; - my $phone_inserted = $h_cust_svc->h_svc_x($inserted); + my $phone_inserted = $h_cust_svc->h_svc_x($inserted+5); my $phone_deleted; $phone_deleted = $h_cust_svc->h_svc_x($deleted) if $deleted; @@ -4057,10 +4157,13 @@ sub _did_summary { } # increment usage minutes - my @cdrs = $phone_inserted->get_cdrs('begin'=>$start,'end'=>$end); - foreach my $cdr ( @cdrs ) { - $minutes += $cdr->billsec/60; - } + if ( $phone_inserted ) { + my @cdrs = $phone_inserted->get_cdrs('begin'=>$start,'end'=>$end,'billsec_sum'=>1); + $minutes = $cdrs[0]->billsec_sum if scalar(@cdrs) == 1; + } + else { + warn "WARNING: no matching h_svc_phone insert record for insert time $inserted, svcnum " . $h_cust_svc->svcnum; + } # don't look at this service again push @seen, $h_cust_svc->svcnum; @@ -4073,6 +4176,75 @@ sub _did_summary { "Total Minutes: $minutes"); } +sub _items_accountcode_cdr { + my $self = shift; + my $escape = shift; + my $format = shift; + + my $section = { 'amount' => 0, + 'calls' => 0, + 'duration' => 0, + 'sort_weight' => '', + 'phonenum' => '', + 'description' => 'Usage by Account Code', + 'post_total' => '', + 'summarized' => '', + 'header' => '', + }; + my @lines; + my %accountcodes = (); + + foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { + next unless $cust_bill_pkg->pkgnum > 0; + + my @header = $cust_bill_pkg->details_header; + next unless scalar(@header); + $section->{'header'} = join(',',@header); + + foreach my $detail ( $cust_bill_pkg->cust_bill_pkg_detail ) { + + $section->{'header'} = $detail->formatted('format' => $format) + if($detail->detail eq $section->{'header'}); + + my $accountcode = $detail->accountcode; + next unless $accountcode; + + my $amount = $detail->amount; + next unless $amount && $amount > 0; + + $accountcodes{$accountcode} ||= { + description => $accountcode, + pkgnum => '', + ref => '', + amount => 0, + calls => 0, + duration => 0, + quantity => '', + product_code => 'N/A', + section => $section, + ext_description => [], + }; + + $section->{'amount'} += $amount; + $accountcodes{$accountcode}{'amount'} += $amount; + $accountcodes{$accountcode}{calls}++; + $accountcodes{$accountcode}{duration} += $detail->duration; + push @{$accountcodes{$accountcode}{ext_description}}, + $detail->formatted('format' => $format); + } + } + + foreach my $l ( values %accountcodes ) { + $l->{amount} = sprintf( "%.2f", $l->{amount} ); + unshift @{$l->{ext_description}}, $section->{'header'}; + push @lines, $l; + } + + my @sorted_lines = sort { $a->{'description'} <=> $b->{'description'} } @lines; + + return ($section,\@sorted_lines); +} + sub _items_svc_phone_sections { my $self = shift; my $escape = shift; @@ -4082,6 +4254,8 @@ sub _items_svc_phone_sections { my %classnums = (); my %lines = (); + my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50; + my %usage_class = map { $_->classnum => $_ } qsearch( 'usage_class', {} ); $usage_class{''} ||= new FS::usage_class { 'classname' => '', 'weight' => 0 }; @@ -4111,8 +4285,8 @@ sub _items_svc_phone_sections { my $desc = $detail->regionname; my $description = $desc; - $description = substr($desc, 0, 50). '...' - if $format eq 'latex' && length($desc) > 50; + $description = substr($desc, 0, $maxlength). '...' + if $format eq 'latex' && length($desc) > $maxlength; $lines{$phonenum}{$desc} ||= { description => &{$escape}($description), @@ -4424,28 +4598,17 @@ sub _items_cust_bill_pkg { my $multisection = $opt{multisection} || ''; my $discount_show_always = 0; + my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50; + my @b = (); my ($s, $r, $u) = ( undef, undef, undef ); foreach my $cust_bill_pkg ( @$cust_bill_pkgs ) { - warn "$me _items_cust_bill_pkg considering cust_bill_pkg $cust_bill_pkg\n" + warn "$me _items_cust_bill_pkg considering cust_bill_pkg ". + $cust_bill_pkg->billpkgnum. ", pkgnum ". $cust_bill_pkg->pkgnum. "\n" if $DEBUG > 1; - $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, { %$_ } - unless ( $_->{amount} == 0 && !$discount_show_always ); - $_ = undef; - } - } - foreach my $display ( grep { defined($section) ? $_->section eq $section : 1 @@ -4462,8 +4625,8 @@ sub _items_cust_bill_pkg { my $type = $display->type; my $desc = $cust_bill_pkg->desc; - $desc = substr($desc, 0, 50). '...' - if $format eq 'latex' && length($desc) > 50; + $desc = substr($desc, 0, $maxlength). '...' + if $format eq 'latex' && length($desc) > $maxlength; my %details_opt = ( 'format' => $format, 'escape_function' => $escape_function, @@ -4477,13 +4640,21 @@ sub _items_cust_bill_pkg { my $cust_pkg = $cust_bill_pkg->cust_pkg; - if ( $cust_bill_pkg->setup != 0 && (!$type || $type eq 'S') ) { + if ( (!$type || $type eq 'S') + && ( $cust_bill_pkg->setup != 0 + || $cust_bill_pkg->setup_show_zero + ) + ) + { warn "$me _items_cust_bill_pkg adding setup\n" if $DEBUG > 1; my $description = $desc; - $description .= ' Setup' if $cust_bill_pkg->recur != 0; + $description .= ' Setup' + if $cust_bill_pkg->recur != 0 + || $discount_show_always + || $cust_bill_pkg->recur_show_zero; my @d = (); unless ( $cust_pkg->part_pkg->hide_svc_detail @@ -4496,8 +4667,8 @@ sub _items_cust_bill_pkg { if ( $multilocation ) { my $loc = $cust_pkg->location_label; - $loc = substr($loc, 0, 50). '...' - if $format eq 'latex' && length($loc) > 50; + $loc = substr($loc, 0, $maxlength). '...' + if $format eq 'latex' && length($loc) > $maxlength; push @d, &{$escape_function}($loc); } @@ -4512,6 +4683,7 @@ sub _items_cust_bill_pkg { push @{ $s->{ext_description} }, @d; } else { $s = { + _is_setup => 1, description => $description, #pkgpart => $part_pkg->pkgpart, pkgnum => $cust_bill_pkg->pkgnum, @@ -4524,9 +4696,13 @@ sub _items_cust_bill_pkg { } - if ( ( $cust_bill_pkg->recur != 0 || $cust_bill_pkg->setup == 0 || - ($discount_show_always && $cust_bill_pkg->recur == 0) ) && - ( !$type || $type eq 'R' || $type eq 'U' ) + if ( ( !$type || $type eq 'R' || $type eq 'U' ) + && ( + $cust_bill_pkg->recur != 0 + || $cust_bill_pkg->setup == 0 + || $discount_show_always + || $cust_bill_pkg->recur_show_zero + ) ) { @@ -4540,7 +4716,8 @@ sub _items_cust_bill_pkg { $description .= " (" . time2str($date_format, $cust_bill_pkg->sdate). " - ". time2str($date_format, $cust_bill_pkg->edate). ")" - unless $conf->exists('disable_line_item_date_ranges'); + unless $conf->exists('disable_line_item_date_ranges') + || $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1); my @d = (); @@ -4571,8 +4748,8 @@ sub _items_cust_bill_pkg { if ( $multilocation ) { my $loc = $cust_pkg->location_label; - $loc = substr($loc, 0, 50). '...' - if $format eq 'latex' && length($loc) > 50; + $loc = substr($loc, 0, $maxlength). '...' + if $format eq 'latex' && length($loc) > $maxlength; push @d, &{$escape_function}($loc); } @@ -4643,7 +4820,6 @@ sub _items_cust_bill_pkg { ext_description => \@d, }; } - } } # recurring or usage with recurring charge @@ -4672,21 +4848,40 @@ 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 + # } + #} + warn "$me _items_cust_bill_pkg done considering cust_bill_pkgs\n" if $DEBUG > 1; - 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, { %$_ } - unless ( $_->{amount} == 0 && !$discount_show_always ); - } - } - @b; }