fix upgrade for cust_bill_pay_batch when a batched payment was deleted by legacy...
[freeside.git] / FS / FS / discount.pm
1 package FS::discount;
2 use base qw( FS::Record );
3
4 use strict;
5
6 =head1 NAME
7
8 FS::discount - Object methods for discount records
9
10 =head1 SYNOPSIS
11
12   use FS::discount;
13
14   $record = new FS::discount \%hash;
15   $record = new FS::discount { 'column' => 'value' };
16
17   $error = $record->insert;
18
19   $error = $new_record->replace($old_record);
20
21   $error = $record->delete;
22
23   $error = $record->check;
24
25 =head1 DESCRIPTION
26
27 An FS::discount object represents a discount definition.  FS::discount inherits
28 from FS::Record.  The following fields are currently supported:
29
30 =over 4
31
32 =item discountnum
33
34 primary key
35
36 =item name
37
38 name
39
40 =item amount
41
42 amount
43
44 =item percent
45
46 percent
47
48 =item months
49
50 months
51
52 =item disabled
53
54 disabled
55
56 =item setup - apply discount to setup fee (not just to recurring fee)
57
58 If the discount is based on a percentage, then the % will be applied to the
59 setup and recurring portions. 
60
61 =back
62
63 =head1 METHODS
64
65 =over 4
66
67 =item new HASHREF
68
69 Creates a new discount.  To add the discount to the database, see L<"insert">.
70
71 Note that this stores the hash reference, not a distinct copy of the hash it
72 points to.  You can ask the object for a copy with the I<hash> method.
73
74 =cut
75
76 # the new method can be inherited from FS::Record, if a table method is defined
77
78 sub table { 'discount'; }
79
80 =item insert
81
82 Adds this record to the database.  If there is an error, returns the error,
83 otherwise returns false.
84
85 =cut
86
87 # the insert method can be inherited from FS::Record
88
89 =item delete
90
91 Delete this record from the database.
92
93 =cut
94
95 # the delete method can be inherited from FS::Record
96
97 =item replace OLD_RECORD
98
99 Replaces the OLD_RECORD with this one in the database.  If there is an error,
100 returns the error, otherwise returns false.
101
102 =cut
103
104 # the replace method can be inherited from FS::Record
105
106 =item check
107
108 Checks all fields to make sure this is a valid discount.  If there is
109 an error, returns the error, otherwise returns false.  Called by the insert
110 and replace methods.
111
112 =cut
113
114 # the check method should currently be supplied - FS::Record contains some
115 # data checking routines
116
117 sub check {
118   my $self = shift;
119
120   if ( $self->_type eq 'Select discount type' ) {
121     return 'Please select a discount type';
122   } elsif ( $self->amount > 0 ) {
123     $self->set('percent', '0');
124   } elsif ( $self->percent > 0 ) {
125     $self->set('amount', '0.00');
126   } else {
127     return "Discount amount or percentage must be > 0";
128   }
129
130   my $error = 
131     $self->ut_numbern('discountnum')
132     || $self->ut_foreign_keyn('classnum', 'discount_class', 'classnum')
133     || $self->ut_textn('name')
134     || $self->ut_money('amount')
135     || $self->ut_float('percent') #actually decimal, but this will do
136     || $self->ut_floatn('months') #actually decimal, but this will do
137     || $self->ut_enum('disabled', [ '', 'Y' ])
138     || $self->ut_enum('setup', [ '', 'Y' ])
139     #|| $self->ut_enum('linked', [ '', 'Y' ])
140   ;
141   return $error if $error;
142
143 #causes "months must be integers greater than 1" errors when you go back and
144 # try to edit an existing discount (because the months format as NN.000)
145 #not worth whatever reason it came in with "prepayment discounts rt#5318" for
146 #  #discourage non-integer months for package discounts
147 #  if ($self->discountnum) {
148 #    my $sql =
149 #      "SELECT count(*) FROM part_pkg_discount WHERE part_pkg_discount.discountnum = ".
150 #      $self->discountnum;
151 #
152 #    my $count = $self->scalar_sql($sql); 
153 #    return "months must be integers greater than 1"
154 #      if ( $count && ($self->ut_number('months') || $self->months < 2) );
155 #  }
156     
157   $self->SUPER::check;
158 }
159
160 =item description_short
161
162 =item description
163
164 Returns a text description incorporating the amount, percent and months fields.
165
166 description_short omits term information
167
168 =cut
169
170 sub description_short {
171   my $self = shift;
172
173   my $conf = new FS::Conf;
174   my $money_char = $conf->config('money_char') || '$';  
175
176   my $desc;
177   if ( $self->name ) {
178     $desc = $self->name . ': ';
179   } else {
180     $desc = 'Discount of ';
181   }
182   $desc .= $money_char. sprintf('%.2f/month', $self->amount)
183     if $self->amount > 0;
184
185   ( my $percent = $self->percent ) =~ s/\.0+$//;
186   $percent =~ s/(\.\d*[1-9])0+$/$1/;
187   $desc .= $percent. '%'
188     if $self->percent > 0;
189
190   $desc;
191 }
192
193 sub description {
194   my $self = shift;
195   my $desc = $self->description_short;
196
197   ( my $months = $self->months ) =~ s/\.0+$//;
198   $months =~ s/(\.\d*[1-9])0+$/$1/;
199   if ($months) {
200     if ($months == 1) {
201       $desc .= " for 1 month";
202     } else {
203       $desc .= " for $months months";
204     }
205   }
206
207   $desc .= ', applies to setup' if $self->setup;
208
209   $desc;
210 }
211
212 sub classname {
213   my $self = shift;
214   my $discount_class = $self->discount_class;
215   $discount_class ? $discount_class->classname : '(none)';
216 }
217
218 =back
219
220 =head1 BUGS
221
222 =head1 SEE ALSO
223
224 L<FS::cust_pkg_discount>, L<FS::Record>, schema.html from the base documentation.
225
226 =cut
227
228 1;
229