diff options
author | jeff <jeff> | 2009-07-17 01:44:24 +0000 |
---|---|---|
committer | jeff <jeff> | 2009-07-17 01:44:24 +0000 |
commit | 725f4954f7d4efaf7cd3fd5e402a5dacd5d185b9 (patch) | |
tree | efc694a364d3b5cb7715c67dd3b676a720c5c500 | |
parent | 72758ac1d94a4ce47e3160ee6227a5e191911c57 (diff) |
bill usage when cancelling package
-rw-r--r-- | FS/FS/Conf.pm | 7 | ||||
-rw-r--r-- | FS/FS/cust_main.pm | 38 | ||||
-rw-r--r-- | FS/FS/cust_pkg.pm | 17 | ||||
-rw-r--r-- | FS/FS/part_pkg/voip_cdr.pm | 59 |
4 files changed, 81 insertions, 40 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index eb046ee42..1d937b781 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -1494,6 +1494,13 @@ worry that config_items is freeside-specific and icky. }, { + 'key' => 'bill_usage_on_cancel', + 'section' => 'billing', + 'description' => 'Enable automatic generation of an invoice for usage when a package is cancelled. Not all packages can do this. Usage data must already be available.', + 'type' => 'checkbox', + }, + + { 'key' => 'require_cardname', 'section' => 'billing', 'description' => 'Require an "Exact name on card" to be entered explicitly; don\'t default to using the first and last name.', diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 286d3332a..1f063d950 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2201,12 +2201,16 @@ Available options are: =item ban - can be set true to ban this customer's credit card or ACH information, if present. +=item nobill - can be set true to skip billing if it might otherwise be done. + =back Always returns a list: an empty list on success or a list of errors. =cut +# nb that dates are not specified as valid options to this method + sub cancel { my( $self, %opt ) = @_; @@ -2232,6 +2236,13 @@ sub cancel { my @pkgs = $self->ncancelled_pkgs; + if ( !$opt{nobill} && $conf->exists('bill_usage_on_cancel') ) { + $opt{nobill} = 1; + my $error = $self->bill( pkg_list => [ @pkgs ], cancel => 1 ); + warn "Error billing during cancel, custnum ". $self->custnum. ": $error" + if $error; + } + warn "$me cancelling ". scalar($self->ncancelled_pkgs). "/". scalar(@pkgs). " packages for customer ". $self->custnum. "\n" if $DEBUG; @@ -2441,6 +2452,13 @@ An array ref of specific packages (objects) to attempt billing, instead trying a Used in conjunction with the I<time> option, this option specifies the date of for the generated invoices. Other calculations, such as whether or not to generate the invoice in the first place, are not affected. +=item cancel + +This boolean value informs the us that the package is being cancelled. This +typically might mean not charging the normal recurring fee but only usage +fees since the last billing. Setup charges may be charged. Not all package +plans support this feature (they tend to charge 0). + =back =cut @@ -2479,7 +2497,8 @@ sub bill { my %taxlisthash; my @precommit_hooks = (); - foreach my $cust_pkg ( $self->ncancelled_pkgs ) { + $options{ pkg_list } ||= [ $self->ncancelled_pkgs ]; #param checks? + foreach my $cust_pkg ( @{ $options{ pkg_list } } ) { warn " bill package ". $cust_pkg->pkgnum. "\n" if $DEBUG > 1; @@ -2537,6 +2556,8 @@ sub bill { } elsif ( $postal_pkg ) { foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) { + my %postal_options = %options; + delete $postal_options{cancel}; my $error = $self->_make_lines( 'part_pkg' => $part_pkg, 'cust_pkg' => $postal_pkg, @@ -2546,7 +2567,7 @@ sub bill { 'recur' => \$total_recur, 'tax_matrix' => \%taxlisthash, 'time' => $time, - 'options' => \%options, + 'options' => \%postal_options, ); if ($error) { $dbh->rollback if $oldAutoCommit; @@ -2822,6 +2843,7 @@ sub _make_lines { || ( $part_pkg->plan eq 'voip_cdr' && $part_pkg->option('bill_every_call') ) + || ( $options{cancel} ) ) { # XXX should this be a package event? probably. events are called @@ -2835,18 +2857,22 @@ sub _make_lines { $lineitems++; # XXX shared with $recur_prog - $sdate = $cust_pkg->bill || $cust_pkg->setup || $time; + $sdate = ( $options{cancel} ? $cust_pkg->last_bill : $cust_pkg->bill ) + || $cust_pkg->setup + || $time; #over two params! lets at least switch to a hashref for the rest... my $increment_next_bill = ( $part_pkg->freq ne '0' && ( $cust_pkg->getfield('bill') || 0 ) <= $time + && !$options{cancel} ); my %param = ( 'precommit_hooks' => $precommit_hooks, 'increment_next_bill' => $increment_next_bill, ); - $recur = eval { $cust_pkg->calc_recur( \$sdate, \@details, \%param ) }; - return "$@ running calc_recur for $cust_pkg\n" + my $method = $options{cancel} ? 'calc_cancel' : 'calc_recur'; + $recur = eval { $cust_pkg->$method( \$sdate, \@details, \%param ) }; + return "$@ running $method for $cust_pkg\n" if ( $@ ); if ( $increment_next_bill ) { @@ -2926,9 +2952,11 @@ sub _make_lines { 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 + $cust_bill_pkg->edate( $time ) if $options{cancel}; } 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->edate( $time ) if $options{cancel}; } $cust_bill_pkg->pkgpart_override($part_pkg->pkgpart) diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index d2f0690eb..f56402377 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -560,6 +560,8 @@ Available options are: =item date - can be set to a unix style timestamp to specify when to cancel (expire) +=item nobill - can be set true to skip billing if it might otherwise be done. + =back If there is an error, returns the error, otherwise returns false. @@ -570,6 +572,8 @@ sub cancel { my( $self, %options ) = @_; my $error; + my $conf = new FS::Conf; + warn "cust_pkg::cancel called with options". join(', ', map { "$_: $options{$_}" } keys %options ). "\n" if $DEBUG; @@ -595,6 +599,19 @@ sub cancel { my $date = $options{date} if $options{date}; # expire/cancel later $date = '' if ($date && $date <= time); # complain instead? + #race condition: usage could be ongoing until unprovisioned + #resolved by performing a change package instead (which unprovisions) and + #later cancelling + if ( !$options{nobill} && !$date && $conf->exists('bill_usage_on_cancel') ) { + my $error = + $self->cust_main->bill( pkg_list => [ $self ], cancel => 1 ); + warn "Error billing during cancel, custnum ". + #$self->cust_main->custnum. ": $error" + ": $error" + if $error; + } + + my $cancel_time = $options{'time'} || time; if ( $options{'reason'} ) { diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm index e6044b174..5a43403f7 100644 --- a/FS/FS/part_pkg/voip_cdr.pm +++ b/FS/FS/part_pkg/voip_cdr.pm @@ -12,6 +12,7 @@ use FS::cdr; use FS::rate; use FS::rate_prefix; use FS::rate_detail; +use FS::part_pkg::recur_Common; @ISA = qw(FS::part_pkg::prorate); @@ -24,12 +25,6 @@ tie my %rating_method, 'Tie::IxHash', 'single_price' => 'A single price per minute for all calls.', ; -tie my %recur_method, 'Tie::IxHash', - 'anniversary' => 'Charge the recurring fee at the frequency specified above', - 'prorate' => 'Charge a prorated fee the first time (selectable billing date)', - 'subscription' => 'Charge the full fee for the first partial period (selectable billing date)', -; - #tie my %cdr_location, 'Tie::IxHash', # 'internal' => 'Internal: CDR records imported into the internal CDR table', # 'external' => 'External: CDR records queried directly from an external '. @@ -72,7 +67,7 @@ tie my %temporalities, 'Tie::IxHash', #'type' => 'radio', #'options' => \%recur_method, 'type' => 'select', - 'select_options' => \%recur_method, + 'select_options' => \%FS::part_pkg::recur_common::recur_method, }, 'rating_method' => { 'name' => 'Rating method', @@ -228,11 +223,32 @@ sub calc_setup { $self->option('setup_fee'); } -#false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again sub calc_recur { my $self = shift; my($cust_pkg, $sdate, $details, $param ) = @_; + my $charges = 0; + + $charges += $self->calc_usage(@_); + $charges += $self->calc_recur_Common(@_); + + $charges; + +} + +sub calc_cancel { + my $self = shift; + my($cust_pkg, $sdate, $details, $param ) = @_; + + $self->calc_usage(@_); +} + +#false laziness w/voip_sqlradacct calc_recur resolve it if that one ever gets used again + +sub calc_usage { + my $self = shift; + my($cust_pkg, $sdate, $details, $param ) = @_; + #my $last_bill = $cust_pkg->last_bill; my $last_bill = $cust_pkg->get('last_bill'); #->last_bill falls back to setup @@ -599,33 +615,6 @@ sub calc_recur { # # } #if ( $spool_cdr && length($downstream_cdr) ) - if ($param->{'increment_next_bill'}) { - my $recur_method = $self->option('recur_method', 1) || 'anniversary'; - - if ( $recur_method eq 'prorate' ) { - - $charges += $self->SUPER::calc_recur(@_); - - } else { - - $charges += $self->option('recur_fee'); - - if ( $recur_method eq 'subscription' ) { - - my $cutoff_day = $self->option('cutoff_day', 1) || 1; - my ($day, $mon, $year) = ( localtime($$sdate) )[ 3..5 ]; - - if ( $day < $cutoff_day ) { - if ( $mon == 0 ) { $mon=11; $year--; } - else { $mon--; } - } - - $$sdate = timelocal(0, 0, 0, $cutoff_day, $mon, $year); - - }#$recur_method eq 'subscription' - }#$recur_method eq 'prorate' - }#increment_next_bill - $charges; } |