X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_bill.pm;h=7f1290fcfa6de61697ab153b8d846b1049713e49;hb=f786ebaff8b6704e4e180428aaaa9afeaea7ecb9;hp=d0e7048b7e508a3ebe9ceeaefefeb91e67e22ae4;hpb=211526de149104c12e1d61155fdd9cf0d2958775;p=freeside.git diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index d0e7048b7..7f1290fcf 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -2,7 +2,7 @@ package FS::cust_bill; use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::Record ); use strict; -use vars qw( $DEBUG $me $date_format ); +use vars qw( $DEBUG $me ); # but NOT $conf use Fcntl qw(:flock); #for spool_csv use Cwd; @@ -44,12 +44,6 @@ use FS::L10N; $DEBUG = 0; $me = '[FS::cust_bill]'; -#ask FS::UID to run this stuff for us later -FS::UID->install_callback( sub { - my $conf = new FS::Conf; #global - $date_format = $conf->config('date_format') || '%x'; #/YY -} ); - =head1 NAME FS::cust_bill - Object methods for cust_bill records @@ -106,23 +100,19 @@ L and L for conversion functions. =back -Customer info at invoice generation time +Deprecated fields =over 4 -=item billing_balance - the customer's balance at the time the invoice was -generated (not including charges on this invoice) - -=item previous_balance - the billing_balance of this customer's previous -invoice plus the charges on that invoice - -=back - -Deprecated +=item billing_balance - the customer's balance immediately before generating +this invoice. DEPRECATED. Use the L method +to determine the customer's balance at a specific time. -=over 4 +=item previous_balance - the customer's balance immediately after generating +the invoice before this one. DEPRECATED. -=item printed - deprecated +=item printed - formerly used to track the number of times an invoice had +been printed; no longer used. =back @@ -420,8 +410,8 @@ 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 ){ + if ( $self->agent_invid + && FS::Conf->new->exists('cust_bill-default_agent_invid') ) { return $self->agent_invid; } else { return $self->invnum; @@ -1084,6 +1074,8 @@ sub generate_email { my %return = ( 'from' => $args{'from'}, 'subject' => ($args{'subject'} || $self->email_subject), + 'custnum' => $self->custnum, + 'msgtype' => 'invoice', ); $args{'unsquelch_cdr'} = $conf->exists('voip-cdr_email'); @@ -1414,7 +1406,7 @@ sub email { my $self = shift; return if $self->hide; my $conf = $self->conf; - my $opt = shift; + my $opt = shift || {}; if ($opt and !ref($opt)) { die "FS::cust_bill::email called with positional parameters"; } @@ -1489,7 +1481,7 @@ I, if specified, overrides "Invoice" as the name of the sent docume sub lpr_data { my $self = shift; my $conf = $self->conf; - my $opt = shift; + my $opt = shift || {}; if ($opt and !ref($opt)) { # nobody does this anyway die "FS::cust_bill::lpr_data called with positional parameters"; @@ -1515,7 +1507,7 @@ sub print { my $self = shift; return if $self->hide; my $conf = $self->conf; - my $opt = shift; + my $opt = shift || {}; if ($opt and !ref($opt)) { die "FS::cust_bill::print called with positional parameters"; } @@ -1550,7 +1542,7 @@ sub fax_invoice { my $self = shift; return if $self->hide; my $conf = $self->conf; - my $opt = shift; + my $opt = shift || {}; if ($opt and !ref($opt)) { die "FS::cust_bill::fax_invoice called with positional parameters"; } @@ -1677,6 +1669,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"; @@ -1983,13 +1976,13 @@ sub print_csv { my $taxtotal = 0; $taxtotal += $_->{'amount'} foreach $self->_items_tax; - my $duedate = $self->due_date2str('%m/%d/%Y'); #date_format? + my $duedate = $self->due_date2str('%m/%d/%Y'); # hardcoded, NOT date_format my( $previous_balance, @unused ) = $self->previous; #previous balance 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); @@ -2270,7 +2263,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) : '' ), ); @@ -2461,13 +2454,20 @@ sub invoice_barcode { =item invnum_date_pretty Returns a string with the invoice number and date, for example: -"Invoice #54 (3/20/2008)" +"Invoice #54 (3/20/2008)". + +Intended for back-end context, with regard to translation and date formatting. =cut +#note: this uses _date_pretty_unlocalized because _date_pretty is too expensive +# for backend use (and also does the wrong thing, localizing for end customer +# instead of backoffice configured date format) sub invnum_date_pretty { my $self = shift; - $self->mt('Invoice #'). $self->invnum. ' ('. $self->_date_pretty. ')'; + #$self->mt('Invoice #'). + 'Invoice #'. #XXX should be translated ala web UI user (not invoice customer) + $self->invnum. ' ('. $self->_date_pretty_unlocalized. ')'; } #sub _items_extra_usage_sections { @@ -2975,6 +2975,49 @@ sub _items_svc_phone_sections { } +=sub _items_usage_class_summary OPTIONS + +Returns a list of detail items summarizing the usage charges on this +invoice. Each one will have 'amount', 'description' (the usage charge name), +and 'usage_classnum'. + +OPTIONS can include 'escape' (a function to escape the descriptions). + +=cut + +sub _items_usage_class_summary { + my $self = shift; + my %opt = @_; + + my $escape = $opt{escape} || sub { $_[0] }; + my $invnum = $self->invnum; + my @classes = qsearch({ + 'table' => 'usage_class', + 'select' => 'classnum, classname, SUM(amount) AS amount', + 'addl_from' => ' LEFT JOIN cust_bill_pkg_detail USING (classnum)' . + ' LEFT JOIN cust_bill_pkg USING (billpkgnum)', + 'extra_sql' => " WHERE cust_bill_pkg.invnum = $invnum". + ' GROUP BY classnum, classname, weight'. + ' HAVING (usage_class.disabled IS NULL OR SUM(amount) > 0)'. + ' ORDER BY weight ASC', + }); + my @l; + my $section = { + description => &{$escape}($self->mt('Usage Summary')), + no_subtotal => 1, + usage_section => 1, + }; + foreach my $class (@classes) { + push @l, { + 'description' => &{$escape}($class->classname), + 'amount' => sprintf('%.2f', $class->amount), + 'usage_classnum' => $class->classnum, + 'section' => $section, + }; + } + return @l; +} + sub _items_previous { my $self = shift; my $conf = $self->conf; @@ -2983,8 +3026,8 @@ sub _items_previous { my @b = (); foreach ( @pr_cust_bill ) { my $date = $conf->exists('invoice_show_prior_due_date') - ? 'due '. $_->due_date2str($date_format) - : time2str($date_format, $_->_date); + ? 'due '. $_->due_date2str('short') + : $self->time2str_local('short', $_->_date); push @b, { 'description' => $self->mt('Previous Balance, Invoice #'). $_->invnum. " ($date)", #'pkgpart' => 'N/A', @@ -3016,14 +3059,22 @@ sub _items_credits { #credits my @objects; if ( $self->conf->exists('previous_balance-payments_since') ) { - my $date = 0; - $date = $self->previous_bill->_date if $self->previous_bill; - @objects = qsearch('cust_credit', { - 'custnum' => $self->custnum, - '_date' => {op => '>=', value => $date}, + 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}, }); - # hard to do this in the qsearch... - @objects = grep { $_->_date < $self->_date } @objects; + } } else { @objects = $self->cust_credited; } @@ -3040,7 +3091,7 @@ sub _items_credits { # " (". time2str("%x",$_->cust_credit->_date) .")". # $reason, 'description' => $self->mt('Credit applied').' '. - time2str($date_format,$obj->_date). $reason, + $self->time2str_local('short', $obj->_date). $reason, 'amount' => sprintf("%.2f",$obj->amount), }; } @@ -3051,18 +3102,32 @@ sub _items_credits { sub _items_payments { my $self = shift; + my %opt = @_; my @b; my $detailed = $self->conf->exists('invoice_payment_details'); my @objects; if ( $self->conf->exists('previous_balance-payments_since') ) { - my $date = 0; - $date = $self->previous_bill->_date if $self->previous_bill; - @objects = qsearch('cust_pay', { + # 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}, }); - @objects = grep { $_->_date < $self->_date } @objects; + # and before the current bill... + @objects = grep { $_->_date < $self->_date } @objects; + } } else { @objects = $self->cust_bill_pay; } @@ -3070,7 +3135,7 @@ sub _items_payments { foreach my $obj (@objects) { my $cust_pay = $obj->isa('FS::cust_pay') ? $obj : $obj->cust_pay; my $desc = $self->mt('Payment received').' '. - time2str($date_format, $cust_pay->_date ); + $self->time2str_local('short', $cust_pay->_date ); $desc .= $self->mt(' via ') . $cust_pay->payby_payinfo_pretty( $self->cust_main->locale ) if $detailed; @@ -3351,6 +3416,22 @@ flag, return net invoices only =item newest_percust +=item custnum + +Return only invoices belonging to that customer. + +=item cust_classnum + +Limit to that customer class (single value or arrayref). + +=item payby + +Limit to customers with that payment method (single value or arrayref). + +=item refnum + +Limit to customers with that advertising source. + =back Note: validates all passed-in data; i.e. safe to use with unchecked CGI params. @@ -3402,6 +3483,14 @@ sub search_sql_where { } + #payby + if ( $param->{payby} ) { + my $payby = $param->{payby}; + $payby = [ $payby ] unless ref $payby; + my $payby_in = join(',', map {dbh->quote($_)} @$payby); + push @search, "cust_main.payby IN($payby_in)" if length($payby_in); + } + #_date if ( $param->{_date} ) { my($beginning, $ending) = @{$param->{_date}};