[freeside-commits] freeside/FS/FS/part_pkg discount_Mixin.pm, NONE, 1.1 flat.pm, 1.52, 1.53 prorate.pm, 1.22, 1.23 prorate_Mixin.pm, 1.4, 1.5 recur_Common.pm, 1.6, 1.7 subscription.pm, 1.17, 1.18

Mark Wells mark at wavetail.420.am
Sat Oct 30 16:22:34 PDT 2010


Update of /home/cvs/cvsroot/freeside/FS/FS/part_pkg
In directory wavetail.420.am:/tmp/cvs-serv32094

Modified Files:
	flat.pm prorate.pm prorate_Mixin.pm recur_Common.pm 
	subscription.pm 
Added Files:
	discount_Mixin.pm 
Log Message:
discount_Mixin

Index: prorate_Mixin.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/prorate_Mixin.pm,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -w -d -r1.4 -r1.5
--- prorate_Mixin.pm	29 Oct 2010 08:51:51 -0000	1.4
+++ prorate_Mixin.pm	30 Oct 2010 23:22:31 -0000	1.5
@@ -5,7 +5,9 @@
 use Time::Local qw(timelocal);
 
 @ISA = qw(FS::part_pkg);
-%info = ( 'disabled' => 1 );
+%info = ( 
+  'disabled'  => 1,
+);
 
 =head1 NAME
 
@@ -28,40 +30,26 @@
 
 =head METHODS
 
-=item calc_prorate
+=item calc_prorate CUST_PKG
 
-Takes all the arguments of calc_recur, and calculates a prorated charge 
-in one of two ways:
+Takes all the arguments of calc_recur, followed by a day of the month 
+to prorate to.  Calculates a prorated charge from the $sdate to that day, 
+and sets the $sdate and $param->{months} accordingly.
 
-- 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.
+Options:
+- recur_fee: The charge to use for a complete billing period.
+- add_full_period: Bill for the time up to the prorate day plus one full
+billing period after that.
+- prorate_round_day: Round the current time to the nearest full day, 
+instead of using the exact time.
 
 =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',1) ) {
-    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;
-  }
+  my ($cust_pkg, $sdate, $details, $param, $cutoff_day) = @_;
 
+  my $charge = $self->option('recur_fee',1) || 0;
   if($cutoff_day) {
     # only works for freq >= 1 month; probably can't be fixed
     my $mnow = $$sdate;
@@ -88,7 +76,7 @@
     # next bill date will be figured as $$sdate + one period
     $$sdate = $mstart;
 
-    my $permonth = $self->option('recur_fee', 1) / $self->freq;
+    my $permonth = $charge / $self->freq;
     my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
 
     if ( $self->option('add_full_period',1) ) {
@@ -100,8 +88,7 @@
     $param->{'months'} = $months;
     $charge = sprintf('%.2f', $permonth * $months);
   }
-  my $discount =  $self->calc_discount(@_);
-  return ($charge - $discount);
+  return $charge;
 }
 
 1;

Index: recur_Common.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/recur_Common.pm,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -w -d -r1.6 -r1.7
--- recur_Common.pm	26 Aug 2010 00:08:59 -0000	1.6
+++ recur_Common.pm	30 Oct 2010 23:22:31 -0000	1.7
@@ -4,9 +4,9 @@
 use vars qw( @ISA %info %recur_method );
 use Tie::IxHash;
 use Time::Local;
-use FS::part_pkg::prorate;
+use FS::part_pkg::flat;
 
- at ISA = qw(FS::part_pkg::prorate);
+ at ISA = qw(FS::part_pkg::flat);
 
 %info = ( 'disabled' => 1 ); #recur_Common not a usable price plan directly
 
@@ -16,6 +16,11 @@
   'subscription' => 'Charge the full fee for the first partial period (selectable billing date)',
 ;
 
