1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
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',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;
}
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];
if ( $self->option('prorate_round_day',1) ) {
$mday++ if $hour >= 12;
$mnow = timelocal(0,0,0,$mday,$mon,$year);
}
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));
}
# next bill date will be figured as $$sdate + one period
$$sdate = $mstart;
my $permonth = $self->option('recur_fee', 1) / $self->freq;
my $months = ( ( $self->freq - 1 ) + ($mend-$mnow) / ($mend-$mstart) );
if ( $self->option('add_full_period',1) ) {
# charge a full period in addition to the partial month
$months += $self->freq;
$$sdate = $self->add_freq($mstart);
}
$param->{'months'} = $months;
$charge = sprintf('%.2f', $permonth * $months);
}
my $discount = $self->calc_discount(@_);
return ($charge - $discount);
}
1;
|