f12b1accdaf58dcb722032f4af0db697f8684c94
[freeside.git] / FS / FS / part_pkg / flat_introrate.pm
1 package FS::part_pkg::flat_introrate;
2 use base qw( FS::part_pkg::flat );
3
4 use strict;
5 use vars qw( %info );
6
7 use FS::Log;
8
9 # mostly false laziness with FS::part_pkg::global_Mixin::validate_moneyn,
10 # except for blank string handling...
11 sub validate_money {
12   my ($option, $valref) = @_;
13   if ( $$valref eq '' ) {
14     $$valref = '0';
15   } elsif ( $$valref =~ /^\s*(\d*)(\.\d{1})\s*$/ ) {
16     #handle one decimal place without barfing out
17     $$valref = ( ($1||''). ($2.'0') ) || 0;
18   } elsif ( $$valref =~ /^\s*(\d*)(\.\d{2})?\s*$/ ) {
19     $$valref = ( ($1||''). ($2||'') ) || 0;
20   } else {
21     return "Illegal (money) $option: ". $$valref;
22   }
23   return '';
24 }
25
26 sub validate_number {
27   my ($option, $valref) = @_;
28   $$valref = 0 unless $$valref;
29   return "Invalid $option"
30     unless ($$valref) = ($$valref =~ /^\s*(\d+)\s*$/);
31   return '';
32 }
33
34 %info = (
35   'name' => 'Introductory price for X months, then flat rate,'.
36             'relative to setup date (anniversary billing)',
37   'shortname' => 'Anniversary, with intro price',
38   'inherit_fields' => [ 'flat', 'usage_Mixin', 'global_Mixin' ],
39   'fields' => {
40     'intro_fee' => { 'name' => 'Introductory recurring fee for this package',
41                      'default' => 0,
42                      'validate' => \&validate_money,
43                    },
44     'intro_duration' =>
45          { 'name' => 'Duration of the introductory period, in number of months',
46            'default' => 0,
47            'validate' => \&validate_number,
48          },
49     'show_as_discount' =>
50          { 'name' => 'Show the introductory rate on the invoice as if it\'s a discount',
51            'type' => 'checkbox',
52          },
53   },
54   'fieldorder' => [ qw(intro_duration intro_fee show_as_discount) ],
55   'weight' => 14,
56 );
57
58 sub intro_end {
59   my($self, $cust_pkg) = @_;
60   my ($duration) = ($self->option('intro_duration') =~ /^\s*(\d+)\s*$/);
61   unless (length($duration)) {
62     my $log = FS::Log->new('FS::part_pkg');
63     $log->warning("Invalid intro_duration '".$self->option('intro_duration')."' on pkgpart ".$self->pkgpart
64                 .", defaulting to 0, check package definition");
65     $duration = 0;
66   }
67
68   # no setup or start_date means "start billing the package ASAP", so assume
69   # it would start billing right now.
70   my $start = $cust_pkg->setup || $cust_pkg->start_date || time;
71
72   $self->add_freq($start, $duration);
73 }
74
75 sub base_recur {
76   my($self, $cust_pkg, $time ) = @_;
77
78   my $now;
79   if (!$time) { # the "$sdate" from _make_lines
80     my $log = FS::Log->new('FS::part_pkg');
81     $log->warning("flat_introrate base_recur requires date!");
82     $now = time;
83   } else {
84     $now = $$time;
85   }
86
87   if ($now < $self->intro_end($cust_pkg)) {
88     return $self->option('intro_fee');
89   } else {
90     return $self->option('recur_fee');
91   }
92
93 }
94
95 sub item_discount {
96   my ($self, $cust_pkg) = @_;
97   return unless $self->option('show_as_discount',1);
98   my $intro_end = $self->intro_end($cust_pkg);
99   my $amount = sprintf('%.2f',
100                 $self->option('intro_fee') - $self->option('recur_fee')
101                );
102   return unless $amount < 0;
103   # otherwise it's an "introductory surcharge"? not the intended use of
104   # the feature.
105
106   { '_is_discount'    => 1,
107     'description'     => $cust_pkg->mt('Introductory discount until') . ' ' .
108                          $cust_pkg->time2str_local('short', $intro_end),
109     'setup_amount'    => 0,
110     'recur_amount'    => $amount,
111     'ext_description' => [],
112     'pkgpart'         => $self->pkgpart,
113     'feepart'         => '',
114   }
115 }
116
117 1;