part_pkg prorate mixin and sync_bill_date option, RT#9554
authormark <mark>
Thu, 19 Aug 2010 19:11:45 +0000 (19:11 +0000)
committermark <mark>
Thu, 19 Aug 2010 19:11:45 +0000 (19:11 +0000)
FS/FS/Conf.pm
FS/FS/part_pkg/flat.pm
FS/FS/part_pkg/prorate.pm
FS/FS/part_pkg/prorate_Mixin.pm [new file with mode: 0644]
FS/FS/part_pkg/recur_Common.pm
httemplate/misc/order_pkg.html

index fe010f7..ce2c01d 100644 (file)
@@ -3294,6 +3294,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'order_pkg-no_start_date',
+    'section'     => 'UI',
+    'description' => 'Don\'t set a default start date for new packages.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'mcp_svcpart',
     'section'     => '',
     'description' => 'Master Control Program svcpart.  Leave this blank.',
index 648a83d..a04f44a 100644 (file)
@@ -13,7 +13,7 @@ use FS::Conf;
 use FS::part_pkg;
 use FS::cust_bill_pkg_discount;
 
-@ISA = qw(FS::part_pkg);
+@ISA = qw(FS::part_pkg FS::part_pkg::prorate_Mixin);
 
 tie my %temporalities, 'Tie::IxHash',
   'upcoming'  => "Upcoming (future)",
@@ -119,6 +119,10 @@ tie my %temporalities, 'Tie::IxHash',
     'start_1st'     => { 'name' => 'Auto-add a start date to the 1st, ignoring the current month.',
                          'type' => 'checkbox',
                        },
+    'sync_bill_date' => { 'name' => 'Prorate first month to synchronize '.
+                                    'with the customer\'s other packages',
+                          'type' => 'checkbox',
+                        },
 
     %usage_fields,
     %usage_recharge_fields,
