[freeside-commits] freeside/FS/FS/part_pkg flat.pm, 1.56, 1.57 prorate.pm, 1.24, 1.25 prorate_Mixin.pm, 1.9, 1.10
Mark Wells
mark at wavetail.420.am
Tue Jan 18 16:41:02 PST 2011
Update of /home/cvs/cvsroot/freeside/FS/FS/part_pkg
In directory wavetail.420.am:/tmp/cvs-serv13694/FS/FS/part_pkg
Modified Files:
flat.pm prorate.pm prorate_Mixin.pm
Log Message:
deferred prorate billing, RT#10630
Index: prorate_Mixin.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/prorate_Mixin.pm,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -w -d -r1.9 -r1.10
--- prorate_Mixin.pm 11 Jan 2011 00:56:14 -0000 1.9
+++ prorate_Mixin.pm 19 Jan 2011 00:41:00 -0000 1.10
@@ -23,36 +23,115 @@
...
if( conditions that trigger prorate ) {
# sets $$sdate and $param->{'months'}, returns the prorated charge
- $charges = $self->calc_prorate($cust_pkg, $sdate, $param, $cutoff_day);
+ $charges = $self->calc_prorate($cust_pkg, $sdate, $param);
}
...
}
=head METHODS
-=item calc_prorate CUST_PKG
+=item calc_prorate CUST_PKG SDATE DETAILS PARAM
-Takes all the arguments of calc_recur, followed by a day of the month
-to prorate to (which must be <= 28). Calculates a prorated charge from
-the $sdate to that day, and sets the $sdate and $param->{months} accordingly.
-base_recur() will be called to determine the base price per billing cycle.
+Takes all the arguments of calc_recur. Calculates a prorated charge from
+the $sdate to the cutoff day for this package definition, and sets the $sdate
+and $param->{months} accordingly. base_recur() will be called to determine
+the base price per billing cycle.
Options:
- 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.
+- prorate_defer_bill: Don't bill the prorate interval until the prorate
+day arrives.
=cut
sub calc_prorate {
my $self = shift;
- my ($cust_pkg, $sdate, $details, $param, $cutoff_day) = @_;
+ my ($cust_pkg, $sdate, $details, $param) = @_;
+ my $cutoff_day = $self->cutoff_day($cust_pkg) or return; #die?
my $charge = $self->base_recur($cust_pkg, $sdate) || 0;
if($cutoff_day) {
- # only works for freq >= 1 month; probably can't be fixed
my $mnow = $$sdate;
+
+ # if this is the first bill but the bill date has been set
+ # (by prorate_defer_bill), calculate from the setup date,
+ # and append the setup fee to @$details.
+ if ( $self->option('prorate_defer_bill')
+ and ! $cust_pkg->getfield('last_bill')
+ and $cust_pkg->setup ) {
+ warn "[calc_prorate] #".$cust_pkg->pkgnum.": running deferred setup\n";
+ $param->{'setup_fee'} = $self->calc_setup($cust_pkg, $$sdate, $details);
+ $mnow = $cust_pkg->setup;
+ }
+
+ my ($mend, $mstart);
+ ($mnow, $mend, $mstart) = $self->_endpoints($mnow, $cutoff_day);
+
+ # next bill date will be figured as $$sdate + one period
+ $$sdate = $mstart;
+
+ my $permonth = $charge / $self->freq;
+ my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
+
+ # add a full period if currently billing for a partial period
+ if ( ( $self->option('add_full_period',1)
+ or $self->option('prorate_defer_bill',1) ) # necessary
+ and $months < $self->freq ) {
+ $months += $self->freq;
+ $$sdate = $self->add_freq($mstart);
+ }
+
+ $param->{'months'} = $months;
+ $charge = sprintf('%.2f', $permonth * $months);
+ }
+ return $charge;
+}
+
+=item prorate_setup CUST_PKG SDATE
+
+Set up the package. This only has an effect if prorate_defer_bill is
+set, in which case it postpones the next bill to the cutoff day.
+
+=cut
+
+sub prorate_setup {
+ my $self = shift;
+ my ($cust_pkg, $sdate) = @_;
+ my $cutoff_day = $self->cutoff_day($cust_pkg);
+ if ( ! $cust_pkg->bill
+ and $self->option('prorate_defer_bill',1)
+ and $cutoff_day
+ ) {
+ my ($mnow, $mend, $mstart) = $self->_endpoints($sdate, $cutoff_day);
+ # if today is the cutoff day, set the next bill to right now instead
+ # of waiting a month.
+ if ( $mnow - $mstart < 86400 ) {
+ $cust_pkg->bill($mnow);
+ }
+ else {
+ $cust_pkg->bill($mend);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+=item _endpoints TIME CUTOFF_DAY
+
+Given a current time and a day of the month to prorate to, return three
+times: the start of the prorate interval (usually the current time), the
+end of the prorate interval (i.e. the cutoff date), and the time one month
+before the end of the prorate interval.
+
+=cut
+
+sub _endpoints {
+ my ($self, $mnow, $cutoff_day) = @_;
+
+ # only works for freq >= 1 month; probably can't be fixed
my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($mnow))[0..5];
if( $self->option('prorate_round_day',1) ) {
$mday++ if $hour >= 12;
@@ -83,23 +162,7 @@
$mstart =
timelocal(0,0,0,$cutoff_day,$mon == 0 ? 11 : $mon - 1,$year-($mon==0));
}
-
- # next bill date will be figured as $$sdate + one period
- $$sdate = $mstart;
-
- my $permonth = $charge / $self->freq;
- my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
-
- # add a full period if currently billing for a partial period
- if ( $self->option('add_full_period',1) and $months < $self->freq ) {
- $months += $self->freq;
- $$sdate = $self->add_freq($mstart);
- }
-
- $param->{'months'} = $months;
- $charge = sprintf('%.2f', $permonth * $months);
- }
- return $charge;
+ return ($mnow, $mend, $mstart);
}
1;
Index: prorate.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/prorate.pm,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -w -d -r1.24 -r1.25
--- prorate.pm 24 Dec 2010 09:49:32 -0000 1.24
+++ prorate.pm 19 Jan 2011 00:41:00 -0000 1.25
@@ -28,16 +28,24 @@
'the nearest full day',
'type' => 'checkbox',
},
+ 'prorate_defer_bill'=> {
+ 'name' => 'Defer the first bill until the billing day',
+ 'type' => 'checkbox',
},
- 'fieldorder' => [ 'cutoff_day', 'add_full_period', 'prorate_round_day' ],
+ },
+ 'fieldorder' => [ 'cutoff_day', 'prorate_defer_bill', 'add_full_period', 'prorate_round_day' ],
'freq' => 'm',
'weight' => 20,
);
+sub cutoff_day {
+ my $self = shift;
+ $self->option('cutoff_day', 1) || 1;
+}
+
sub calc_recur {
my $self = shift;
- my $cutoff_day = $self->option('cutoff_day') || 1;
- return $self->calc_prorate(@_, $cutoff_day) - $self->calc_discount(@_);
+ return $self->calc_prorate(@_) - $self->calc_discount(@_);
}
1;
Index: flat.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/part_pkg/flat.pm,v
retrieving revision 1.56
retrieving revision 1.57
diff -u -w -d -r1.56 -r1.57
--- flat.pm 8 Jan 2011 01:40:20 -0000 1.56
+++ flat.pm 19 Jan 2011 00:41:00 -0000 1.57
@@ -54,6 +54,11 @@
'with the customer\'s other packages',
'type' => 'checkbox',
},
+ 'prorate_defer_bill' => {
+ 'name' => 'When synchronizing, defer the bill until '.
+ 'the customer\'s next bill date',
+ 'type' => 'checkbox',
+ },
'suspend_bill' => { 'name' => 'Continue recurring billing while suspended',
'type' => 'checkbox',
},
@@ -70,7 +75,7 @@
'fieldorder' => [ qw( recur_temporality
expire_months adjourn_months
contract_end_months
- start_1st sync_bill_date
+ start_1st sync_bill_date prorate_defer_bill
suspend_bill unsuspend_adjust_bill
externalid ),
],
@@ -80,6 +85,8 @@
sub calc_setup {
my($self, $cust_pkg, $sdate, $details ) = @_;
+ return 0 if $self->prorate_setup($cust_pkg, $sdate);
+
my $i = 0;
my $count = $self->option( 'additional_count', 'quiet' ) || 0;
while ($i < $count) {
@@ -88,7 +95,8 @@
my $quantity = $cust_pkg->quantity || 1;
- sprintf("%.2f", $quantity * $self->unit_setup($cust_pkg, $sdate, $details) );
+ my $charge = $quantity * $self->unit_setup($cust_pkg, $sdate, $details);
+ sprintf('%.2f', $charge);
}
sub unit_setup {
@@ -108,12 +116,8 @@
if $self->option('recur_temporality', 1) eq 'preceding' && $last_bill == 0;
my $charge = $self->base_recur($cust_pkg, $sdate);
- if ( $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];
- $charge = $self->calc_prorate(@_, $cutoff_day);
- }
+ if ( my $cutoff_day = $self->cutoff_day($cust_pkg) ) {
+ $charge = $self->calc_prorate(@_);
}
elsif ( $param->{freq_override} ) {
# XXX not sure if this should be mutually exclusive with sync_bill_date.
@@ -126,6 +130,18 @@
return sprintf('%.2f', $charge - $discount);
}
+sub cutoff_day {
+ my $self = shift;
+ my $cust_pkg = shift;
+ if ( $self->option('sync_bill_date',1) ) {
+ my $next_bill = $cust_pkg->cust_main->next_bill_date;
+ if ( defined($next_bill) ) {
+ return (localtime($next_bill))[3];
+ }
+ }
+ return 0;
+}
+
sub base_recur {
my($self, $cust_pkg, $sdate) = @_;
$self->option('recur_fee', 1) || 0;
@@ -167,7 +183,7 @@
my $freq_sec = $1 * $sec{$2||'m'};
return 0 unless $freq_sec;
- sprintf("%.2f", $self->base_recur($cust_pkg) * ( $next_bill - $time ) / $freq_sec );
+ sprintf("%.2f", $self->base_recur($cust_pkg, \$time) * ( $next_bill - $time ) / $freq_sec );
}
More information about the freeside-commits
mailing list