Changes to add classified reasons for invoice void
[freeside.git] / FS / FS / cust_bill_void.pm
1 package FS::cust_bill_void;
2 use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin
3              FS::reason_Mixin FS::Record );
4
5 use strict;
6 use FS::Record qw( qsearch qsearchs dbh fields );
7 use FS::cust_statement;
8 use FS::access_user;
9 use FS::cust_bill_pkg_void;
10 use FS::cust_bill;
11
12 =head1 NAME
13
14 FS::cust_bill_void - Object methods for cust_bill_void records
15
16 =head1 SYNOPSIS
17
18   use FS::cust_bill_void;
19
20   $record = new FS::cust_bill_void \%hash;
21   $record = new FS::cust_bill_void { 'column' => 'value' };
22
23   $error = $record->insert;
24
25   $error = $new_record->replace($old_record);
26
27   $error = $record->delete;
28
29   $error = $record->check;
30
31 =head1 DESCRIPTION
32
33 An FS::cust_bill_void object represents a voided invoice.  FS::cust_bill_void
34 inherits from FS::Record.  The following fields are currently supported:
35
36 =over 4
37
38 =item invnum
39
40 primary key
41
42 =item custnum
43
44 custnum
45
46 =item _date
47
48 _date
49
50 =item charged
51
52 charged
53
54 =item invoice_terms
55
56 invoice_terms
57
58 =item previous_balance
59
60 previous_balance
61
62 =item billing_balance
63
64 billing_balance
65
66 =item closed
67
68 closed
69
70 =item statementnum
71
72 statementnum
73
74 =item agent_invid
75
76 agent_invid
77
78 =item promised_date
79
80 promised_date
81
82 =item void_date
83
84 void_date
85
86 =item reason 
87
88 freeform string (deprecated)
89
90 =item reasonnum 
91
92 reason for voiding the payment (see L<FS::reson>)
93
94 =item void_usernum
95
96 void_usernum
97
98
99 =back
100
101 =head1 METHODS
102
103 =over 4
104
105 =item new HASHREF
106
107 Creates a new voided invoice.  To add the voided invoice to the database, see L<"insert">.
108
109 Note that this stores the hash reference, not a distinct copy of the hash it
110 points to.  You can ask the object for a copy with the I<hash> method.
111
112 =cut
113
114 sub table { 'cust_bill_void'; }
115 sub notice_name { 'VOIDED Invoice'; }
116 sub template_conf { 'invoice_'; }
117
118 sub has_sections {
119   my $self = shift;
120   my $agentnum = $self->cust_main->agentnum;
121   my $tc = $self->template_conf;
122
123   $self->conf->exists($tc.'sections', $agentnum) ||
124   $self->conf->exists($tc.'sections_by_location', $agentnum);
125 }
126
127
128 =item insert
129
130 Adds this record to the database.  If there is an error, returns the error,
131 otherwise returns false.
132
133 =cut
134
135 =item unvoid 
136
137 "Un-void"s this invoice: Deletes the voided invoice from the database and adds
138 back a normal invoice (and related tables).
139
140 =cut
141
142 sub unvoid {
143   my $self = shift;
144
145   local $SIG{HUP} = 'IGNORE';
146   local $SIG{INT} = 'IGNORE';
147   local $SIG{QUIT} = 'IGNORE';
148   local $SIG{TERM} = 'IGNORE';
149   local $SIG{TSTP} = 'IGNORE';
150   local $SIG{PIPE} = 'IGNORE';
151
152   my $oldAutoCommit = $FS::UID::AutoCommit;
153   local $FS::UID::AutoCommit = 0;
154   my $dbh = dbh;
155
156   my $cust_bill = new FS::cust_bill ( {
157     map { $_ => $self->get($_) } fields('cust_bill')
158   } );
159   my $error = $cust_bill->insert;
160   if ( $error ) {
161     $dbh->rollback if $oldAutoCommit;
162     return $error;
163   }
164
165   foreach my $cust_bill_pkg_void ( $self->cust_bill_pkg ) {
166     my $error = $cust_bill_pkg_void->unvoid;
167     if ( $error ) {
168       $dbh->rollback if $oldAutoCommit;
169       return $error;
170     }
171   }
172
173   $error = $self->delete;
174   if ( $error ) {
175     $dbh->rollback if $oldAutoCommit;
176     return $error;
177   }
178
179   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
180
181   '';
182
183 }
184
185 =item delete
186
187 Delete this record from the database.
188
189 =cut
190
191 =item replace OLD_RECORD
192
193 Replaces the OLD_RECORD with this one in the database.  If there is an error,
194 returns the error, otherwise returns false.
195
196 =cut
197
198 =item check
199
200 Checks all fields to make sure this is a valid voided invoice.  If there is
201 an error, returns the error, otherwise returns false.  Called by the insert
202 and replace methods.
203
204 =cut
205
206 sub check {
207   my $self = shift;
208
209   my $error = 
210     $self->ut_number('invnum')
211     || $self->ut_foreign_key('custnum', 'cust_main', 'custnum' )
212     || $self->ut_numbern('_date')
213     || $self->ut_money('charged')
214     || $self->ut_textn('invoice_terms')
215     || $self->ut_moneyn('previous_balance')
216     || $self->ut_moneyn('billing_balance')
217     || $self->ut_enum('closed', [ '', 'Y' ])
218     || $self->ut_foreign_keyn('statementnum', 'cust_statement', 'statementnum')
219     || $self->ut_numbern('agent_invid')
220     || $self->ut_numbern('promised_date')
221     || $self->ut_numbern('void_date')
222     || $self->ut_textn('reason')
223     || $self->ut_numbern('void_usernum')
224     || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum')
225   ;
226   return $error if $error;
227
228   $self->void_date(time) unless $self->void_date;
229
230   $self->void_usernum($FS::CurrentUser::CurrentUser->usernum)
231     unless $self->void_usernum;
232
233   $self->SUPER::check;
234 }
235
236 =item display_invnum
237
238 Returns the displayed invoice number for this invoice: agent_invid if
239 cust_bill-default_agent_invid is set and it has a value, invnum otherwise.
240
241 =cut
242
243 sub display_invnum {
244   my $self = shift;
245   my $conf = $self->conf;
246   if ( $conf->exists('cust_bill-default_agent_invid') && $self->agent_invid ){
247     return $self->agent_invid;
248   } else {
249     return $self->invnum;
250   }
251 }
252
253 =item void_access_user
254
255 Returns the voiding employee object (see L<FS::access_user>).
256
257 =cut
258
259 sub void_access_user {
260   my $self = shift;
261   qsearchs('access_user', { 'usernum' => $self->void_usernum } );
262 }
263
264 =item cust_main
265
266 =item cust_bill_pkg
267
268 =item reason
269
270 Returns the text of the associated void reason (see L<FS::reason>) for this.
271
272 =cut
273
274 sub cust_bill_pkg { #actually cust_bill_pkg_void objects
275   my $self = shift;
276   qsearch('cust_bill_pkg_void', { invnum=>$self->invnum });
277 }
278
279 =back
280
281 =item cust_pkg
282
283 Returns the packages (see L<FS::cust_pkg>) corresponding to the line items for
284 this invoice.
285
286 =cut
287
288 sub cust_pkg {
289   my $self = shift;
290   my @cust_pkg = map { $_->pkgnum > 0 ? $_->cust_pkg : () }
291                  $self->cust_bill_pkg;
292   my %saw = ();
293   grep { ! $saw{$_->pkgnum}++ } @cust_pkg;
294 }
295
296 =item search_sql_where HASHREF
297
298 Class method which returns an SQL WHERE fragment to search for parameters
299 specified in HASHREF.  Accepts the following parameters for 
300 L<FS::cust_bill::search_sql_where>: C<_date>, C<invnum_min>, C<invnum_max>,
301 C<agentnum>, C<custnum>, C<cust_classnum>, C<refnum>.  Also 
302 accepts the following:
303
304 =over 4
305
306 =item void_date
307
308 Arrayref of start and end date to find invoices voided in a date range.
309
310 =item void_usernum
311
312 User identifier (L<FS::access_user> key) that voided the invoice.
313
314 =back
315
316 =cut
317
318 sub search_sql_where {
319   my($class, $param) = @_;
320
321   my $cust_bill_param = {
322     map { $_ => $param->{$_} }
323     grep { exists($param->{$_}) }
324     qw( _date invnum_min invnum_max agentnum custnum cust_classnum 
325         refnum )
326   };
327   my $search_sql = FS::cust_bill->search_sql_where($cust_bill_param);
328   $search_sql =~ s/cust_bill/cust_bill_void/g;
329   my @search = ($search_sql);
330
331   if ( $param->{void_date} ) {
332     my($beginning, $ending) = @{$param->{void_date}};
333     push @search, "cust_bill_void.void_date >= $beginning",
334                   "cust_bill_void.void_date <  $ending";
335   }
336
337   if ( $param->{void_usernum} =~ /^(\d+)$/ ) {
338     my $usernum = $1;
339     push @search, "cust_bill_void.void_usernum = $1";
340   }
341
342   join(" AND ", @search);
343 }
344
345
346 =item enable_previous
347
348 =cut
349
350 sub enable_previous { 0 }
351
352 =back
353
354 =head1 BUGS
355
356 =head1 SEE ALSO
357
358 L<FS::Record>, schema.html from the base documentation.
359
360 =cut
361
362 1;
363