+sub base_recur {
+  my $self = shift;
+  $self->option('recur_fee', 1) || 0;
+}
+
 sub calc_recur_Common {
   my $self = shift;
   my($cust_pkg, $sdate, $details, $param ) = @_; #only need $sdate & $param
@@ -26,16 +31,21 @@
 
     my $recur_method = $self->option('recur_method', 1) || 'anniversary';
                   
-    if ( $recur_method eq 'prorate' 
-        or ($recur_method eq 'anniversary' and $self->option('sync_bill_date',1))
-      ) {
-      $charges = $self->calc_prorate(@_);
-    } 
-    else {
-
-      $charges = $self->option('recur_fee');
+    $charges = $self->base_recur;
 
-      if ( $recur_method eq 'subscription' ) {
+    if ( $recur_method eq 'prorate' ) {
+      my $cutoff_day = $self->option('cutoff_day') || 1;
+      $charges = $self->calc_prorate(@_, $cutoff_day);
+    }
+    elsif ( $recur_method eq 'anniversary' and 
+            $self->option('sync_bill_date',1) ) {
+      my $next_bill = $cust_pkg->cust_main->next_bill_date;
+      if ( defined($next_bill) ) {
+        my $cutoff_day = (localtime($next_bill))[3];
+        $charges = $self->calc_prorate(@_, $cutoff_day);
+      }
+    } 
+    elsif ( $recur_method eq 'subscription' ) {
 
         my $cutoff_day = $self->option('cutoff_day', 1) || 1;
         my ($day, $mon, $year) = ( localtime($$sdate) )[ 3..5 ];
@@ -48,9 +58,9 @@
         $$sdate = timelocal(0, 0, 0, $cutoff_day, $mon, $year);
 
       }#$recur_method eq 'subscription'
+
     $charges -= $self->calc_discount( $cust_pkg, $sdate, $details, $param );
 
-    }#$recur_method eq 'prorate' or ...
   }#increment_next_bill
 
   return $charges;

Index: prorate.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/prorate.pm,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -w -d -r1.22 -r1.23
--- prorate.pm	29 Oct 2010 08:51:51 -0000	1.22
+++ prorate.pm	30 Oct 2010 23:22:31 -0000	1.23
@@ -106,7 +106,8 @@
 
 sub calc_recur {
   my $self = shift;
-  $self->calc_prorate(@_);
+  my $cutoff_day = $self->option('cutoff_day') || 1;
+  return $self->calc_prorate(@_, $cutoff_day) - $self->calc_discount(@_);
 }
 
 1;

Index: flat.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/flat.pm,v
retrieving revision 1.52
retrieving revision 1.53
diff -u -w -d -r1.52 -r1.53
--- flat.pm	22 Oct 2010 22:28:51 -0000	1.52
+++ flat.pm	30 Oct 2010 23:22:31 -0000	1.53
@@ -11,9 +11,10 @@
 use FS::UI::bytecount;
 use FS::Conf;
 use FS::part_pkg;
-use FS::cust_bill_pkg_discount;
 
- at ISA = qw(FS::part_pkg FS::part_pkg::prorate_Mixin);
+ at ISA = qw(FS::part_pkg 
+          FS::part_pkg::prorate_Mixin
+          FS::part_pkg::discount_Mixin);
 
 tie my %temporalities, 'Tie::IxHash',
   'upcoming'  => "Upcoming (future)",
@@ -194,100 +195,23 @@
   return 0
     if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
 
-  if( $self->option('sync_bill_date',1) ) {
-    return $self->calc_prorate(@_);
-  }
-  else {
     my $charge = $self->base_recur($cust_pkg);
-    $charge *= $param->{freq_override} if $param->{freq_override};
-    my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
-
-    return sprintf('%.2f', $charge - $discount);
-  }
-}
-
-sub calc_discount {
-  my($self, $cust_pkg, $sdate, $details, $param ) = @_;
-
-  my $br = $self->base_recur($cust_pkg);
-
-  my $tot_discount = 0;
-  #UI enforces just 1 for now, will need ordering when they can be stacked
-
-  if ( $param->{freq_override} ) {
-    my $real_part_pkg = new FS::part_pkg { $self->hash };
-    $real_part_pkg->pkgpart($param->{real_pkgpart} || $self->pkgpart);
-    my @discount = grep { $_->months == $param->{freq_override} }
-                   map { $_->discount }
-                   $real_part_pkg->part_pkg_discount;
-    my $discount = shift @discount;
-    $param->{months} = $param->{freq_override} unless $param->{months};
-    my $error;
-    if ($discount) {
-      if ($discount->months == $param->{months}) {
-        $cust_pkg->discountnum($discount->discountnum);
-        $error = $cust_pkg->insert_discount;
-      } else {
-        $cust_pkg->discountnum(-1);
-        foreach ( qw( amount percent months ) ) {
-          my $method = "discountnum_$_";
-          $cust_pkg->$method($discount->$_);
-        }
-        $error = $cust_pkg->insert_discount;
-      }
-      die "error discounting using part_pkg_discount: $error" if $error;
+  if ( $self->option('sync_bill_date',1) ) {
+    my $next_bill = $cust_pkg->cust_main->next_bill_date;
+    if ( defined($next_bill) and $next_bill != $$sdate ) {
+      my $cutoff_day = (localtime($next_bill))[3];
+      $charge = $self->calc_prorate(@_, $cutoff_day);
     }
   }
-
-  my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
-  foreach my $cust_pkg_discount ( @cust_pkg_discount ) {
-     my $discount = $cust_pkg_discount->discount;
-     #UI enforces one or the other (for now?  probably for good)
-     my $amount = 0;
-     $amount += $discount->amount
-       if $cust_pkg->pkgpart == $param->{real_pkgpart};
-     $amount += sprintf('%.2f', $discount->percent * $br / 100 );
-
-     my $chg_months = $param->{'months'} || $cust_pkg->part_pkg->freq;
-     
-     my $months = $discount->months
-                    ? min( $chg_months,
-                           $discount->months - $cust_pkg_discount->months_used )
-                    : $chg_months;
-
-     my $error = $cust_pkg_discount->increment_months_used($months)
-       if $cust_pkg->pkgpart == $param->{real_pkgpart};
-     die "error discounting: $error" if $error;
-
-     $amount *= $months;
-     $amount = sprintf('%.2f', $amount);
-
-     next unless $amount > 0;
-
-     #record details in cust_bill_pkg_discount
-     my $cust_bill_pkg_discount = new FS::cust_bill_pkg_discount {
-       'pkgdiscountnum' => $cust_pkg_discount->pkgdiscountnum,
-       'amount'         => $amount,
-       'months'         => $months,
-     };
-     push @{ $param->{'discounts'} }, $cust_bill_pkg_discount;
-
-     #add details on discount to invoice
-     my $conf = new FS::Conf;
-     my $money_char = $conf->config('money_char') || '$';  
-     $months = sprintf('%.2f', $months) if $months =~ /\./;
-
-     my $d = 'Includes ';
-     $d .= $discount->name. ' ' if $discount->name;
-     $d .= 'discount of '. $discount->description_short;
-     $d .= " for $months month". ( $months!=1 ? 's' : '' );
-     $d .= ": $money_char$amount" if $months != 1 || $discount->percent;
-     push @$details, $d;
-
-     $tot_discount += $amount;
+  elsif ( $param->{freq_override} ) {
+    # XXX not sure if this should be mutually exclusive with sync_bill_date.
+    # Given the very specific problem that freq_override is meant to 'solve',
+    # it probably should.
+    $charge *= $param->{freq_override} if $param->{freq_override};
   }
 
-  sprintf('%.2f', $tot_discount);
+  my $discount = $self->calc_discount($cust_pkg, $sdate, $details, $param);
+  return sprintf('%.2f', $charge - $discount);
 }
 
 sub base_recur {

--- NEW FILE: discount_Mixin.pm ---
package FS::part_pkg::discount_Mixin;

use strict;
use vars qw(@ISA %info);
use FS::part_pkg;
use FS::cust_pkg;
use FS::cust_bill_pkg_discount;
use Time::Local qw(timelocal);
use List::Util 'min';

@ISA = qw(FS::part_pkg);
%info = ( 'disabled' => 1 );

=head1 NAME

FS::part_pkg::discount_Mixin - Mixin class for part_pkg:: classes that 
can be discounted.

=head1 SYNOPSIS

package FS::part_pkg::...;
use base qw( FS::part_pkg::discount_Mixin );

sub calc_recur {
  ...
  my $discount = $self->calc_discount($cust_pkg, $$sdate, $details, $param);
  $charge -= $discount;
  ...
}

=head METHODS

=item calc_discount

Takes all the arguments of calc_recur.  Calculates and returns  the amount 
by which to reduce the recurring fee; also increments months used on the 
discount and generates an invoice detail describing it.

=cut

sub calc_discount {
  my($self, $cust_pkg, $sdate, $details, $param ) = @_;

  my $br = $self->base_recur($cust_pkg);

  my $tot_discount = 0;
  #UI enforces just 1 for now, will need ordering when they can be stacked

  if ( $param->{freq_override} ) {
    # When a customer pays for more than one month at a time to receive a 
    # term discount, freq_override is set to the number of months.
    my $real_part_pkg = new FS::part_pkg { $self->hash };
    $real_part_pkg->pkgpart($param->{real_pkgpart} || $self->pkgpart);
    # Find a discount with that duration...
    my @discount = grep { $_->months == $param->{freq_override} }
                    map { $_->discount } $real_part_pkg->part_pkg_discount;
    my $discount = shift @discount;
    # and default to bill that many months at once.
    $param->{months} = $param->{freq_override} unless $param->{months};
    my $error;
    if ($discount) {
      # Then set the cust_pkg discount.
      if ($discount->months == $param->{months}) {
        $cust_pkg->discountnum($discount->discountnum);
        $error = $cust_pkg->insert_discount;
      } else {
        $cust_pkg->discountnum(-1);
        foreach ( qw( amount percent months ) ) {
          my $method = "discountnum_$_";
          $cust_pkg->$method($discount->$_);
        }
        $error = $cust_pkg->insert_discount;
      }
      die "error discounting using part_pkg_discount: $error" if $error;
    }
  }

  my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
  foreach my $cust_pkg_discount ( @cust_pkg_discount ) {
    my $discount = $cust_pkg_discount->discount;
    #UI enforces one or the other (for now?  probably for good)
    my $amount = 0;
    $amount += $discount->amount
    if $cust_pkg->pkgpart == $param->{real_pkgpart};
    $amount += sprintf('%.2f', $discount->percent * $br / 100 );
    my $chg_months = $param->{'months'} || $cust_pkg->part_pkg->freq;

    my $months = $discount->months
    ? min( $chg_months,
      $discount->months - $cust_pkg_discount->months_used )
    : $chg_months;

    my $error = $cust_pkg_discount->increment_months_used($months)
    if $cust_pkg->pkgpart == $param->{real_pkgpart};
    die "error discounting: $error" if $error;

    $amount *= $months;
    $amount = sprintf('%.2f', $amount);

    next unless $amount > 0;

    #record details in cust_bill_pkg_discount
    my $cust_bill_pkg_discount = new FS::cust_bill_pkg_discount {
      'pkgdiscountnum' => $cust_pkg_discount->pkgdiscountnum,
      'amount'         => $amount,
      'months'         => $months,
    };
    push @{ $param->{'discounts'} }, $cust_bill_pkg_discount;

    #add details on discount to invoice
    my $conf = new FS::Conf;
    my $money_char = $conf->config('money_char') || '$';
    $months = sprintf('%.2f', $months) if $months =~ /\./;

    my $d = 'Includes ';
    $d .= $discount->name. ' ' if $discount->name;
    $d .= 'discount of '. $discount->description_short;
    $d .= " for $months month". ( $months!=1 ? 's' : '' );
    $d .= ": $money_char$amount" if $months != 1 || $discount->percent;
    push @$details, $d;

    $tot_discount += $amount;
  }

  sprintf('%.2f', $tot_discount);
}

1;

Index: subscription.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/subscription.pm,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -w -d -r1.17 -r1.18
--- subscription.pm	1 Apr 2010 08:10:05 -0000	1.17
+++ subscription.pm	30 Oct 2010 23:22:31 -0000	1.18
@@ -18,7 +18,7 @@
     'recur_fee' => { 'name' => 'Recurring fee for this package',
                      'default' => 0,
 			   },
-    'cutoff_day' => { 'name' => 'billing day',
+    'cutoff_day' => { 'name' => 'Billing day',
                       'default' => 1,
                     },
     'seconds'       => { 'name' => 'Time limit for this package',



More information about the freeside-commits mailing list