X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=9d1631fb51084cd58d5a813e4fd01de02e1ba158;hb=988ca5d2ed7121f00e11456e4919e42529d1aaec;hp=aed1ca5a76fcb021a04a3059936f9006cdce2798;hpb=94e28d653f2bd0fe01cf42969a12f4d6cc689c51;p=freeside.git diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index aed1ca5a7..9d1631fb5 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -377,6 +377,25 @@ sub display_invnum { } } +=item previous_bill + +Returns the customer's last invoice before this one. + +=cut + +sub previous_bill { + my $self = shift; + if ( !$self->get('previous_bill') ) { + $self->set('previous_bill', qsearchs({ + 'table' => 'cust_bill', + 'hashref' => { 'custnum' => $self->custnum, + '_date' => { op=>'<', value=>$self->_date } }, + 'order_by' => 'ORDER BY _date DESC LIMIT 1', + }) ); + } + $self->get('previous_bill'); +} + =item previous Returns a list consisting of the total previous balance for this customer, @@ -1726,6 +1745,7 @@ sub send_csv { my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/cust_bill"; mkdir $spooldir, 0700 unless -d $spooldir; + # don't localize dates here, they're a defined format my $tracctnum = $self->invnum. time2str('-%Y%m%d%H%M%S', time); my $file = "$spooldir/$tracctnum.csv"; @@ -1985,7 +2005,7 @@ sub print_csv { my $pmt_cr_applied = 0; $pmt_cr_applied += $_->{'amount'} - foreach ( $self->_items_payments, $self->_items_credits ) ; + foreach ( $self->_items_payments(%opt), $self->_items_credits(%opt) ) ; my $totaldue = sprintf('%.2f', $self->owed + $previous_balance); @@ -2047,6 +2067,7 @@ sub print_csv { $self->custnum, $cust_main->first, $cust_main->last, + $cust_main->company, $cust_main->address1, $cust_main->address2, $cust_main->city, @@ -2126,7 +2147,7 @@ sub print_csv { ? time2str("%x", $cust_bill_pkg->sdate) : '' ), ($cust_bill_pkg->edate - ?time2str("%x", $cust_bill_pkg->edate) + ? time2str("%x", $cust_bill_pkg->edate) : '' ), ); @@ -2725,8 +2746,9 @@ sub print_generic { #invoice info 'invnum' => $self->invnum, - 'date' => time2str($date_format, $self->_date), - 'today' => time2str($date_format_long, $today), + '_date' => $self->_date, + 'date' => $self->time2str_local($date_format, $self->_date), + 'today' => $self->time2str_local($date_format_long, $today), 'terms' => $self->terms, 'template' => $template, #params{'template'}, 'notice_name' => ($params{'notice_name'} || 'Invoice'),#escape_function? @@ -2769,16 +2791,10 @@ sub print_generic { ); - #localization - my $lh = FS::L10N->get_handle( $params{'locale'} || $cust_main->locale ); + #localization (see FS::cust_main_Mixin) $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 # prototype here to silence warnings - $invoice_data{'time2str'} = sub ($;$$) { $dh->time2str(@_) }; - # eventually use this date handle everywhere in here, too + $invoice_data{'time2str'} = sub ($;$$) { $self->time2str_local(@_) }; my $min_sdate = 999999999999; my $max_edate = 0; @@ -2791,8 +2807,9 @@ sub print_generic { } $invoice_data{'bill_period'} = ''; - $invoice_data{'bill_period'} = time2str('%e %h', $min_sdate) - . " to " . time2str('%e %h', $max_edate) + $invoice_data{'bill_period'} = $self->time2str_local('%e %h', $min_sdate) + . " to " . + $self->time2str_local('%e %h', $max_edate) if ($max_edate != 0 && $min_sdate != 999999999999); $invoice_data{finance_section} = ''; @@ -2872,10 +2889,9 @@ sub print_generic { # 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 ) { + if ( $self->previous_bill ) { $invoice_data{'last_bill'} = { - '_date' => $last_bill->_date, #unformatted + '_date' => $self->previous_bill->_date, #unformatted # all we need for now }; } @@ -2981,6 +2997,7 @@ sub print_generic { my $taxtotal = 0; my $tax_section = { 'description' => $self->mt('Taxes, Surcharges, and Fees'), 'subtotal' => $taxtotal, # adjusted below + 'tax_section' => 1, }; my $tax_weight = _pkg_category($tax_section->{description}) ? _pkg_category($tax_section->{description})->weight @@ -3314,7 +3331,7 @@ sub print_generic { $adjust_section->{'pretotal'} = $self->mt('New charges total').' '. $other_money_char. sprintf('%.2f', $self->charged ); } - }else{ + } else { push @total_items, $total; } push @buf,['','-----------']; @@ -3335,7 +3352,9 @@ sub print_generic { # credits my $credittotal = 0; - foreach my $credit ( $self->_items_credits('trim_len'=>60) ) { + foreach my $credit ( + $self->_items_credits( 'template' => $template, 'trim_len' => 60) + ) { my $total; $total->{'total_item'} = &$escape_function($credit->{'description'}); @@ -3361,13 +3380,17 @@ sub print_generic { $invoice_data{'credittotal'} = sprintf('%.2f', $credittotal); #credits (again) - foreach my $credit ( $self->_items_credits('trim_len'=>32) ) { + foreach my $credit ( + $self->_items_credits( 'template' => $template, 'trim_len' => 32) + ) { push @buf, [ $credit->{'description'}, $money_char.$credit->{'amount'} ]; } # payments my $paymenttotal = 0; - foreach my $payment ( $self->_items_payments ) { + foreach my $payment ( + $self->_items_payments( 'template' => $template ) + ) { my $total = {}; $total->{'total_item'} = &$escape_function($payment->{'description'}); $paymenttotal += $payment->{'amount'}; @@ -3481,6 +3504,10 @@ sub print_generic { } } @discounts_avail; } + # debugging hook: call this with 'diag' => 1 to just get a hash of + # the invoice variables + return \%invoice_data if ( $params{'diag'} ); + # All sections and items are built; now fill in templates. my @includelist = (); push @includelist, 'summary' if $summarypage; @@ -3797,7 +3824,7 @@ sub due_date { sub due_date2str { my $self = shift; - $self->due_date ? time2str(shift, $self->due_date) : ''; + $self->due_date ? $self->time2str_local(shift, $self->due_date) : ''; } sub balance_due_msg { @@ -3819,7 +3846,7 @@ sub balance_due_date { my $duedate = ''; if ( $conf->exists('invoice_default_terms') && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) { - $duedate = time2str($rdate_format, $self->_date + ($1*86400) ); + $duedate = $self->time2str_local($rdate_format, $self->_date + ($1*86400) ); } $duedate; } @@ -3849,7 +3876,7 @@ Returns a string with the date, for example: "3/20/2008" sub _date_pretty { my $self = shift; - time2str($date_format, $self->_date); + $self->time2str_local($date_format, $self->_date); } =item _items_sections LATE SUMMARYPAGE ESCAPE EXTRA_SECTIONS FORMAT @@ -4792,7 +4819,7 @@ sub _items_previous { foreach ( @pr_cust_bill ) { my $date = $conf->exists('invoice_show_prior_due_date') ? 'due '. $_->due_date2str($date_format) - : time2str($date_format, $_->_date); + : $self->time2str_local($date_format, $_->_date); push @b, { 'description' => $self->mt('Previous Balance, Invoice #'). $_->invnum. " ($date)", #'pkgpart' => 'N/A', @@ -5108,16 +5135,16 @@ sub _items_cust_bill_pkg { $cust_main->agentnum ); if ( defined($date_style) && $date_style eq 'month_of' ) { - $time_period = time2str('The month of %B', $cust_bill_pkg->sdate); + $time_period = $self->time2str_local('The month of %B', $cust_bill_pkg->sdate); } elsif ( defined($date_style) && $date_style eq 'X_month' ) { my $desc = $conf->config( 'cust_bill-line_item-date_description', $cust_main->agentnum ); $desc .= ' ' unless $desc =~ /\s$/; - $time_period = $desc. time2str('%B', $cust_bill_pkg->sdate); + $time_period = $desc. $self->time2str_local('%B', $cust_bill_pkg->sdate); } else { - $time_period = time2str($date_format, $cust_bill_pkg->sdate). - " - ". time2str($date_format, $cust_bill_pkg->edate); + $time_period = $self->time2str_local($date_format, $cust_bill_pkg->sdate). + " - ". $self->time2str_local($date_format, $cust_bill_pkg->edate); } $description .= " ($time_period)"; } @@ -5143,7 +5170,7 @@ sub _items_cust_bill_pkg { if $DEBUG > 1; my @svc_labels = map &{$escape_function}($_), - $cust_pkg->h_labels_short($self->_date, undef, 'I'); + $cust_pkg->h_labels_short(@dates, 'I'); push @d, @svc_labels unless $cust_bill_pkg->pkgpart_override; #don't redisplay services $svc_label = $svc_labels[0]; @@ -5270,8 +5297,8 @@ sub _items_cust_bill_pkg { if ( $cust_bill_pkg->recur != 0 ) { push @b, { 'description' => "$desc (". - time2str($date_format, $cust_bill_pkg->sdate). ' - '. - time2str($date_format, $cust_bill_pkg->edate). ')', + $self->time2str_local($date_format, $cust_bill_pkg->sdate). ' - '. + $self->time2str_local($date_format, $cust_bill_pkg->edate). ')', 'amount' => sprintf("%.2f", $cust_bill_pkg->recur), }; } @@ -5311,12 +5338,33 @@ sub _items_credits { my @b; #credits - foreach ( $self->cust_credited ) { + my @objects; + if ( $self->conf->exists('previous_balance-payments_since') ) { + if ( $opt{'template'} eq 'statement' ) { + # then the current bill is a "statement" (i.e. an invoice sent as + # a payment receipt) + # and in that case we want to see payments on or after THIS invoice + @objects = qsearch('cust_credit', { + 'custnum' => $self->custnum, + '_date' => {op => '>=', value => $self->_date}, + }); + } else { + my $date = 0; + $date = $self->previous_bill->_date if $self->previous_bill; + @objects = qsearch('cust_credit', { + 'custnum' => $self->custnum, + '_date' => {op => '>=', value => $date}, + }); + } + } else { + @objects = $self->cust_credited; + } - #something more elaborate if $_->amount ne $_->cust_credit->credited ? + foreach my $obj ( @objects ) { + my $cust_credit = $obj->isa('FS::cust_credit') ? $obj : $obj->cust_credit; - my $reason = substr($_->cust_credit->reason, 0, $trim_len); - $reason .= '...' if length($reason) < length($_->cust_credit->reason); + my $reason = substr($cust_credit->reason, 0, $trim_len); + $reason .= '...' if length($reason) < length($cust_credit->reason); $reason = " ($reason) " if $reason; push @b, { @@ -5324,8 +5372,8 @@ sub _items_credits { # " (". time2str("%x",$_->cust_credit->_date) .")". # $reason, 'description' => $self->mt('Credit applied').' '. - time2str($date_format,$_->cust_credit->_date). $reason, - 'amount' => sprintf("%.2f",$_->amount), + $self->time2str_local($date_format,$obj->_date). $reason, + 'amount' => sprintf("%.2f",$obj->amount), }; } @@ -5335,17 +5383,47 @@ sub _items_credits { sub _items_payments { my $self = shift; + my %opt = @_; my @b; - #get & print payments - foreach ( $self->cust_bill_pay ) { + my $detailed = $self->conf->exists('invoice_payment_details'); + my @objects; + if ( $self->conf->exists('previous_balance-payments_since') ) { + # then show payments dated on/after the previous bill... + if ( $opt{'template'} eq 'statement' ) { + # then the current bill is a "statement" (i.e. an invoice sent as + # a payment receipt) + # and in that case we want to see payments on or after THIS invoice + @objects = qsearch('cust_pay', { + 'custnum' => $self->custnum, + '_date' => {op => '>=', value => $self->_date}, + }); + } else { + # the normal case: payments on or after the previous invoice + my $date = 0; + $date = $self->previous_bill->_date if $self->previous_bill; + @objects = qsearch('cust_pay', { + 'custnum' => $self->custnum, + '_date' => {op => '>=', value => $date}, + }); + # and before the current bill... + @objects = grep { $_->_date < $self->_date } @objects; + } + } else { + @objects = $self->cust_bill_pay; + } - #something more elaborate if $_->amount ne ->cust_pay->paid ? + foreach my $obj (@objects) { + my $cust_pay = $obj->isa('FS::cust_pay') ? $obj : $obj->cust_pay; + my $desc = $self->mt('Payment received').' '. + $self->time2str_local($date_format, $cust_pay->_date ); + $desc .= $self->mt(' via ') . + $cust_pay->payby_payinfo_pretty( $self->cust_main->locale ) + if $detailed; push @b, { - 'description' => $self->mt('Payment received').' '. - time2str($date_format,$_->cust_pay->_date ), - 'amount' => sprintf("%.2f", $_->amount ) + 'description' => $desc, + 'amount' => sprintf("%.2f", $obj->amount ) }; }