7b448dd189234ef89f679690adc86f3b8d660bb7
[freeside.git] / FS / FS / cust_event_fee.pm
1 package FS::cust_event_fee;
2 use base qw( FS::cust_main_Mixin FS::Record FS::FeeOrigin_Mixin );
3
4 use strict;
5 use FS::Record qw( qsearch dbh );
6
7 =head1 NAME
8
9 FS::cust_event_fee - Object methods for cust_event_fee records
10
11 =head1 SYNOPSIS
12
13   use FS::cust_event_fee;
14
15   $record = new FS::cust_event_fee \%hash;
16   $record = new FS::cust_event_fee { 'column' => 'value' };
17
18   $error = $record->insert;
19
20   $error = $new_record->replace($old_record);
21
22   $error = $record->delete;
23
24   $error = $record->check;
25
26 =head1 DESCRIPTION
27
28 An FS::cust_event_fee object links a billing event that charged a fee
29 (an L<FS::cust_event>) to the resulting invoice line item (an 
30 L<FS::cust_bill_pkg> object).  FS::cust_event_fee inherits from FS::Record 
31 and FS::FeeOrigin_Mixin.  The following fields are currently supported:
32
33 =over 4
34
35 =item eventfeenum - primary key
36
37 =item eventnum - key of the cust_event record that required the fee to be 
38 created.  This is a unique column; there's no reason for a single event 
39 instance to create more than one fee.
40
41 =item billpkgnum - key of the cust_bill_pkg record representing the fee 
42 on an invoice.  This is also a unique column but can be NULL to indicate
43 a fee that hasn't been billed yet.  In that case it will be billed the next
44 time billing runs for the customer.
45
46 =item feepart - key of the fee definition (L<FS::part_fee>).
47
48 =item nextbill - 'Y' if the fee should be charged on the customer's next
49 bill, rather than causing a bill to be produced immediately.
50
51 =back
52
53 =head1 METHODS
54
55 =over 4
56
57 =item new HASHREF
58
59 Creates a new event-fee link.  To add the record to the database, 
60 see L<"insert">.
61
62 =cut
63
64 sub table { 'cust_event_fee'; }
65
66 =item insert
67
68 Adds this record to the database.  If there is an error, returns the error,
69 otherwise returns false.
70
71 =item delete
72
73 Delete this record from the database.
74
75 =item replace OLD_RECORD
76
77 Replaces the OLD_RECORD with this one in the database.  If there is an error,
78 returns the error, otherwise returns false.
79
80 =item check
81
82 Checks all fields to make sure this is a valid example.  If there is
83 an error, returns the error, otherwise returns false.  Called by the insert
84 and replace methods.
85
86 =cut
87
88 sub check {
89   my $self = shift;
90
91   my $error = 
92     $self->ut_numbern('eventfeenum')
93     || $self->ut_foreign_key('eventnum', 'cust_event', 'eventnum')
94     || $self->ut_foreign_keyn('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
95     || $self->ut_foreign_key('feepart', 'part_fee', 'feepart')
96     || $self->ut_flag('nextbill')
97   ;
98   return $error if $error;
99
100   $self->SUPER::check;
101 }
102
103 =back
104
105 =head1 CLASS METHODS
106
107 =over 4
108
109 =item _by_cust CUSTNUM[, PARAMS]
110
111 See L<FS::FeeOrigin_Mixin/by_cust>. This is the implementation for 
112 event-triggered fees.
113
114 =cut
115
116 sub _by_cust {
117   my $class = shift;
118   my $custnum = shift or return;
119   my %params = @_;
120   $custnum =~ /^\d+$/ or die "bad custnum $custnum";
121
122   # silliness
123   my $where = ($params{hashref} && keys (%{ $params{hashref} }))
124               ? 'AND'
125               : 'WHERE';
126   qsearch({
127     table     => 'cust_event_fee',
128     addl_from => 'JOIN cust_event USING (eventnum) ' .
129                  'JOIN part_event USING (eventpart) ',
130     extra_sql => "$where eventtable = 'cust_main' ".
131                  "AND cust_event.tablenum = $custnum",
132     %params
133   }),
134   qsearch({
135     table     => 'cust_event_fee',
136     addl_from => 'JOIN cust_event USING (eventnum) ' .
137                  'JOIN part_event USING (eventpart) ' .
138                  'JOIN cust_bill ON (cust_event.tablenum = cust_bill.invnum)',
139     extra_sql => "$where eventtable = 'cust_bill' ".
140                  "AND cust_bill.custnum = $custnum",
141     %params
142   }),
143   qsearch({
144     table     => 'cust_event_fee',
145     addl_from => 'JOIN cust_event USING (eventnum) ' .
146                  'JOIN part_event USING (eventpart) ' .
147                  'JOIN cust_pay_batch ON (cust_event.tablenum = cust_pay_batch.paybatchnum)',
148     extra_sql => "$where eventtable = 'cust_pay_batch' ".
149                  "AND cust_pay_batch.custnum = $custnum",
150     %params
151   }),
152   qsearch({
153     table     => 'cust_event_fee',
154     addl_from => 'JOIN cust_event USING (eventnum) ' .
155                  'JOIN part_event USING (eventpart) ' .
156                  'JOIN cust_pkg ON (cust_event.tablenum = cust_pkg.pkgnum)',
157     extra_sql => "$where eventtable = 'cust_pkg' ".
158                  "AND cust_pkg.custnum = $custnum",
159     %params
160   })
161 }
162
163 =item cust_bill
164
165 See L<FS::FeeOrigin_Mixin/cust_bill>. This version simply returns the event
166 object if the event is an invoice event.
167
168 =cut
169
170 sub cust_bill {
171   my $self = shift;
172   my $object = $self->cust_event->cust_X;
173   if ( $object->isa('FS::cust_bill') ) {
174     return $object;
175   } else {
176     return '';
177   }
178 }
179
180 =item cust_pkg
181
182 See L<FS::FeeOrigin_Mixin/cust_bill>. This version simply returns the event
183 object if the event is a package event.
184
185 =cut
186
187 sub cust_pkg {
188   my $self = shift;
189   my $object = $self->cust_event->cust_X;
190   if ( $object->isa('FS::cust_pkg') ) {
191     return $object;
192   } else {
193     return '';
194   }
195 }
196
197 =back
198
199 =cut
200
201 sub _upgrade_schema {
202   my ($class, %opts) = @_;
203
204   my $sql = '
205     DELETE FROM cust_event_Fee WHERE NOT EXISTS
206       ( SELECT 1 FROM cust_event WHERE cust_event.eventnum = cust_event_fee.eventnum )
207   ';
208
209   my $sth = dbh->prepare($sql) or die dbh->errstr;
210   $sth->execute or die $sth->errstr;
211   '';
212 }
213
214
215 =head1 BUGS
216
217 =head1 SEE ALSO
218
219 L<FS::cust_event>, L<FS::FeeOrigin_Mixin>, L<FS::Record>
220
221 =cut
222
223 1;
224