use FS::cust_svc;
use FS::cust_bill;
use FS::cust_bill_pkg;
+use FS::cust_bill_pkg_display;
use FS::cust_pay;
use FS::cust_pay_pending;
use FS::cust_pay_void;
=item spool_cdr - Enable individual CDR spooling, empty or `Y'
+=item dundate - a suggestion to events (see L<FS::part_bill_event">) to delay until this unix timestamp
+
=item squelch_cdr - Discourage individual CDR printing, empty or `Y'
=back
$self->ncancelled_pkgs;
foreach my $cust_pkg ( @cancel_pkgs ) {
- my $error = $cust_pkg->cancel;
+ my $cpr = $cust_pkg->last_cust_pkg_reason('expire');
+ my $error = $cust_pkg->cancel($cpr ? ( 'reason' => $cpr->reasonnum,
+ 'reason_otaker' => $cpr->otaker
+ )
+ : ()
+ );
warn "Error cancelling expired pkg ". $cust_pkg->pkgnum.
" for custnum ". $self->custnum. ": $error"
if $error;
$self->ncancelled_pkgs;
foreach my $cust_pkg ( @susp_pkgs ) {
- my $error = $cust_pkg->suspend;
+ my $cpr = $cust_pkg->last_cust_pkg_reason('adjourn')
+ if ($cust_pkg->adjourn && $cust_pkg->adjourn < $^T);
+ my $error = $cust_pkg->suspend($cpr ? ( 'reason' => $cpr->reasonnum,
+ 'reason_otaker' => $cpr->otaker
+ )
+ : ()
+ );
+
warn "Error suspending package ". $cust_pkg->pkgnum.
" for custnum ". $self->custnum. ": $error"
if $error;
sub bill {
my( $self, %options ) = @_;
return '' if $self->payby eq 'COMP';
- local $DEBUG = 1;
warn "$me bill customer ". $self->custnum. "\n"
if $DEBUG;
$self->select_for_update; #mutex
my @cust_bill_pkg = ();
- my @appended_cust_bill_pkg = ();
###
# find the packages which are due for billing, find out how much they are
'cust_pkg' => $cust_pkg,
'precommit_hooks' => \@precommit_hooks,
'line_items' => \@cust_bill_pkg,
- 'appended_line_items' => \@appended_cust_bill_pkg,
'setup' => \$total_setup,
'recur' => \$total_recur,
'tax_matrix' => \%taxlisthash,
} #foreach my $cust_pkg
- push @cust_bill_pkg, @appended_cust_bill_pkg;
-
unless ( @cust_bill_pkg ) { #don't create an invoice w/o line items
#but do commit any package date cycling that happened
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
return "can't charge postal invoice fee for customer ".
$self->custnum. ": $postal_pkg";
}
- if ( $postal_pkg ) {
+ if ( $postal_pkg &&
+ ( scalar( grep { $_->recur && $_->recur > 0 } @cust_bill_pkg) ||
+ !$conf->exists('postal_invoice-recurring_only')
+ )
+ )
+ {
foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) {
my $error =
$self->_make_lines( 'part_pkg' => $part_pkg,
'cust_pkg' => $postal_pkg,
'precommit_hooks' => \@precommit_hooks,
'line_items' => \@cust_bill_pkg,
- 'appended_line_items' => \@appended_cust_bill_pkg,
'setup' => \$total_setup,
'recur' => \$total_recur,
'tax_matrix' => \%taxlisthash,
my $cust_pkg = $params{cust_pkg} or die "no cust_pkg specified";
my $precommit_hooks = $params{precommit_hooks} or die "no package specified";
my $cust_bill_pkgs = $params{line_items} or die "no line buffer specified";
- my $appended_cust_bill_pkg = $params{appended_line_items}
- or die "no appended line buffer specified";
my $total_setup = $params{setup} or die "no setup accumulator specified";
my $total_recur = $params{recur} or die "no recur accumulator specified";
my $taxlisthash = $params{tax_matrix} or die "no tax accumulator specified";
# only for figuring next bill date, nothing else, so, reset $sdate again
# here
$sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
+ #no need, its in $hash{last_bill}# my $last_bill = $cust_pkg->last_bill;
$cust_pkg->last_bill($sdate);
if ( $part_pkg->freq =~ /^\d+$/ ) {
warn " charges (setup=$setup, recur=$recur); adding line items\n"
if $DEBUG > 1;
+
+ my @cust_pkg_detail = map { $_->detail } $cust_pkg->cust_pkg_detail('I');
+ if ( $DEBUG > 1 ) {
+ warn " adding customer package invoice detail: $_\n"
+ foreach @cust_pkg_detail;
+ }
+ push @details, @cust_pkg_detail;
+
my $cust_bill_pkg = new FS::cust_bill_pkg {
'pkgnum' => $cust_pkg->pkgnum,
'setup' => $setup,
'recur' => $recur,
'unitrecur' => $unitrecur,
'quantity' => $cust_pkg->quantity,
- 'sdate' => $sdate,
- 'edate' => $cust_pkg->bill,
'details' => \@details,
};
+
+ if ( $part_pkg->option('recur_temporality', 1) eq 'preceding' ) {
+ $cust_bill_pkg->sdate( $hash{last_bill} );
+ $cust_bill_pkg->edate( $sdate - 86399 ); #60s*60m*24h-1
+ } else { #if ( $part_pkg->option('recur_temporality', 1) eq 'upcoming' ) {
+ $cust_bill_pkg->sdate( $sdate );
+ $cust_bill_pkg->edate( $cust_pkg->bill );
+ }
+
$cust_bill_pkg->pkgpart_override($part_pkg->pkgpart)
unless $part_pkg->pkgpart == $real_pkgpart;
# handle taxes
###
- unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
-
- #some garbage disappears on cust_bill_pkg refactor
- my $err_or_cust_bill_pkg =
- $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg);
-
- return $err_or_cust_bill_pkg
- unless ( ref($err_or_cust_bill_pkg) );
-
- push @$cust_bill_pkgs, @$err_or_cust_bill_pkg;
+ my $error =
+ $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg, $cust_pkg);
+ return $error if $error;
- } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP'
+ push @$cust_bill_pkgs, $cust_bill_pkg;
} #if $setup != 0 || $recur != 0
} #if $line_items
- if ( $part_pkg->can('append_cust_bill_pkgs') ) {
- my %param = ( 'precommit_hooks' => $precommit_hooks, );
- my ($more_cust_bill_pkgs) =
- eval { $part_pkg->append_cust_bill_pkgs( $cust_pkg, \$sdate, \%param ) };
-
- return "$@ running append_cust_bill_pkgs for $cust_pkg\n"
- if ( $@ );
- return "$more_cust_bill_pkgs"
- unless ( ref($more_cust_bill_pkgs) );
-
- foreach my $cust_bill_pkg ( @{$more_cust_bill_pkgs} ) {
-
- $cust_bill_pkg->pkgpart_override($part_pkg->pkgpart)
- unless $part_pkg->pkgpart == $real_pkgpart;
-
- unless ($cust_bill_pkg->duplicate) {
- $$total_setup += $cust_bill_pkg->setup;
- $$total_recur += $cust_bill_pkg->recur;
-
- ###
- # handle taxes
- ###
-
- unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
-
- #some garbage disappears on cust_bill_pkg refactor
- my $err_or_cust_bill_pkg =
- $self->_handle_taxes($part_pkg, $taxlisthash, $cust_bill_pkg);
-
- return $err_or_cust_bill_pkg
- unless ( ref($err_or_cust_bill_pkg) );
-
- push @$appended_cust_bill_pkg, @$err_or_cust_bill_pkg;
-
- } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP'
- }
- }
- }
+ '';
}
my $part_pkg = shift;
my $taxlisthash = shift;
my $cust_bill_pkg = shift;
+ my $cust_pkg = shift;
my %cust_bill_pkg = ();
my %taxes = ();
: '';
my @classes;
- push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->type eq 'U';
+ #push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->type eq 'U';
+ push @classes, $cust_bill_pkg->usage_classes if $cust_bill_pkg->usage;
push @classes, 'setup' if $cust_bill_pkg->setup;
push @classes, 'recur' if $cust_bill_pkg->recur;
if ( $conf->exists('enable_taxproducts')
&& (scalar($part_pkg->part_pkg_taxoverride) || $part_pkg->has_taxproduct)
+ && ( $self->tax !~ /Y/i && $self->payby ne 'COMP' )
)
{
$taxes{''} = $err_or_ref;
}
- }else{
+ } elsif ( $self->tax !~ /Y/i && $self->payby ne 'COMP' ) {
my %taxhash = map { $_ => $self->get("$prefix$_") }
qw( state county country );
$part_pkg->taxclass ). "\n";
}
- } #if $conf->exists('enable_taxproducts')
-
- # XXX all this goes away with cust_bill_pay refactor
-
- $cust_bill_pkg{setup} = $cust_bill_pkg if $cust_bill_pkg->setup;
- $cust_bill_pkg{recur} = $cust_bill_pkg if $cust_bill_pkg->recur;
-
- #split setup and recur
- if ($cust_bill_pkg->setup && $cust_bill_pkg->recur) {
- my $cust_bill_pkg_recur = new FS::cust_bill_pkg { $cust_bill_pkg->hash };
- $cust_bill_pkg->set('details', []);
- $cust_bill_pkg->recur(0);
- $cust_bill_pkg->type('');
- $cust_bill_pkg{recur} = $cust_bill_pkg_recur;
- }
-
- #split usage from recur
- my $usage = $cust_bill_pkg->usage;
- if ($usage) {
- my $cust_bill_pkg_usage =
- new FS::cust_bill_pkg { $cust_bill_pkg{recur}->hash };
- $cust_bill_pkg_usage->recur($usage);
- $cust_bill_pkg{recur}->recur( $cust_bill_pkg{recur}->recur - $usage );
- $cust_bill_pkg{recur}->type( '' );
- $cust_bill_pkg{''} = $cust_bill_pkg_usage;
- }
-
- #subdivide usage by usage_class
- if (exists($cust_bill_pkg{''})) {
- foreach my $class (grep {$_} @classes) {
- my $usage = $cust_bill_pkg{''}->usage($class);
- my $cust_bill_pkg_usage =
- new FS::cust_bill_pkg { $cust_bill_pkg{''}->hash };
- $cust_bill_pkg_usage->recur($usage);
- $cust_bill_pkg{''}->recur( $cust_bill_pkg{''}->recur - $usage );
- $cust_bill_pkg{$class} = $cust_bill_pkg_usage;
+ } #if $conf->exists('enable_taxproducts') ...
+
+ my @display = ();
+ if ( $conf->exists('separate_usage') ) {
+ my $section = $cust_pkg->part_pkg->option('usage_section', 'Hush!');
+ my $summary = $cust_pkg->part_pkg->option('summarize_usage', 'Hush!');
+ push @display, new FS::cust_bill_pkg_display { type => 'S' };
+ push @display, new FS::cust_bill_pkg_display { type => 'R' };
+ push @display, new FS::cust_bill_pkg_display { type => 'U',
+ section => $section
+ };
+ if ($section && $summary) {
+ $display[2]->post_total('Y');
+ push @display, new FS::cust_bill_pkg_display { type => 'U',
+ summary => 'Y',
+ }
}
- $cust_bill_pkg{''}->set('details', []);
- delete $cust_bill_pkg{''} unless $cust_bill_pkg{''}->recur;
}
+ $cust_bill_pkg->set('display', \@display);
- foreach my $key (keys %cust_bill_pkg) {
+ my %tax_cust_bill_pkg = $cust_bill_pkg->disintegrate;
+ foreach my $key (keys %tax_cust_bill_pkg) {
my @taxes = @{ $taxes{$key} };
- my $cust_bill_pkg = $cust_bill_pkg{$key};
+ my $tax_cust_bill_pkg = $tax_cust_bill_pkg{$key};
foreach my $tax ( @taxes ) {
my $taxname = ref( $tax ). ' '. $tax->taxnum;
if ( exists( $taxlisthash->{ $taxname } ) ) {
- push @{ $taxlisthash->{ $taxname } }, $cust_bill_pkg;
+ push @{ $taxlisthash->{ $taxname } }, $tax_cust_bill_pkg;
}else{
- $taxlisthash->{ $taxname } = [ $tax, $cust_bill_pkg ];
+ $taxlisthash->{ $taxname } = [ $tax, $tax_cust_bill_pkg ];
}
}
}
- # sort setup,recur,'', and the rest numeric && return
- my @result = map { $cust_bill_pkg{$_} }
- sort { my $ad = ($a=~/^\d+$/); my $bd = ($b=~/^\d+$/);
- ( $ad cmp $bd ) || ( $ad ? $a<=>$b : $b cmp $a )
- }
- keys %cust_bill_pkg;
-
- \@result;
+ '';
}
sub _gather_taxes {
# 3: insert
##
- foreach my $cust_event ( @cust_event ) {
+ unless( $opt{testonly} ) {
+ foreach my $cust_event ( @cust_event ) {
- my $error = $cust_event->insert();
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
+ my $error = $cust_event->insert();
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;