$detail->{'edate'} = $line_item->{'edate'};
$detail->{'seconds'} = $line_item->{'seconds'};
$detail->{'svc_label'} = $line_item->{'svc_label'};
+ $detail->{'usage_item'} = $line_item->{'usage_item'};
push @detail_items, $detail;
push @buf, ( [ $detail->{'description'},
}
$invoice_data{summary_subtotals} = \@summary_subtotals;
+ # usage subtotals
+ if ( $conf->exists('usage_class_summary')
+ and $self->can('_items_usage_class_summary') ) {
+ my @usage_subtotals = $self->_items_usage_class_summary(escape => $escape_function);
+ if ( @usage_subtotals ) {
+ unshift @sections, $usage_subtotals[0]->{section};
+ unshift @detail_items, @usage_subtotals;
+ }
+ }
+
+ # invoice history "section" (not really a section)
+ # not to be included in any subtotals, completely independent of
+ # everything...
+ if ( $conf->exists('previous_invoice_history') ) {
+ my %history;
+ my %monthorder;
+ foreach my $cust_bill ( $cust_main->cust_bill ) {
+ # XXX hardcoded format, and currently only 'charged'; add other fields
+ # if they become necessary
+ my $date = $self->time2str_local('%b %Y', $cust_bill->_date);
+ $history{$date} ||= 0;
+ $history{$date} += $cust_bill->charged;
+ # just so we have a numeric sort key
+ $monthorder{$date} ||= $cust_bill->_date;
+ }
+ my @sorted_months = sort { $monthorder{$a} <=> $monthorder{$b} }
+ keys %history;
+ my @sorted_amounts = map { sprintf('%.2f', $history{$_}) } @sorted_months;
+ $invoice_data{monthly_history} = [ \@sorted_months, \@sorted_amounts ];
+ }
+
# debugging hook: call this with 'diag' => 1 to just get a hash of
# the invoice variables
return \%invoice_data if ( $params{'diag'} );
sub _items_nontax {
my $self = shift;
- grep { $_->pkgnum } $self->cust_bill_pkg;
+ # The order of these is important. Bundled line items will be merged into
+ # the most recent non-hidden item, so it needs to be the one with:
+ # - the same pkgnum
+ # - the same start date
+ # - no pkgpart_override
+ #
+ # So: sort by pkgnum,
+ # then by sdate
+ # then sort the base line item before any overrides
+ # then sort hidden before non-hidden add-ons
+ # then sort by override pkgpart (for consistency)
+ sort { $a->pkgnum <=> $b->pkgnum or
+ $a->sdate <=> $b->sdate or
+ ($a->pkgpart_override ? 0 : -1) or
+ ($b->pkgpart_override ? 0 : 1) or
+ $b->hidden cmp $a->hidden or
+ $a->pkgpart_override <=> $b->pkgpart_override
+ }
+ # and of course exclude taxes and fees
+ grep { $_->pkgnum > 0 } $self->cust_bill_pkg;
}
sub _items_fee {
}
} # otherwise include them all in the main section
# XXX what to do when sectioning by location?
+
+ my @ext_desc;
+ my %base_invnums; # invnum => invoice date
+ foreach ($cust_bill_pkg->cust_bill_pkg_fee) {
+ if ($_->base_invnum) {
+ my $base_bill = FS::cust_bill->by_key($_->base_invnum);
+ my $base_date = $self->time2str_local('short', $base_bill->_date)
+ if $base_bill;
+ $base_invnums{$_->base_invnum} = $base_date || '';
+ }
+ }
+ foreach (sort keys(%base_invnums)) {
+ next if $_ == $self->invnum;
+ push @ext_desc,
+ $self->mt('from invoice \\#[_1] on [_2]', $_, $base_invnums{$_});
+ }
push @items,
{ feepart => $cust_bill_pkg->feepart,
amount => sprintf('%.2f', $cust_bill_pkg->setup + $cust_bill_pkg->recur),
description => $part_fee->itemdesc_locale($self->cust_main->locale),
+ ext_description => \@ext_desc
# sdate/edate?
- # ext_description referencing the base_invnum?
};
}
@items;
@cust_bill_pkg_display = grep { !$_->summary }
@cust_bill_pkg_display;
}
+
+ my $classname = ''; # package class name, will fill in later
+
foreach my $display (@cust_bill_pkg_display) {
warn "$me _items_cust_bill_pkg considering cust_bill_pkg_display ".
%item_dates = map { $_ => $cust_bill_pkg->$_ } ('sdate', 'edate')
unless $part_pkg->option('disable_line_item_date_ranges',1);
+ # not normally used, but pass this to the template anyway
+ $classname = $part_pkg->classname;
+
if ( (!$type || $type eq 'S')
&& ( $cust_bill_pkg->setup != 0
|| $cust_bill_pkg->setup_show_zero
my @d = ();
my $svc_label;
+
+ # always pass the svc_label through to the template, even if
+ # not displaying it as an ext_description
+ my @svc_labels = map &{$escape_function}($_),
+ $cust_pkg->h_labels_short($self->_date, undef, 'I');
+
+ $svc_label = $svc_labels[0];
+
unless ( $cust_pkg->part_pkg->hide_svc_detail
|| $cust_bill_pkg->hidden )
{
- 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];
-
my $lnum = $cust_main ? $cust_main->ship_locationnum
: $self->prospect_main->locationnum;
# show the location label if it's not the customer's default
push @dates, $prev->sdate if $prev;
push @dates, undef if !$prev;
+ my @svc_labels = map &{$escape_function}($_),
+ $cust_pkg->h_labels_short(@dates, 'I');
+ $svc_label = $svc_labels[0];
+
# show service labels, unless...
# the package is set not to display them
unless ( $part_pkg->hide_svc_detail
warn "$me _items_cust_bill_pkg adding service details\n"
if $DEBUG > 1;
- 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;
pkgpart => $pkgpart,
pkgnum => $cust_bill_pkg->pkgnum,
amount => $amount,
+ usage_item => 1,
recur_show_zero => $cust_bill_pkg->recur_show_zero,
%item_dates,
ext_description => \@d,