}
}
+=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,
my $self = shift;
my $total = 0;
my @cust_bill = sort { $a->_date <=> $b->_date }
- grep { $_->owed != 0 && $_->_date < $self->_date }
- qsearch( 'cust_bill', { 'custnum' => $self->custnum } )
+ grep { $_->owed != 0 }
+ qsearch( 'cust_bill', { 'custnum' => $self->custnum,
+ #'_date' => { op=>'<', value=>$self->_date },
+ 'invnum' => { op=>'<', value=>$self->invnum },
+ } )
;
foreach ( @cust_bill ) { $total += $_->owed; }
$total, @cust_bill;
}
+=item enable_previous
+
+Whether to show the 'Previous Charges' section when printing this invoice.
+The negation of the 'disable_previous_balance' config setting.
+
+=cut
+
+sub enable_previous {
+ my $self = shift;
+ my $agentnum = $self->cust_main->agentnum;
+ !$self->conf->exists('disable_previous_balance', $agentnum);
+}
+
=item cust_bill_pkg
Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
$balance_over = shift if scalar(@_) && $_[0] !~ /^\s*$/;
}
+ my $cust_main = $self->cust_main;
+
return 'N/A' unless ! $agentnums
- or grep { $_ == $self->cust_main->agentnum } @$agentnums;
+ or grep { $_ == $cust_main->agentnum } @$agentnums;
return ''
- unless $self->cust_main->total_owed_date($self->_date) > $balance_over;
+ unless $cust_main->total_owed_date($self->_date) > $balance_over;
$invoice_from ||= $self->_agent_invoice_from || #XXX should go away
- $conf->config('invoice_from', $self->cust_main->agentnum );
+ $conf->config('invoice_from', $cust_main->agentnum );
my %opt = (
'template' => $template,
'notice_name' => ( $notice_name || 'Invoice' ),
);
- my @invoicing_list = $self->cust_main->invoicing_list;
+ my @invoicing_list = $cust_main->invoicing_list;
#$self->email_invoice(\%opt)
$self->email(\%opt)
- if grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list;
+ if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list )
+ && ! $self->invoice_noemail;
#$self->print_invoice(\%opt)
$self->print(\%opt)
$self->batch_invoice(\%opt);
}
else {
- do_print $self->lpr_data(\%opt);
+ do_print(
+ $self->lpr_data(\%opt),
+ 'agentnum' => $self->cust_main->agentnum,
+ );
}
}
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";
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);
'0', # 29 | Other Taxes & Fees*** NUM* 9
);
+ } elsif ( lc($opt{'format'}) eq 'oneline' ) { #name?
+
+ my ($previous_balance) = $self->previous;
+ $previous_balance = sprintf('%.2f', $previous_balance);
+ my $totaldue = sprintf('%.2f', $self->owed + $previous_balance);
+ my @items = map {
+ $_->{pkgnum},
+ $_->{description},
+ $_->{amount}
+ }
+ $self->_items_pkg, #_items_nontax? no sections or anything
+ # with this format
+ $self->_items_tax;
+
+ $csv->combine(
+ $cust_main->agentnum,
+ $cust_main->agent->agent,
+ $self->custnum,
+ $cust_main->first,
+ $cust_main->last,
+ $cust_main->company,
+ $cust_main->address1,
+ $cust_main->address2,
+ $cust_main->city,
+ $cust_main->state,
+ $cust_main->zip,
+
+ # invoice fields
+ time2str("%x", $self->_date),
+ $self->invnum,
+ $self->charged,
+ $totaldue,
+ $previous_balance,
+ $self->due_date2str("%x"),
+
+ @items,
+ );
+
} else {
$csv->combine(
}
+ } elsif ( lc($opt{'format'}) eq 'oneline' ) {
+
+ #do nothing
+
} else {
foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
? time2str("%x", $cust_bill_pkg->sdate)
: '' ),
($cust_bill_pkg->edate
- ?time2str("%x", $cust_bill_pkg->edate)
+ ? time2str("%x", $cust_bill_pkg->edate)
: '' ),
);
#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?
);
- #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;
}
$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} = '';
# 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
};
}
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
my $adjusttotal = 0;
- my $adjust_section = { 'description' =>
- $self->mt('Credits, Payments, and Adjustments'),
- 'subtotal' => 0, # adjusted below
- };
+ my $adjust_section = {
+ 'description' => $self->mt('Credits, Payments, and Adjustments'),
+ 'adjust_section' => 1,
+ 'subtotal' => 0, # adjusted below
+ };
my $adjust_weight = _pkg_category($adjust_section->{description})
? _pkg_category($adjust_section->{description})->weight
: 0;
my $late_sections = [];
my $extra_sections = [];
my $extra_lines = ();
+
+ my $default_section = { 'description' => '',
+ 'subtotal' => '',
+ 'no_subtotal' => 1,
+ };
+
if ( $multisection ) {
($extra_sections, $extra_lines) =
$self->_items_extra_usage_sections($escape_function_nonbsp, $format)
}
} else {# not multisection
# make a default section
- push @sections, { 'description' => '', 'subtotal' => '',
- 'no_subtotal' => 1 };
+ push @sections, $default_section;
# 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?
}
}
- unless ( $conf->exists('disable_previous_balance')
- || $conf->exists('previous_balance-summary_only')
- )
- {
+ # previous invoice balances in the Previous Charges section if there
+ # is one, otherwise in the main detail section
+ if ( $self->can('_items_previous') &&
+ $self->enable_previous &&
+ ! $conf->exists('previous_balance-summary_only') ) {
warn "$me adding previous balances\n"
if $DEBUG > 1;
ext_description => [],
};
$detail->{'ref'} = $line_item->{'pkgnum'};
+ $detail->{'pkgpart'} = $line_item->{'pkgpart'};
$detail->{'quantity'} = 1;
- $detail->{'section'} = $previous_section;
+ $detail->{'section'} = $multisection ? $previous_section
+ : $default_section;
$detail->{'description'} = &$escape_function($line_item->{'description'});
if ( exists $line_item->{'ext_description'} ) {
@{$detail->{'ext_description'}} = map {
}
}
-
- if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
+
+ if ( @pr_cust_bill && $self->enable_previous ) {
push @buf, ['','-----------'];
push @buf, [ $self->mt('Total Previous Balance'),
$money_char. sprintf("%10.2f", $pr_total) ];
ext_description => [],
};
$detail->{'ref'} = $line_item->{'pkgnum'};
+ $detail->{'pkgpart'} = $line_item->{'pkgpart'};
$detail->{'quantity'} = $line_item->{'quantity'};
$detail->{'section'} = $section;
$detail->{'description'} = &$escape_function($line_item->{'description'});
$detail->{'sdate'} = $line_item->{'sdate'};
$detail->{'edate'} = $line_item->{'edate'};
$detail->{'seconds'} = $line_item->{'seconds'};
+ $detail->{'svc_label'} = $line_item->{'svc_label'};
push @detail_items, $detail;
push @buf, ( [ $detail->{'description'},
$invoice_data{current_less_finance} =
sprintf('%.2f', $self->charged - $invoice_data{finance_amount} );
- if ( $multisection && !$conf->exists('disable_previous_balance')
+ # create a major section for previous balance if we have major sections,
+ # or if previous_section is in summary form
+ if ( ( $multisection && $self->enable_previous )
|| $conf->exists('previous_balance-summary_only') )
{
unshift @sections, $previous_section if $pr_total;
push @buf,['','-----------'];
push @buf,[$self->mt(
- $conf->exists('disable_previous_balance')
+ (!$self->enable_previous)
? 'Total Charges'
: 'Total New Charges'
),
$money_char. sprintf("%10.2f",$self->charged) ];
push @buf,['',''];
+ # calculate total, possibly including total owed on previous
+ # invoices
{
my $total = {};
my $item = 'Total';
$item = $conf->config('previous_balance-exclude_from_total')
|| 'Total New Charges'
if $conf->exists('previous_balance-exclude_from_total');
- my $amount = $self->charged +
- ( $conf->exists('disable_previous_balance') ||
- $conf->exists('previous_balance-exclude_from_total')
- ? 0
- : $pr_total
- );
+ my $amount = $self->charged;
+ if ( $self->enable_previous and !$conf->exists('previous_balance-exclude_from_total') ) {
+ $amount += $pr_total;
+ }
+
$total->{'total_item'} = &$embolden_function($self->mt($item));
$total->{'total_amount'} =
&$embolden_function( $other_money_char. sprintf( '%.2f', $amount ) );
$adjust_section->{'pretotal'} = $self->mt('New charges total').' '.
$other_money_char. sprintf('%.2f', $self->charged );
}
- }else{
+ } else {
push @total_items, $total;
}
push @buf,['','-----------'];
];
push @buf,['',''];
}
-
- unless ( $conf->exists('disable_previous_balance') ) {
+
+ # if we're showing previous invoices, also show previous
+ # credits and payments
+ if ( $self->enable_previous
+ and $self->can('_items_credits')
+ and $self->can('_items_payments') )
+ {
#foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
# 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'});
$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'};
$total->{'total_item'} = &$embolden_function($self->balance_due_msg);
$total->{'total_amount'} =
&$embolden_function(
- $other_money_char. sprintf('%.2f', $summarypage
- ? $self->charged +
- $self->billing_balance
- : $self->owed + $pr_total
+ $other_money_char. sprintf('%.2f', #why? $summarypage
+ # ? $self->charged +
+ # $self->billing_balance
+ # :
+ $self->owed + $pr_total
)
);
if ( $multisection && !$adjust_section->{sort_weight} ) {
} } @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;
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 {
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;
}
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
if ( $display->post_total && !$summarypage ) {
if (! $type || $type eq 'S') {
$late_subtotal{$section} += $cust_bill_pkg->setup
- if $cust_bill_pkg->setup != 0;
+ if $cust_bill_pkg->setup != 0
+ || $cust_bill_pkg->setup_show_zero;
}
if (! $type) {
$late_subtotal{$section} += $cust_bill_pkg->recur
- if $cust_bill_pkg->recur != 0;
+ if $cust_bill_pkg->recur != 0
+ || $cust_bill_pkg->recur_show_zero;
}
if ($type && $type eq 'R') {
$late_subtotal{$section} += $cust_bill_pkg->recur - $usage
- if $cust_bill_pkg->recur != 0;
+ if $cust_bill_pkg->recur != 0
+ || $cust_bill_pkg->recur_show_zero;
}
if ($type && $type eq 'U') {
if (! $type || $type eq 'S') {
$subtotal{$section} += $cust_bill_pkg->setup
- if $cust_bill_pkg->setup != 0;
+ if $cust_bill_pkg->setup != 0
+ || $cust_bill_pkg->setup_show_zero;
}
if (! $type) {
$subtotal{$section} += $cust_bill_pkg->recur
- if $cust_bill_pkg->recur != 0;
+ if $cust_bill_pkg->recur != 0
+ || $cust_bill_pkg->recur_show_zero;
}
if ($type && $type eq 'R') {
$subtotal{$section} += $cust_bill_pkg->recur - $usage
- if $cust_bill_pkg->recur != 0;
+ if $cust_bill_pkg->recur != 0
+ || $cust_bill_pkg->recur_show_zero;
}
if ($type && $type eq 'U') {
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',
my $maxlength = $conf->config('cust_bill-latex_lineitem_maxlength') || 50;
+ my $cust_main = $self->cust_main;#for per-agent cust_bill-line_item-ate_style
+
my @b = ();
my ($s, $r, $u) = ( undef, undef, undef );
foreach my $cust_bill_pkg ( @$cust_bill_pkgs )
}
}
+ my @cust_bill_pkg_display = $cust_bill_pkg->cust_bill_pkg_display;
+
warn "$me _items_cust_bill_pkg considering cust_bill_pkg ".
$cust_bill_pkg->billpkgnum. ", pkgnum ". $cust_bill_pkg->pkgnum. "\n"
if $DEBUG > 1;
}
#grep { !$_->summary || !$summary_page } # bunk!
grep { !$_->summary || $multisection }
- $cust_bill_pkg->cust_bill_pkg_display
+ @cust_bill_pkg_display
)
{
my $cust_pkg = $cust_bill_pkg->cust_pkg;
+ # which pkgpart to show for display purposes?
+ my $pkgpart = $cust_bill_pkg->pkgpart_override || $cust_pkg->pkgpart;
+
# start/end dates for invoice formats that do nonstandard
# things with them
- my %item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate');
+ my %item_dates = ();
+ %item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate')
+ unless $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1);
if ( (!$type || $type eq 'S')
&& ( $cust_bill_pkg->setup != 0
|| $cust_bill_pkg->recur_show_zero;
my @d = ();
+ my $svc_label;
unless ( $cust_pkg->part_pkg->hide_svc_detail
|| $cust_bill_pkg->hidden )
{
- push @d, map &{$escape_function}($_),
- $cust_pkg->h_labels_short($self->_date, undef, 'I')
+ my @svc_labels = map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date, undef, 'I');
+ push @d, @svc_labels
unless $cust_bill_pkg->pkgpart_override; #don't redisplay services
+ $svc_label = $svc_labels[0];
if ( $multilocation ) {
my $loc = $cust_pkg->location_label;
$s = {
_is_setup => 1,
description => $description,
- #pkgpart => $part_pkg->pkgpart,
+ pkgpart => $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,
+ svc_label => ($svc_label || ''),
};
};
my $description = ($is_summary && $type && $type eq 'U')
? "Usage charges" : $desc;
+ my $part_pkg = $cust_pkg->part_pkg;
+
+ #pry be a bit more efficient to look some of this conf stuff up
+ # outside the loop
unless (
$conf->exists('disable_line_item_date_ranges')
- || $cust_pkg->part_pkg->option('disable_line_item_date_ranges',1)
+ || $part_pkg->option('disable_line_item_date_ranges',1)
+ || ! $cust_bill_pkg->sdate
+ || ! $cust_bill_pkg->edate
) {
my $time_period;
- my $date_style = $conf->config('cust_bill-line_item-date_style');
- if ( $date_style eq 'month_of' ) {
- $time_period = time2str('The month of %B', $cust_bill_pkg->sdate);
+ my $date_style = '';
+ $date_style = $conf->config( 'cust_bill-line_item-date_style-non_monthly',
+ $cust_main->agentnum
+ )
+ if $part_pkg && $part_pkg->freq !~ /^1m?$/;
+ $date_style ||= $conf->config( 'cust_bill-line_item-date_style',
+ $cust_main->agentnum
+ );
+ if ( defined($date_style) && $date_style eq 'month_of' ) {
+ $time_period = $self->mt('The month of [_1]',
+ $self->time2str_local('%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. $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)";
}
my @d = ();
my @seconds = (); # for display of usage info
+ my $svc_label = '';
#at least until cust_bill_pkg has "past" ranges in addition to
#the "future" sdate/edate ones... see #3032
warn "$me _items_cust_bill_pkg adding service details\n"
if $DEBUG > 1;
- push @d, map &{$escape_function}($_),
- $cust_pkg->h_labels_short(@dates, 'I')
- #$cust_bill_pkg->edate,
- #$cust_bill_pkg->sdate)
+ my @svc_labels = map &{$escape_function}($_),
+ $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];
warn "$me _items_cust_bill_pkg done adding service details\n"
if $DEBUG > 1;
} else {
$r = {
description => $description,
- #pkgpart => $part_pkg->pkgpart,
+ pkgpart => $pkgpart,
pkgnum => $cust_bill_pkg->pkgnum,
amount => $amount,
recur_show_zero => $cust_bill_pkg->recur_show_zero,
quantity => $cust_bill_pkg->quantity,
%item_dates,
ext_description => \@d,
+ svc_label => ($svc_label || ''),
};
$r->{'seconds'} = \@seconds if grep {defined $_} @seconds;
}
} else {
$u = {
description => $description,
- #pkgpart => $part_pkg->pkgpart,
+ pkgpart => $pkgpart,
pkgnum => $cust_bill_pkg->pkgnum,
amount => $amount,
recur_show_zero => $cust_bill_pkg->recur_show_zero,
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),
};
}
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, {
# " (". 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),
};
}
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 )
};
}
join(', ', map { "#$_" } $plan->pkgnums)
if $list_pkgnums;
+ # discounts for non-integer months don't work anyway
+ $months = sprintf("%d", $months);
+
+{
description => $self->mt('Save [_1]% by paying for [_2] months',
$percent, $months),
push @search, "cust_main.agentnum = $1";
}
- #agentnum
+ #refnum
+ if ( $param->{'refnum'} =~ /^(\d+)$/ ) {
+ push @search, "cust_main.refnum = $1";
+ }
+
+ #custnum
if ( $param->{'custnum'} =~ /^(\d+)$/ ) {
push @search, "cust_bill.custnum = $1";
}
+ #customer classnum
+ if ( $param->{'cust_classnum'} ) {
+ my $classnums = $param->{'cust_classnum'};
+ $classnums = [ $classnums ] if !ref($classnums);
+ $classnums = [ grep /^\d+$/, @$classnums ];
+ push @search, 'cust_main.classnum in ('.join(',',@$classnums).')'
+ if @$classnums;
+ }
+
#_date
if ( $param->{_date} ) {
my($beginning, $ending) = @{$param->{_date}};