summaryrefslogtreecommitdiff
path: root/FS/FS/part_pkg/prorate_Mixin.pm
blob: 9c0c2669b284ba684b59e31e72e004ca3720766d (plain)
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
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 CUST_PKG

Takes all the arguments of calc_recur, followed by a day of the month 
to prorate to.  Calculates a prorated charge from the $sdate to that day, 
and sets the $sdate and $param->{months} accordingly.

Options:
- recur_fee: The charge to use for a complete billing period.
- 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.

=cut

sub calc_prorate {
  my $self  = shift;
  my ($cust_pkg, $sdate, $details, $param, $cutoff_day) = @_;
 
  my $charge = $self->option('recur_fee',1) || 0;
  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 = $charge / $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);
  }
  return $charge;
}

1;