$money_char $date_format $rdate_format $date_format_long );
use vars qw( $invoice_lines @buf ); #yuck
use Fcntl qw(:flock); #for spool_csv
+use Cwd;
use List::Util qw(min max);
use Date::Format;
use Text::Template 1.20;
use FS::payby;
use FS::bill_batch;
use FS::cust_bill_batch;
-use Cwd;
+use FS::cust_bill_pay_pkg;
+use FS::cust_credit_bill_pkg;
@ISA = qw( FS::cust_main_Mixin FS::Record );
cust_pay_batch
cust_bill_pay_batch
cust_bill_pkg
+ cust_bill_batch
)) {
foreach my $linked ( $self->$table() ) {
shift->cust_credited(@_);
}
-=item cust_bill_pay_pkgnum PKGNUM
+#=item cust_bill_pay_pkgnum PKGNUM
+#
+#Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice
+#with matching pkgnum.
+#
+#=cut
+#
+#sub cust_bill_pay_pkgnum {
+# my( $self, $pkgnum ) = @_;
+# map { $_ } #return $self->num_cust_bill_pay_pkgnum($pkgnum) unless wantarray;
+# sort { $a->_date <=> $b->_date }
+# qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum,
+# 'pkgnum' => $pkgnum,
+# }
+# );
+#}
+
+=item cust_bill_pay_pkg PKGNUM
Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice
-with matching pkgnum.
+applied against the matching pkgnum.
=cut
-sub cust_bill_pay_pkgnum {
+sub cust_bill_pay_pkg {
my( $self, $pkgnum ) = @_;
- map { $_ } #return $self->num_cust_bill_pay_pkgnum($pkgnum) unless wantarray;
- sort { $a->_date <=> $b->_date }
- qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum,
- 'pkgnum' => $pkgnum,
- }
- );
+
+ qsearch({
+ 'select' => 'cust_bill_pay_pkg.*',
+ 'table' => 'cust_bill_pay_pkg',
+ 'addl_from' => ' LEFT JOIN cust_bill_pay USING ( billpaynum ) '.
+ ' LEFT JOIN cust_bill_pkg USING ( billpkgnum ) ',
+ 'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum.
+ " AND cust_bill_pkg.pkgnum = $pkgnum",
+ });
+
}
-=item cust_credited_pkgnum PKGNUM
+#=item cust_credited_pkgnum PKGNUM
+#
+#=item cust_credit_bill_pkgnum PKGNUM
+#
+#Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice
+#with matching pkgnum.
+#
+#=cut
+#
+#sub cust_credited_pkgnum {
+# my( $self, $pkgnum ) = @_;
+# map { $_ } #return $self->num_cust_credit_bill_pkgnum($pkgnum) unless wantarray;
+# sort { $a->_date <=> $b->_date }
+# qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum,
+# 'pkgnum' => $pkgnum,
+# }
+# );
+#}
+#
+#sub cust_credit_bill_pkgnum {
+# shift->cust_credited_pkgnum(@_);
+#}
-=item cust_credit_bill_pkgnum PKGNUM
+=item cust_credit_bill_pkg PKGNUM
-Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice
-with matching pkgnum.
+Returns all credit applications (see L<FS::cust_credit_bill>) for this invoice
+applied against the matching pkgnum.
=cut
-sub cust_credited_pkgnum {
+sub cust_credit_bill_pkg {
my( $self, $pkgnum ) = @_;
- map { $_ } #return $self->num_cust_credit_bill_pkgnum($pkgnum) unless wantarray;
- sort { $a->_date <=> $b->_date }
- qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum,
- 'pkgnum' => $pkgnum,
- }
- );
+
+ qsearch({
+ 'select' => 'cust_credit_bill_pkg.*',
+ 'table' => 'cust_credit_bill_pkg',
+ 'addl_from' => ' LEFT JOIN cust_credit_bill USING ( creditbillnum ) '.
+ ' LEFT JOIN cust_bill_pkg USING ( billpkgnum ) ',
+ 'extra_sql' => ' WHERE cust_bill_pkg.invnum = '. $self->invnum.
+ " AND cust_bill_pkg.pkgnum = $pkgnum",
+ });
+
}
-sub cust_credit_bill_pkgnum {
- shift->cust_credited_pkgnum(@_);
+=item cust_bill_batch
+
+Returns all invoice batch records (L<FS::cust_bill_batch>) for this invoice.
+
+=cut
+
+sub cust_bill_batch {
+ my $self = shift;
+ qsearch('cust_bill_batch', { 'invnum' => $self->invnum });
}
=item tax
my $balance = 0;
$balance += $_->setup + $_->recur for $self->cust_bill_pkg_pkgnum($pkgnum);
- $balance -= $_->amount for $self->cust_bill_pay_pkgnum($pkgnum);
- $balance -= $_->amount for $self->cust_credited_pkgnum($pkgnum);
+ $balance -= $_->amount for $self->cust_bill_pay_pkg($pkgnum);
+ $balance -= $_->amount for $self->cust_credit_bill_pkg($pkgnum);
$balance = sprintf( "%.2f", $balance);
$balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
);
my $embolden_function = $embolden_functions{$format};
+ my %newline_tokens = ( 'latex' => '\\\\',
+ 'html' => '<br>',
+ 'template' => "\n",
+ );
+ my $newline_token = $newline_tokens{$format};
+
warn "$me generating template variables\n"
if $DEBUG > 1;
#invoice from info
'company_name' => scalar( $conf->config('company_name', $agentnum) ),
'company_address' => join("\n", $conf->config('company_address', $agentnum) ). "\n",
+ 'company_phonenum'=> scalar( $conf->config('company_phonenum', $agentnum) ),
'returnaddress' => $returnaddress,
'agent' => &$escape_function($cust_main->agent->agent),
sprintf('%.2f', $pr_total),
'summarized' => $summarypage ? 'Y' : '',
};
- $previous_section->{posttotal} = '0 / 30 / 60/ 90 days overdue '.
+ $previous_section->{posttotal} = '0 / 30 / 60 / 90 days overdue '.
join(' / ', map { $cust_main->balance_date_range(@$_) }
$self->_prior_month30s
)
}
}
-
+
if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
push @buf, ['','-----------'];
push @buf, [ 'Total Previous Balance',
if $DEBUG > 1;
my ($didsummary,$minutes) = $self->_did_summary;
- my $didsummary_desc = 'DID Activity Summary (Past 30 days)';
+ my $didsummary_desc = 'DID Activity Summary (since last invoice)';
push @detail_items,
{ 'description' => $didsummary_desc,
'ext_description' => [ $didsummary, $minutes ],
push @buf,[$self->balance_due_msg, $money_char.
sprintf("%10.2f", $balance_due ) ];
}
+
+ if ( $conf->exists('previous_balance-show_credit')
+ and $cust_main->balance < 0 ) {
+ my $credit_total = {
+ 'total_item' => &$embolden_function($self->credit_balance_msg),
+ 'total_amount' => &$embolden_function(
+ $other_money_char. sprintf('%.2f', -$cust_main->balance)
+ ),
+ };
+ if ( $multisection ) {
+ $adjust_section->{'posttotal'} .= $newline_token .
+ $credit_total->{'total_item'} . ' ' . $credit_total->{'total_amount'};
+ }
+ else {
+ push @total_items, $credit_total;
+ }
+ push @buf,['','-----------'];
+ push @buf,[$self->credit_balance_msg, $money_char.
+ sprintf("%10.2f", -$cust_main->balance ) ];
+ }
}
if ( $multisection ) {
my ($file, $logofile, $barcodefile) = $self->print_latex(@_);
my $ps = generate_ps($file);
unlink($logofile);
- unlink($barcodefile);
+ unlink($barcodefile) if $barcodefile;
$ps;
}
my ($file, $logofile, $barcodefile) = $self->print_latex(@_);
my $pdf = generate_pdf($file);
unlink($logofile);
- unlink($barcodefile);
+ unlink($barcodefile) if $barcodefile;
$pdf;
}
$duedate;
}
+sub credit_balance_msg { 'Credit Balance Remaining' }
+
=item invnum_date_pretty
Returns a string with the invoice number and date, for example:
}
} @sections;
push @early, @$extra_sections if $extra_sections;
-
+
sort { $a->{sort_weight} <=> $b->{sort_weight} } @early;
}
sub _did_summary {
my $self = shift;
my $end = $self->_date;
- my $start = $end - 2592000; # 30 days
+
+ # start at date of previous invoice + 1 second or 0 if no previous invoice
+ my $start = $self->scalar_sql("SELECT max(_date) FROM cust_bill WHERE custnum = ? and invnum != ?",$self->custnum,$self->invnum);
+ $start = 0 if !$start;
+ $start++;
+
my $cust_main = $self->cust_main;
my @pkgs = $cust_main->all_pkgs;
my($num_activated,$num_deactivated,$num_portedin,$num_portedout,$minutes)
}
- warn "$me _items_cust_bill_pkg adding details\n"
- if $DEBUG > 1;
+ unless ( $is_summary ) {
+ warn "$me _items_cust_bill_pkg adding details\n"
+ if $DEBUG > 1;
- push @d, $cust_bill_pkg->details(%details_opt)
- unless $is_summary; # || ($type && $type eq 'R');
+ #instead of omitting details entirely in this case (unwanted side
+ # effects), just omit CDRs
+ $details_opt{'format_function'} = sub { () }
+ if $type && $type eq 'R';
+
+ push @d, $cust_bill_pkg->details(%details_opt);
+ }
warn "$me _items_cust_bill_pkg calculating amount\n"
if $DEBUG > 1;
my $amount = 0;
if (!$type) {
$amount = $cust_bill_pkg->recur;
- }elsif($type eq 'R') {
+ } elsif ($type eq 'R') {
$amount = $cust_bill_pkg->recur - $cust_bill_pkg->usage;
- }elsif($type eq 'U') {
+ } elsif ($type eq 'U') {
$amount = $cust_bill_pkg->usage;
}