X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=77c571b8c3f5cac19b3ade64ce853e62ecb3e5b1;hb=d01b288624d883262266afe4775367a5e0e5a328;hp=bd8c1df19d10c33b19551bebd829d257fb0f6b35;hpb=ef6e75c42256463303650203628803f5e781f3db;p=freeside.git diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index bd8c1df19..77c571b8c 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -243,7 +243,6 @@ sub delete { cust_event cust_credit_bill cust_bill_pay - cust_bill_pay cust_credit_bill cust_pay_batch cust_bill_pay_batch @@ -2363,11 +2362,13 @@ unsquelch_cdr - overrides any per customer cdr squelching when true notice_name - overrides "Invoice" as the name of the sent document (templates from 10/2009 or newer required) +locale - override customer's locale + =cut #what's with all the sprintf('%10.2f')'s in here? will it cause any # (alignment in text invoice?) problems to change them all to '%.2f' ? -# yes: fixed width (dot matrix) text printing will be borked +# yes: fixed width/plain text printing will be borked sub print_generic { my( $self, %params ) = @_; my $conf = $self->conf; @@ -2656,7 +2657,7 @@ sub print_generic { ); #localization - my $lh = FS::L10N->get_handle($cust_main->locale); + my $lh = FS::L10N->get_handle( $params{'locale'} || $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 @@ -2856,7 +2857,7 @@ sub print_generic { my $previous_section = { 'description' => $self->mt('Previous Charges'), 'subtotal' => $other_money_char. sprintf('%.2f', $pr_total), - 'summarized' => $summarypage ? 'Y' : '', + 'summarized' => '', #why? $summarypage ? 'Y' : '', }; $previous_section->{posttotal} = '0 / 30 / 60 / 90 days overdue '. join(' / ', map { $cust_main->balance_date_range(@$_) } @@ -2867,12 +2868,11 @@ sub print_generic { my $taxtotal = 0; my $tax_section = { 'description' => $self->mt('Taxes, Surcharges, and Fees'), 'subtotal' => $taxtotal, # adjusted below - 'summarized' => $summarypage ? 'Y' : '', }; my $tax_weight = _pkg_category($tax_section->{description}) ? _pkg_category($tax_section->{description})->weight : 0; - $tax_section->{'summarized'} = $summarypage && !$tax_weight ? 'Y' : ''; + $tax_section->{'summarized'} = ''; #why? $summarypage && !$tax_weight ? 'Y' : ''; $tax_section->{'sort_weight'} = $tax_weight; @@ -2880,12 +2880,11 @@ sub print_generic { my $adjust_section = { 'description' => $self->mt('Credits, Payments, and Adjustments'), 'subtotal' => 0, # adjusted below - 'summarized' => $summarypage ? 'Y' : '', }; my $adjust_weight = _pkg_category($adjust_section->{description}) ? _pkg_category($adjust_section->{description})->weight : 0; - $adjust_section->{'summarized'} = $summarypage && !$adjust_weight ? 'Y' : ''; + $adjust_section->{'summarized'} = ''; #why? $summarypage && !$adjust_weight ? 'Y' : ''; $adjust_section->{'sort_weight'} = $adjust_weight; my $unsquelched = $params{unsquelch_cdr} || $cust_main->squelch_cdr ne 'Y'; @@ -3035,7 +3034,7 @@ sub print_generic { $options{'section'} = $section if $multisection; $options{'format'} = $format; $options{'escape_function'} = $escape_function; - $options{'format_function'} = sub { () } unless $unsquelched; + $options{'no_usage'} = 1 unless $unsquelched; $options{'unsquelched'} = $unsquelched; $options{'summary_page'} = $summarypage; $options{'skip_usage'} = @@ -4763,6 +4762,7 @@ format: the invoice format. escape_function: the function used to escape strings. +DEPRECATED? (expensive, mostly unused?) format_function: the function used to format CDRs. section: a hashref containing 'description'; if this is present, @@ -4791,6 +4791,7 @@ sub _items_cust_bill_pkg { my $format = $opt{format} || ''; my $escape_function = $opt{escape_function} || sub { shift }; my $format_function = $opt{format_function} || ''; + my $no_usage = $opt{no_usage} || ''; my $unsquelched = $opt{unsquelched} || ''; #unused my $section = $opt{section}->{description} if $opt{section}; my $summary_page = $opt{summary_page} || ''; #unused @@ -4847,6 +4848,7 @@ sub _items_cust_bill_pkg { my %details_opt = ( 'format' => $format, 'escape_function' => $escape_function, 'format_function' => $format_function, + 'no_usage' => $opt{'no_usage'}, ); if ( $cust_bill_pkg->pkgnum > 0 ) { @@ -5004,7 +5006,7 @@ sub _items_cust_bill_pkg { #instead of omitting details entirely in this case (unwanted side # effects), just omit CDRs - $details_opt{'format_function'} = sub { () } + $details_opt{'no_usage'} = 1 if $type && $type eq 'R'; push @d, $cust_bill_pkg->details(%details_opt); @@ -5181,105 +5183,100 @@ a setup fee if the discount is allowed to apply to setup fees. sub _items_discounts_avail { my $self = shift; - my %total; - my $pkgnums = 0; - my $pkgnums_times_discounts = 0; - # tricky, because packages may not all be eligible for the same discounts - foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { - my $cust_pkg = $cust_bill_pkg->cust_pkg or next; - my $part_pkg = $cust_pkg->part_pkg or next; - # for simplicity, skip all this if the customer already has a term discount - return () if $cust_pkg->cust_pkg_discount_active; - - $pkgnums++; - next if $part_pkg->freq ne '1'; - - foreach my $discount ( - map { $_->discount } $part_pkg->part_pkg_discount - ) { - - $total{$discount->discountnum} ||= - { - discount => $discount, - pkgnums => [], - base_current => 0, - base_permonth => 0, - setup_include => 0, - setup_exclude => 0, - }; - my $hash = $total{$discount->discountnum}; - $hash->{discount} = $discount; - $hash->{thismonth} += $cust_bill_pkg->recur || 0; - $hash->{setup} += $cust_bill_pkg->setup || 0; - $hash->{base_permonth} += $part_pkg->base_recur_permonth; - - # and make a list of pkgnums - push @{ $hash->{pkgnums} }, $cust_pkg->pkgnum; - $pkgnums_times_discounts++; + my %terms; + my $list_pkgnums = 0; # if any packages are not eligible for all discounts + + my ($previous_balance) = $self->previous; + + foreach (qsearch('discount',{ 'months' => { op => '>', value => 1} })) { + $terms{$_->months} = { + pkgnums => [], + base => $previous_balance || 0, # pre-discount sum of charges + discounted => $previous_balance || 0, # post-discount sum + list_pkgnums => 0, # whether any packages are not discounted } } + foreach my $months (keys %terms) { + my $hash = $terms{$months}; - # Test for the simple case where all packages on the invoice - # are eligible for the same set of discounts. If not, we need - # to list eligibility in the ext_description. - my $list_pkgnums = ( $pkgnums_times_discounts != $pkgnums * keys(%total) ); - - foreach my $hash (values %total) { - my $discount = $hash->{discount}; - my ($amount, $term_total, $percent, $permonth); - my $months = $discount->months; - $hash->{months} = $months; - - if ( $discount->percent ) { - - # per discount_Mixin, percent discounts are calculated on the base - # recurring fee, not the prorated fee. - $percent = $discount->percent; - $amount = sprintf('%.2f', 0.01 * $percent * $hash->{base_permonth}); - # percent discounts apply to setup fee - if ( $discount->setup ) { - $hash->{setup} *= (1 - 0.01*$percent); - } + # tricky, because packages may not all be eligible for the same discounts + foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) { + my $cust_pkg = $cust_bill_pkg->cust_pkg or next; + my $part_pkg = $cust_pkg->part_pkg or next; - } - elsif ( $discount->amount > 0 ) { + next if $part_pkg->freq ne '1'; + my $setup = $cust_bill_pkg->setup || 0; + my $recur = $cust_bill_pkg->recur || 0; + my $permonth = $part_pkg->base_recur_permonth || 0; + + my ($discount) = grep { $_->months == $months } + map { $_->discount } $part_pkg->part_pkg_discount; + + $hash->{base} += $setup + $recur + ($months - 1) * $permonth; + if ($discount) { - # amount discounts are amount * number of packages - $amount = $discount->amount * scalar(@{ $hash->{pkgnums} }); - $percent = sprintf('%.0f', 100 * $amount / $hash->{base_permonth}); + my $discountable; + if ( $discount->setup ) { + $discountable += $setup; + } + else { + $hash->{discounted} += $setup; + } - # flat discounts are applied to setup and recur together - if ( $discount->setup ) { - $hash->{thismonth} += $hash->{setup}; - $hash->{setup} = 0; + if ( $discount->percent ) { + $discountable += $months * $permonth; + $discountable -= ($discountable * $discount->percent / 100); + $discountable -= ($permonth - $recur); # correct for prorate + $hash->{discounted} += $discountable; + } + else { + $discountable += $recur; + $discountable -= $discount->amount * $recur/$permonth; + + $discountable += ($months - 1) * max($permonth - $discount->amount,0); + } + + $hash->{discounted} += $discountable; + push @{ $hash->{pkgnums} }, $cust_pkg->pkgnum; + } + else { #no discount + $hash->{discounted} += $setup + $recur + ($months - 1) * $permonth; + $hash->{list_pkgnums} = 1; } + } #foreach $cust_bill_pkg - } + # don't show this line if no packages have discounts at this term + # or if there are no new charges to apply the discount to + delete $terms{$months} if $hash->{base} == $hash->{discounted} + or $hash->{base} == 0; + + } - $permonth = max( $hash->{base_permonth} - $amount, 0); - $term_total = max( $hash->{thismonth} - $amount , 0 ) # this month - + $permonth * ($months - 1) # rest of the term - + $hash->{setup}; # setup fee + $list_pkgnums = grep { $_->{list_pkgnums} > 0 } values %terms; + + foreach my $months (keys %terms) { + my $hash = $terms{$months}; + my $term_total = sprintf('%.2f', $hash->{discounted}); + # possibly shouldn't include previous balance in these? + my $percent = sprintf('%.0f', 100 * (1 - $term_total / $hash->{base}) ); + my $permonth = sprintf('%.2f', $term_total / $months); $hash->{description} = $self->mt('Save [_1]% by paying for [_2] months', - $percent, $months, + $percent, $months ); $hash->{amount} = $self->mt('[_1] ([_2] per month)', - sprintf('%.2f',$term_total), #no money_char to accommodate template quirk - $money_char.sprintf('%.2f',$permonth) ); + $term_total, $money_char.$permonth + ); my @detail; if ( $list_pkgnums ) { - push @detail, $self->mt('for item'). ' '. + push @detail, $self->mt('discount on item'). ' '. join(', ', map { "#$_" } @{ $hash->{pkgnums} }); } - if ( !$discount->setup and $hash->{setup} ) { - push @detail, $self->mt('excluding setup fees'); - } $hash->{ext_description} = join ', ', @detail; } - sort { -( $a->{months} <=> $b->{months} ) } values(%total); + map { $terms{$_} } sort {$b <=> $a} keys %terms; } =item call_details [ OPTION => VALUE ... ]