@@ -129,7 +133,7 @@ tie my %temporalities, 'Tie::IxHash',
   },
   'fieldorder' => [ qw( setup_fee recur_fee
                         recur_temporality unused_credit
-                        expire_months start_1st
+                        expire_months start_1st sync_bill_date
                       ),
                     @usage_fieldorder, @usage_recharge_fieldorder,
                     qw( externalid ),
@@ -158,7 +162,8 @@ sub unit_setup {
 }
 
 sub calc_recur {
-  my($self, $cust_pkg, $sdate, $details, $param ) = @_;
+  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
@@ -166,11 +171,15 @@ sub calc_recur {
   return 0
     if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
 
-  my $br = $self->base_recur($cust_pkg);
-
-  my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
+  if( $self->option('sync_bill_date') ) {
+    return $self->calc_prorate(@_);
+  }
+  else {
+    my $charge = $self->base_recur($cust_pkg);
+    my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
 
-  sprintf('%.2f', $br - $discount);
+    return sprintf('%.2f', $charge - $discount);
+  }
 }
 
 sub calc_discount {
index 09561cf..918b910 100644 (file)
@@ -95,34 +95,8 @@ use FS::part_pkg::flat;
 );
 
 sub calc_recur {
-  my($self, $cust_pkg, $sdate, $details, $param ) = @_;
-  my $cutoff_day = $self->option('cutoff_day', 1) || 1;
-  my $mnow = $$sdate;
-  my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
-  my $mend;
-  my $mstart;
-  
-  if ( $mday >= $cutoff_day ) {
-    $mend =
-      timelocal(0,0,0,$cutoff_day, $mon == 11 ? 0 : $mon+1, $year+($mon==11));
-    $mstart =
-      timelocal(0,0,0,$cutoff_day,$mon,$year);  
-
-  } else {
-    $mend = timelocal(0,0,0,$cutoff_day, $mon, $year);
-    if ($mon==0) {$mon=11;$year--;} else {$mon--;}
-    $mstart=  timelocal(0,0,0,$cutoff_day,$mon,$year);  
-  }
-
-  $$sdate = $mstart;
-  my $permonth = $self->option('recur_fee') / $self->freq;
-
-  my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
-
-  $param->{'months'} = $months;
-  my $discount = $self->calc_discount( $cust_pkg, $sdate, $details, $param);
-
-  sprintf('%.2f', $permonth * $months - $discount);
+  my $self = shift;
+  $self->calc_prorate(@_);
 }
 
 1;
diff --git a/FS/FS/part_pkg/prorate_Mixin.pm b/FS/FS/part_pkg/prorate_Mixin.pm
new file mode 100644 (file)
index 0000000..a60858b
--- /dev/null
@@ -0,0 +1,96 @@
+package FS::part_pkg::prorate_Mixin;
+
+use strict;
+use vars qw(@ISA %info);
+use Time::Local qw(timelocal);
+
+@ISA = qw(FS::part_pkg);
+%info = ( 'disabled' => 1 );
+
+=head1 NAME
+
+FS::part_pkg::prorate_Mixin - Mixin class for part_pkg:: classes that 
+need to prorate partial months
+
+=head1 SYNOPSIS
+
+package FS::part_pkg::...;
+use base qw( FS::part_pkg::prorate_Mixin );
+
+sub calc_recur {
+  ...
+  if( conditions that trigger prorate ) {
+    # sets $$sdate and $param->{'months'}, returns the prorated charge
+    $charges = $self->calc_prorate($cust_pkg, $sdate, $param, $cutoff_day);
+  } 
+  ...
+}
+
+=head METHODS
+
+=item calc_prorate
+
+Takes all the arguments of calc_recur, and calculates a prorated charge 
+in one of two ways:
+
+- If 'sync_bill_date' is set: Charge for a number of days to synchronize 
+  this package to the customer's next bill date.  If this is their only 
+  package (or they're already synchronized), that will take them through 
+  one billing cycle.
+- If 'cutoff_day' is set: Prorate this package so that its next bill date 
+  falls on that day of the month.
+
+=cut
+
+sub calc_prorate {
+  my $self  = shift;
+  my ($cust_pkg, $sdate, $details, $param) = @_;
+  my $charge = $self->option('recur_fee') || 0;
+  my $cutoff_day;
+  if( $self->option('sync_bill_date') ) {
+    my $next_bill = $cust_pkg->cust_main->next_bill_date;
+    if( defined($next_bill) and $next_bill != $$sdate ) {
+      $cutoff_day = (localtime($next_bill))[3];
+    }
+    else {
+      # don't prorate, assume a full month
+      $param->{'months'} = $self->freq;
+    }
+  }
+  else { # no sync, use cutoff_day or day 1
+    $cutoff_day = $self->option('cutoff_day') || 1;
+  }
+
+  if($cutoff_day) {
+    # only works for freq >= 1 month; probably can't be fixed
+    my $mnow = $$sdate;
+    my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($mnow))[0..5];
+    my $mend;
+    my $mstart;
+    if ( $mday >= $cutoff_day ) {
+      $mend = 
+        timelocal(0,0,0,$cutoff_day,$mon == 11 ? 0 : $mon + 1,$year+($mon==11));
+      $mstart =
+        timelocal(0,0,0,$cutoff_day,$mon,$year);
+    }
+    else {
+      $mend = 
+        timelocal(0,0,0,$cutoff_day,$mon,$year);
+      $mstart = 
+        timelocal(0,0,0,$cutoff_day,$mon == 0 ? 11 : $mon - 1,$year-($mon==11));
+    }
+    
+    $$sdate = $mstart;
+
+    my $permonth = $self->option('recur_fee', 1) / $self->freq;
+    my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
+    
+    $param->{'months'} = $months;
+    $charge = sprintf('%.2f', $permonth * $months);
+  }
+  my $discount =  $self->calc_discount(@_);
+  return ($charge - $discount);
+}
+
+1;
index 8ed9eb6..9a67745 100644 (file)
@@ -4,9 +4,9 @@ use strict;
 use vars qw( @ISA %info %recur_method );
 use Tie::IxHash;
 use Time::Local;
-use FS::part_pkg::prorate;
+use FS::part_pkg::prorate_Mixin;
 
-@ISA = qw(FS::part_pkg::prorate);
+@ISA = qw(FS::part_pkg::prorate_Mixin);
 
 %info = ( 'disabled' => 1 ); #recur_Common not a usable price plan directly
 
@@ -26,11 +26,12 @@ sub calc_recur_Common {
 
     my $recur_method = $self->option('recur_method', 1) || 'anniversary';
                   
-    if ( $recur_method eq 'prorate' ) {
-
-      $charges = $self->SUPER::calc_recur(@_);
-
-    } else {
+    if ( $recur_method eq 'prorate' 
+        or ($recur_method eq 'anniversary' and $self->option('sync_bill_date'))
+      ) {
+      $charges = $self->calc_prorate(@_);
+    } 
+    else {
 
       $charges = $self->option('recur_fee');
 
@@ -47,14 +48,12 @@ sub calc_recur_Common {
         $$sdate = timelocal(0, 0, 0, $cutoff_day, $mon, $year);
 
       }#$recur_method eq 'subscription'
+    $charges -= $self->calc_discount( $cust_pkg, $sdate, $details, $param );
 
-      $charges -= $self->calc_discount( $cust_pkg, $sdate, $details, $param );
-
-    }#$recur_method eq 'prorate'
-
+    }#$recur_method eq 'prorate' or ...
   }#increment_next_bill
 
-  $charges;
+  return $charges;
 
 }
 
index 33b2bb3..2511a3d 100644 (file)
@@ -128,7 +128,11 @@ my $cust_main = qsearchs({
 my $pkgpart = scalar($cgi->param('pkgpart'));
 
 my $format = $date_format. ' %T %z (%Z)'; #false laziness w/REAL_cust_pkg.cgi?
-my $start_date = $cust_main->next_bill_date;
-$start_date = $start_date ? time2str($format, $start_date) : '';
+my $start_date = '';
+if(! $conf->exists('order_pkg-no_start_date') ) {
+  warn "foo";
+  $cust_main->next_bill_date;
+  $start_date = $start_date ? time2str($format, $start_date) : '';
+}
 
 </%init>