correct unused-time credits for discounted packages, #16352
authormark <mark>
Wed, 8 Feb 2012 02:17:58 +0000 (02:17 +0000)
committermark <mark>
Wed, 8 Feb 2012 02:17:58 +0000 (02:17 +0000)
FS/FS/cust_bill_pkg.pm
FS/FS/part_pkg.pm
FS/FS/part_pkg/flat.pm

index 1a99f6a..1ee5c09 100644 (file)
@@ -893,7 +893,7 @@ sub usage {
 
   if ( $self->get('details') ) {
 
-    return sum( 
+    return sum( 0, 
       map { $_->amount || 0 }
       grep { !defined($classnum) or $classnum eq $_->classnum }
       @{ $self->get('details') }
@@ -908,7 +908,7 @@ sub usage {
     my $sth = dbh->prepare($sql) or die dbh->errstr;
     $sth->execute or die $sth->errstr;
 
-    return $sth->fetchrow_arrayref->[0];
+    return $sth->fetchrow_arrayref->[0] || 0;
 
   }
 
index 1c30b67..373982b 100644 (file)
@@ -1292,29 +1292,16 @@ sub calc_recur { die 'no calc_recur for '. shift->plan. "\n"; }
 sub calc_remain { 0; }
 sub calc_units  { 0; }
 
+#fallback for everything not based on flat.pm
+sub recur_temporality { 'upcoming'; }
+sub calc_cancel { 0; }
+
 #fallback for everything except bulk.pm
 sub hide_svc_detail { 0; }
 
 #fallback for packages that can't/won't summarize usage
 sub sum_usage { 0; }
 
-# somewhat more intelligent fallback--
-# covers the standard cases of billing outstanding usage or just running
-# another recurring billing cycle
-sub calc_cancel {
-  my $self = shift;
-  my $conf = new FS::Conf;
-  if ( $self->recur_temporality eq 'preceding'
-       and $self->option('bill_recur_on_cancel',1) ) {
-    return $self->calc_recur(@_);
-  }
-  elsif ( $conf->exists('bill_usage_on_cancel') # should be a package option?
-          and $self->can('calc_usage') ) {
-    return $self->calc_usage(@_);
-  }
-  0;
-}
-
 =item recur_cost_permonth CUST_PKG
 
 recur_cost divided by freq (only supported for monthly and longer frequencies)
index 531a6a8..0e44f5d 100644 (file)
@@ -6,6 +6,7 @@ use base qw( FS::part_pkg::prorate_Mixin
 
 use strict;
 use vars qw( %info %usage_recharge_fields @usage_recharge_fieldorder );
+use FS::Record qw( qsearch );
 use Tie::IxHash;
 use List::Util qw( min );
 use FS::UI::bytecount;
@@ -189,6 +190,22 @@ sub base_recur_permonth {
   sprintf('%.2f', $self->base_recur($cust_pkg) / $self->freq );
 }
 
+sub calc_cancel {
+  my $self = shift;
+  my $conf = new FS::Conf;
+  if ( $self->recur_temporality eq 'preceding'
+       and $self->option('bill_recur_on_cancel', 1) ) {
+    # run another recurring cycle
+    return $self->calc_recur(@_);
+  }
+  elsif ( $conf->exists('bill_usage_on_cancel') # should be a package option?
+          and $self->can('calc_usage') ) {
+    # bill for outstanding usage
+    return $self->calc_usage(@_);
+  }
+  0;
+}
+
 sub calc_remain {
   my ($self, $cust_pkg, %options) = @_;
 
@@ -205,19 +222,28 @@ sub calc_remain {
               || ! $next_bill
               || $next_bill < $time;
 
-  my %sec = (
-    'h' =>    3600, # 60 * 60
-    'd' =>   86400, # 60 * 60 * 24
-    'w' =>  604800, # 60 * 60 * 24 * 7
-    'm' => 2629744, # 60 * 60 * 24 * 365.2422 / 12 
-  );
-
-  $self->freq =~ /^(\d+)([hdwm]?)$/
-    or die 'unparsable frequency: '. $self->freq;
-  my $freq_sec = $1 * $sec{$2||'m'};
-  return 0 unless $freq_sec;
-
-  sprintf("%.2f", $self->base_recur($cust_pkg, \$time) * ( $next_bill - $time ) / $freq_sec );
+  # Use actual charge for this period, not base_recur (for discounts).
+  # Use sdate < $time and edate >= $time because when billing on 
+  # cancellation, edate = $time.
+  my $credit = 0;
+  foreach my $item ( 
+    qsearch('cust_bill_pkg', { 
+      pkgnum => $cust_pkg->pkgnum,
+      sdate => {op => '<' , value => $time},
+      edate => {op => '>=', value => $time},
+      recur => {op => '>' , value => 0},
+    })
+  ) {
+    # hack to deal with the weird behavior of edate on package cancellation
+    my $edate = $item->edate;
+    if ( $self->recur_temporality eq 'preceding' ) {
+      $edate = $self->add_freq($item->sdate);
+    }
+    $credit += ($item->recur - $item->usage) * 
+               ($edate - $time) / ($edate - $item->sdate);
+  } 
+  sprintf('%.2f', $credit);
+  #sprintf("%.2f", $self->base_recur($cust_pkg, \$time) * ( $next_bill - $time ) / $freq_sec );
 
 }