X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fpart_pkg%2Fprorate_Mixin.pm;h=78a089313ecd574dbb68a972b4acc7c8aa60e4e9;hb=7f5e32275a5a2674fd1d220cd651b222b9831476;hp=1aa2f2c0c78c230b412e0a979c0ecc30e7dd734a;hpb=0fb307c305e4bc2c9c27dc25a3308beae3a4d33c;p=freeside.git diff --git a/FS/FS/part_pkg/prorate_Mixin.pm b/FS/FS/part_pkg/prorate_Mixin.pm index 1aa2f2c0c..78a089313 100644 --- a/FS/FS/part_pkg/prorate_Mixin.pm +++ b/FS/FS/part_pkg/prorate_Mixin.pm @@ -1,14 +1,35 @@ package FS::part_pkg::prorate_Mixin; use strict; -use vars qw(@ISA %info); -use Time::Local qw(timelocal); +use vars qw( %info ); +use Time::Local qw( timelocal ); -@ISA = qw(FS::part_pkg); %info = ( 'disabled' => 1, + # define all fields that are referenced in this code + 'fields' => { + 'add_full_period' => { + 'name' => 'When prorating first month, also bill for one full '. + 'period after that', + 'type' => 'checkbox', + }, + 'prorate_round_day' => { + 'name' => 'When prorating, round to the nearest full day', + 'type' => 'checkbox', + }, + 'prorate_defer_bill' => { + 'name' => 'When prorating, defer the first bill until the '. + 'billing day', + 'type' => 'checkbox', + }, + }, + 'fieldorder' => [ qw(prorate_defer_bill prorate_round_day add_full_period) ], ); +sub fieldorder { + @{ $info{'fieldorder'} } +} + =head1 NAME FS::part_pkg::prorate_Mixin - Mixin class for part_pkg:: classes that @@ -23,14 +44,14 @@ 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); + $charges = $self->calc_prorate($cust_pkg, $sdate, $param, $cutoff_day); } ... } =head METHODS -=item calc_prorate CUST_PKG SDATE DETAILS PARAM +=item calc_prorate CUST_PKG SDATE DETAILS PARAM CUTOFF_DAY 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 @@ -48,21 +69,21 @@ day arrives. =cut sub calc_prorate { - my $self = shift; - my ($cust_pkg, $sdate, $details, $param) = @_; - my $cutoff_day = $self->cutoff_day($cust_pkg) or return; #die? + my ($self, $cust_pkg, $sdate, $details, $param, $cutoff_day) = @_; + die "no cutoff_day" unless $cutoff_day; + die "can't prorate non-monthly package\n" if $self->freq =~ /\D/; my $charge = $self->base_recur($cust_pkg, $sdate) || 0; - if ( $cutoff_day ) { + 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') + if ( $self->option('prorate_defer_bill',1) and ! $cust_pkg->getfield('last_bill') and $cust_pkg->setup ) { - warn "[calc_prorate] #".$cust_pkg->pkgnum.": running deferred setup\n"; + #warn "[calc_prorate] #".$cust_pkg->pkgnum.": running deferred setup\n"; $param->{'setup_fee'} = $self->calc_setup($cust_pkg, $$sdate, $details); $mnow = $cust_pkg->setup; } @@ -77,8 +98,12 @@ sub calc_prorate { 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 + # or periods up to freq_override if billing for an override interval + if ( ($param->{'freq_override'} || 0) > 1 ) { + $months += $param->{'freq_override'} - 1; + } + elsif ( ( $self->option('add_full_period',1) + or $self->option('prorate_defer_bill',1) ) and $months < $self->freq ) { $months += $self->freq; $$sdate = $self->add_freq($mstart); @@ -86,7 +111,7 @@ sub calc_prorate { $param->{'months'} = $months; $charge = sprintf('%.2f', $permonth * $months); - } + return $charge; } @@ -106,10 +131,12 @@ sub prorate_setup { 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 today is the cutoff day, set the next bill and setup both to + # midnight today, so that the customer will be billed normally for a + # month starting today. if ( $mnow - $mstart < 86400 ) { - $cust_pkg->bill($mnow); + $cust_pkg->setup($mstart); + $cust_pkg->bill($mstart); } else { $cust_pkg->bill($mend); @@ -134,7 +161,13 @@ sub _endpoints { # 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; + # If the time is 12:00-23:59, move to the next day by adding 18 + # hours to $mnow. Because of DST this can end up from 05:00 to 18:59 + # but it's always within the next day. + $mnow += 64800 if $hour >= 12; + # Get the new day, month, and year. + ($mday,$mon,$year) = (localtime($mnow))[3..5]; + # Then set $mnow to midnight on that day. $mnow = timelocal(0,0,0,$mday,$mon,$year); } my $mend;