1 package FS::cust_pkg_discount;
2 use base qw( FS::otaker_Mixin
8 use FS::Record qw( dbh ); # qsearch qsearchs dbh );
13 FS::cust_pkg_discount - Object methods for cust_pkg_discount records
17 use FS::cust_pkg_discount;
19 $record = new FS::cust_pkg_discount \%hash;
20 $record = new FS::cust_pkg_discount { 'column' => 'value' };
22 $error = $record->insert;
24 $error = $new_record->replace($old_record);
26 $error = $record->delete;
28 $error = $record->check;
32 An FS::cust_pkg_discount object represents the application of a discount to a
33 customer package. FS::cust_pkg_discount inherits from FS::Record. The
34 following fields are currently supported:
44 Customer package (see L<FS::cust_pkg>)
48 Discount (see L<FS::discount>)
60 order taker, see L<FS::access_user>
64 whether this discount applies to setup fees or recurring fees
74 Creates a new discount application. To add the record to the database, see
77 Note that this stores the hash reference, not a distinct copy of the hash it
78 points to. You can ask the object for a copy with the I<hash> method.
82 # the new method can be inherited from FS::Record, if a table method is defined
84 sub table { 'cust_pkg_discount'; }
88 Adds this record to the database. If there is an error, returns the error,
89 otherwise returns false.
93 Delete this record from the database.
97 # the delete method can be inherited from FS::Record
99 =item replace OLD_RECORD
101 Replaces the OLD_RECORD with this one in the database. If there is an error,
102 returns the error, otherwise returns false.
106 # the replace method can be inherited from FS::Record
110 Checks all fields to make sure this is a valid discount applciation. If there
111 is an error, returns the error, otherwise returns false. Called by the insert
116 # the check method should currently be supplied - FS::Record contains some
117 # data checking routines
123 $self->ut_numbern('pkgdiscountnum')
124 || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum')
125 || $self->ut_foreign_key('discountnum', 'discount', 'discountnum' )
126 || $self->ut_sfloat('months_used') #actually decimal, but this will do
127 || $self->ut_numbern('end_date')
128 || $self->ut_alphan('otaker')
129 || $self->ut_numbern('usernum')
130 || $self->ut_enum('disabled', [ '', 'Y' ] )
131 || $self->ut_enum('setuprecur', [ 'setup', 'recur' ] )
133 return $error if $error;
135 my $cust_pkg = $self->cust_pkg;
136 my $discount = $self->discount;
137 if ( $self->setuprecur eq 'setup' ) {
138 if ( !$discount->setup ) {
139 # UI prevents this, and historical discounts should never have it either
140 return "Discount #".$self->discountnum." can't be applied to setup fees.";
141 } elsif ( $cust_pkg->base_setup == 0 ) {
143 return "Can't apply setup discount to a package with no setup fee.";
145 # else we're good. do NOT disallow applying setup discounts when the
146 # setup date is already set; upgrades use that.
148 if ( $self->cust_pkg->base_recur == 0 ) {
149 return "Can't apply recur discount to a package with no recurring fee.";
150 } elsif ( $cust_pkg->part_pkg->freq eq '0' ) {
151 return "Can't apply recur discount to a one-time charge.";
155 $self->usernum($FS::CurrentUser::CurrentUser->usernum) unless $self->usernum;
162 Returns the customer package (see L<FS::cust_pkg>).
166 Returns the discount (see L<FS::discount>).
168 =item increment_months_used MONTHS
170 Increments months_used by the given parameter
174 sub increment_months_used {
175 my( $self, $used ) = @_;
176 #UPDATE cust_pkg_discount SET months_used = months_used + ?
177 #leaves no history, and billing is mutexed per-customer, so the dum way is ok
178 $self->months_used( $self->months_used + $used );
182 =item decrement_months_used MONTHS
184 Decrement months_used by the given parameter
186 (Note: as in, extending the length of the discount. Typically only used to
187 stack/extend a discount when the customer package has one active already.)
191 sub decrement_months_used {
192 my( $self, $recharged ) = @_;
193 #UPDATE cust_pkg_discount SET months_used = months_used - ?
194 #leaves no history, and billing is mutexed per-customer
196 #we're run from part_event/Action/referral_pkg_discount on behalf of a
197 # different customer, so we need to grab this customer's mutex.
198 # incidentally, that's some inelegant encapsulation breaking shit, and a
199 # great argument in favor of native-DB trigger history so we can trust
200 # in normal ACID like the SQL above instead of this
201 $self->cust_pkg->cust_main->select_for_update;
203 $self->months_used( $self->months_used - $recharged );
213 my $discount = $self->discount;
215 if ( $self->disabled ne 'Y'
216 and ( ! $discount->months || $self->months_used < $discount->months )
225 # Used by FS::Upgrade to migrate to a new database.
226 sub _upgrade_data { # class method
227 my ($class, %opts) = @_;
228 $class->_upgrade_otaker(%opts);
230 # #14092: set setuprecur field on discounts. if we get one that applies to
231 # both setup and recur, split it into two discounts.
232 my $search = FS::Cursor->new({
233 table => 'cust_pkg_discount',
234 hashref => { setuprecur => '' }
236 while ( my $cust_pkg_discount = $search->fetch ) {
237 my $discount = $cust_pkg_discount->discount;
238 my $cust_pkg = $cust_pkg_discount->cust_pkg;
239 # 1. Does it apply to the setup fee?
240 # Yes, if: the discount applies to setup fees generally, and the package
242 # No, if: the discount is a flat amount, and is not first-month only.
243 if ( $discount->setup
244 and $cust_pkg->base_setup > 0
245 and ($discount->amount == 0 or $discount->months == 1)
248 # then clone this discount into a new one
249 my $setup_discount = FS::cust_pkg_discount->new({
250 $cust_pkg_discount->hash,
251 setuprecur => 'setup',
254 my $error = $setup_discount->insert;
255 die "$error (migrating cust_pkg_discount to setup discount)" if $error;
257 # 2. Does it apply to the recur fee?
258 # Yes, if: the package has a recur fee.
259 if ( $cust_pkg->base_recur > 0 ) {
260 # then modify this discount in place
261 $cust_pkg_discount->set('setuprecur' => 'recur');
262 my $error = $cust_pkg_discount->replace;
263 die "$error (migrating cust_pkg_discount)" if $error;
265 # not in here yet: splitting the cust_bill_pkg_discount records.
266 # (not really necessary)
276 L<FS::discount>, L<FS::cust_pkg>, L<FS::Record>, schema.html from the base documentation.