+=cut
+
+sub _items_usage_class_summary {
+ my $self = shift;
+ my %opt = @_;
+
+ my $escape = $opt{escape} || sub { $_[0] };
+ my $money_char = $opt{money_char};
+ my $invnum = $self->invnum;
+ my @classes = qsearch({
+ 'table' => 'usage_class',
+ 'select' => 'classnum, classname, SUM(amount) AS amount,'.
+ ' COUNT(*) AS calls, SUM(duration) AS duration',
+ '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')),
+ usage_section => 1,
+ subtotal => 0,
+ };
+ foreach my $class (@classes) {
+ $section->{subtotal} += $class->get('amount');
+ push @l, {
+ 'description' => &{$escape}($class->classname),
+ 'amount' => $money_char.sprintf('%.2f', $class->get('amount')),
+ 'quantity' => $class->get('calls'),
+ 'duration' => $class->get('duration'),
+ 'usage_classnum' => $class->classnum,
+ 'section' => $section,
+ };
+ }
+ $section->{subtotal} = $money_char.sprintf('%.2f', $section->{subtotal});
+ return @l;
+}
+
+=item _items_previous()
+
+ Returns an array of hashrefs, each hashref representing a line-item on
+ the current bill for previous unpaid invoices.
+
+ keys for each previous_item:
+ - amount (see notes)
+ - pkgnum
+ - description
+ - invnum
+ - _date
+
+ Payments and credits shown on this invoice may vary based on configuraiton.
+
+ when conf flag previous_balance-payments_since is set:
+ This method works backwards to rebuild the invoice as a snapshot in time.
+ The invoice displayed will have the balances owed, and payments made,
+ reflecting the state of the account at the time of invoice generation.
+
+=cut
+
+sub _items_previous {
+
+ my $self = shift;
+
+ # simple memoize
+ if ($self->get('_items_previous')) {
+ return sort { $a->{_date} <=> $b->{_date} }
+ values %{ $self->get('_items_previous') };
+ }
+
+ # Gets the customer's current balance and outstanding invoices.
+ my ($prev_balance, @open_invoices) = $self->previous;
+
+ my %invoices = map {
+ $_->invnum => $self->__items_previous_map_invoice($_)
+ } @open_invoices;
+
+ # Which credits and payments displayed on the bill will vary based on
+ # conf flag previous_balance-payments_since.
+ my @credits = $self->_items_credits();
+ my @payments = $self->_items_payments();
+
+
+ if ($self->conf->exists('previous_balance-payments_since')) {
+ # For each credit or payment, determine which invoices it was applied to.
+ # Manipulate data displayed so the invoice displayed appears as a
+ # snapshot in time... with previous balances and balance owed displayed
+ # as they were at the time of invoice creation.
+
+ my @credits_postbill = $self->_items_credits_postbill();
+ my @payments_postbill = $self->_items_payments_postbill();
+
+ my %pmnt_dupechk;
+ my %cred_dupechk;
+
+ # Each section below follows this pattern on a payment/credit
+ #
+ # - Dupe check, avoid adjusting for the same item twice
+ # - If invoice being adjusted for isn't in our list, add it
+ # - Adjust the invoice balance to refelct balnace without the
+ # credit or payment applied
+ #
+
+ # Working with payments displayed on this bill
+ for my $pmt_hash (@payments) {
+ my $pmt_obj = qsearchs('cust_pay', {paynum => $pmt_hash->{paynum}});
+ for my $cust_bill_pay ($pmt_obj->cust_bill_pay) {
+ next if exists $pmnt_dupechk{$cust_bill_pay->billpaynum};
+ $pmnt_dupechk{$cust_bill_pay->billpaynum} = 1;
+
+ my $invnum = $cust_bill_pay->invnum;
+
+ $invoices{$invnum} = $self->__items_previous_get_invoice($invnum)
+ unless exists $invoices{$invnum};
+
+ $invoices{$invnum}->{amount} += $cust_bill_pay->amount;
+ }
+ }
+
+ # Working with credits displayed on this bill
+ for my $cred_hash (@credits) {
+ my $cred_obj = qsearchs('cust_credit', {crednum => $cred_hash->{crednum}});
+ for my $cust_credit_bill ($cred_obj->cust_credit_bill) {
+ next if exists $cred_dupechk{$cust_credit_bill->creditbillnum};
+ $cred_dupechk{$cust_credit_bill->creditbillnum} = 1;
+
+ my $invnum = $cust_credit_bill->invnum;
+
+ $invoices{$invnum} = $self->__items_previous_get_invoice($invnum)
+ unless exists $invoices{$invnum};
+
+ $invoices{$invnum}->{amount} += $cust_credit_bill->amount;
+ }
+ }
+
+ # Working with both credits and payments which are not displayed
+ # on this bill, but which have affected this bill's balances
+ for my $postbill (@payments_postbill, @credits_postbill) {
+
+ if ($postbill->{billpaynum}) {
+ next if exists $pmnt_dupechk{$postbill->{billpaynum}};
+ $pmnt_dupechk{$postbill->{billpaynum}} = 1;
+ } elsif ($postbill->{creditbillnum}) {
+ next if exists $cred_dupechk{$postbill->{creditbillnum}};
+ $cred_dupechk{$postbill->{creditbillnum}} = 1;
+ } else {
+ die "Missing creditbillnum or billpaynum";
+ }
+
+ my $invnum = $postbill->{invnum};
+
+ $invoices{$invnum} = $self->__items_previous_get_invoice($invnum)
+ unless exists $invoices{$invnum};
+
+ $invoices{$invnum}->{amount} += $postbill->{amount};
+ }
+
+ # Make sure current invoice doesn't appear in previous items
+ delete $invoices{$self->invnum}
+ if exists $invoices{$self->invnum};
+
+ }
+
+ # Make sure amount is formatted as a dollar string
+ # (Formatting should happen on the template side, but is not?)
+ $invoices{$_}->{amount} = sprintf('%.2f',$invoices{$_}->{amount})
+ for keys %invoices;
+
+ $self->set('_items_previous', \%invoices);
+ return sort { $a->{_date} <=> $b->{_date} } values %invoices;
+
+}
+
+=item _items_previous_total
+
+ Return sum of amounts from all items returned by _items_previous
+ Results will vary based on invoicing conf flags
+
+=cut
+
+sub _items_previous_total {
+ my $self = shift;
+ my $tot = 0;
+ $tot += $_->{amount} for $self->_items_previous();
+ return $tot;
+}
+
+sub __items_previous_get_invoice {
+ # Helper function for _items_previous
+ #
+ # Read a record from cust_bill, return a hash of it's information
+ my ($self, $invnum) = @_;
+ die "Incorrect usage of __items_previous_get_invoice()" unless $invnum;
+
+ my $cust_bill = qsearchs('cust_bill', {invnum => $invnum});
+ return $self->__items_previous_map_invoice($cust_bill);
+}
+
+sub __items_previous_map_invoice {
+ # Helper function for _items_previous
+ #
+ # Transform a cust_bill object into a simple hash reference of the type
+ # required by _items_previous
+ my ($self, $cust_bill) = @_;
+ die "Incorrect usage of __items_previous_map_invoice" unless ref $cust_bill;
+
+ my $date = $self->conf->exists('invoice_show_prior_due_date')
+ ? 'due '.$cust_bill->due_date2str('short')
+ : $self->time2str_local('short', $cust_bill->_date);
+
+ return {
+ invnum => $cust_bill->invnum,
+ amount => $cust_bill->owed,
+ pkgnum => 'N/A',
+ _date => $cust_bill->_date,
+ description => join(' ',
+ $self->mt('Previous Balance, Invoice #'),
+ $cust_bill->invnum,
+ "($date)"
+ ),
+ }
+}
+
+=item _items_credits()
+
+ Return array of hashrefs containing credits to be shown as line-items
+ when rendering this bill.
+
+ keys for each credit item:
+ - crednum: id of payment
+ - amount: payment amount
+ - description: line item to be displayed on the bill
+
+ This method has three ways it selects which credits to display on
+ this bill:
+
+ 1) Default Case: No Conf flag for 'previous_balance-payments_since'
+
+ Returns credits that have been applied to this bill only
+
+ 2) Case:
+ Conf flag set for 'previous_balance-payments_since'
+
+ List all credits that have been recorded during the time period
+ between the timestamps of the last invoice and this invoice
+
+ 3) Case:
+ Conf flag set for 'previous_balance-payments_since'
+ $opt{'template'} eq 'statement'
+
+ List all payments that have been recorded between the timestamps
+ of the previous invoice and the following invoice.
+
+ This is used to give the customer a receipt for a payment
+ in the form of their last bill with the payment amended.
+
+ I am concerned with this implementation, but leaving in place as is
+ If this option is selected, while viewing an older bill, the old bill
+ will show ALL future credits for future bills, but no charges for
+ future bills. Somebody could be misled into believing they have a
+ large account credit when they don't. Also, interrupts the chain of
+ invoices as an account history... the customer could have two invoices
+ in their fileing cabinet, for two different dates, both with a line item
+ for the same duplicate credit. The accounting is technically accurate,
+ but somebody could easily become confused and think two credits were
+ made, when really those two line items on two different bills represent
+ only a single credit
+
+=cut
+
+sub _items_credits {
+
+ my $self= shift;
+
+ # Simple memoize
+ return @{$self->get('_items_credits')} if $self->get('_items_credits');
+
+ my %opt = @_;
+ my $template = $opt{template} || $self->get('_template');
+ my $trim_len = $opt{template} || $self->get('trim_len') || 40;
+
+ my @return;
+ my @cust_credit_objs;