1 package FS::part_pkg::prorate_Mixin;
5 use Time::Local qw( timelocal );
13 FS::part_pkg::prorate_Mixin - Mixin class for part_pkg:: classes that
14 need to prorate partial months
18 package FS::part_pkg::...;
19 use base qw( FS::part_pkg::prorate_Mixin );
23 if( conditions that trigger prorate ) {
24 # sets $$sdate and $param->{'months'}, returns the prorated charge
25 $charges = $self->calc_prorate($cust_pkg, $sdate, $param, $cutoff_day);
32 =item calc_prorate CUST_PKG
34 Takes all the arguments of calc_recur, followed by a day of the month
35 to prorate to (which must be <= 28). Calculates a prorated charge from
36 the $sdate to that day, and sets the $sdate and $param->{months} accordingly.
37 base_recur() will be called to determine the base price per billing cycle.
40 - add_full_period: Bill for the time up to the prorate day plus one full
41 billing period after that.
42 - prorate_round_day: Round the current time to the nearest full day,
43 instead of using the exact time.
49 my ($cust_pkg, $sdate, $details, $param, $cutoff_day) = @_;
51 my $charge = $self->base_recur($cust_pkg, $sdate) || 0;
53 # only works for freq >= 1 month; probably can't be fixed
55 my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($mnow))[0..5];
56 if( $self->option('prorate_round_day',1) ) {
57 # If the time is 12:00-23:59, move to the next day by adding 18
58 # hours to $mnow. Because of DST this can end up from 05:00 to 18:59
59 # but it's always within the next day.
60 $mnow += 64800 if $hour >= 12;
61 # Get the new day, month, and year.
62 ($mday,$mon,$year) = (localtime($mnow))[3..5];
63 # Then set $mnow to midnight on that date.
64 $mnow = timelocal(0,0,0,$mday,$mon,$year);
68 # if cutoff day > 28, force it to the 1st of next month
69 if ( $cutoff_day > 28 ) {
71 # and if we are currently after the 28th, roll the current day
75 #set $mnow = $mend so the amount billed will be zero
76 $mnow = timelocal(0,0,0,1,$mon == 11 ? 0 : $mon + 1,$year+($mon==11));
79 if ( $mday >= $cutoff_day ) {
81 timelocal(0,0,0,$cutoff_day,$mon == 11 ? 0 : $mon + 1,$year+($mon==11));
83 timelocal(0,0,0,$cutoff_day,$mon,$year);
87 timelocal(0,0,0,$cutoff_day,$mon,$year);
89 timelocal(0,0,0,$cutoff_day,$mon == 0 ? 11 : $mon - 1,$year-($mon==0));
92 # next bill date will be figured as $$sdate + one period
95 my $permonth = $charge / $self->freq;
96 my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
98 # add a full period if currently billing for a partial period
99 if ( $self->option('add_full_period',1) and $months < $self->freq ) {
100 $months += $self->freq;
101 $$sdate = $self->add_freq($mstart);
104 $param->{'months'} = $months;
105 $charge = sprintf('%.2f', $permonth * $months);