From 4117c20f85ce085d7dd42b8970ce9c65b95d7e27 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 19 Aug 2010 19:11:45 +0000 Subject: [PATCH] part_pkg prorate mixin and sync_bill_date option, RT#9554 --- FS/FS/Conf.pm | 7 +++ FS/FS/part_pkg/flat.pm | 23 +++++++--- FS/FS/part_pkg/prorate.pm | 30 +------------ FS/FS/part_pkg/prorate_Mixin.pm | 96 +++++++++++++++++++++++++++++++++++++++++ FS/FS/part_pkg/recur_Common.pm | 23 +++++----- httemplate/misc/order_pkg.html | 8 +++- 6 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 FS/FS/part_pkg/prorate_Mixin.pm diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index fe010f77b..ce2c01d46 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -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.', diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm index 648a83ddc..a04f44ae4 100644 --- a/FS/FS/part_pkg/flat.pm +++ b/FS/FS/part_pkg/flat.pm @@ -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 { diff --git a/FS/FS/part_pkg/prorate.pm b/FS/FS/part_pkg/prorate.pm index 09561cf51..918b910be 100644 --- a/FS/FS/part_pkg/prorate.pm +++ b/FS/FS/part_pkg/prorate.pm @@ -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 index 000000000..a60858b37 --- /dev/null +++ b/FS/FS/part_pkg/prorate_Mixin.pm @@ -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; diff --git a/FS/FS/part_pkg/recur_Common.pm b/FS/FS/part_pkg/recur_Common.pm index 8ed9eb6af..9a6774579 100644 --- a/FS/FS/part_pkg/recur_Common.pm +++ b/FS/FS/part_pkg/recur_Common.pm @@ -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; } diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html index 33b2bb390..2511a3deb 100644 --- a/httemplate/misc/order_pkg.html +++ b/httemplate/misc/order_pkg.html @@ -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) : ''; +} -- 2.11.0