8bd80887ed71cbae2b99221bb39a67715c1c66db
[freeside.git] / FS / FS / cust_bill_pkg_void.pm
1 package FS::cust_bill_pkg_void;
2 use base qw( FS::TemplateItem_Mixin FS::Record );
3
4 use strict;
5 use FS::Record qw( qsearch qsearchs dbh fields );
6 use FS::cust_bill_void;
7 use FS::cust_bill_pkg_detail;
8 use FS::cust_bill_pkg_display;
9 use FS::cust_bill_pkg_discount;
10 use FS::cust_bill_pkg;
11 use FS::cust_bill_pkg_fee;
12 use FS::cust_bill_pkg_tax_location;
13 use FS::cust_bill_pkg_tax_rate_location;
14 use FS::cust_tax_exempt_pkg;
15 use FS::Cursor;
16
17 =head1 NAME
18
19 FS::cust_bill_pkg_void - Object methods for cust_bill_pkg_void records
20
21 =head1 SYNOPSIS
22
23   use FS::cust_bill_pkg_void;
24
25   $record = new FS::cust_bill_pkg_void \%hash;
26   $record = new FS::cust_bill_pkg_void { 'column' => 'value' };
27
28   $error = $record->insert;
29
30   $error = $new_record->replace($old_record);
31
32   $error = $record->delete;
33
34   $error = $record->check;
35
36 =head1 DESCRIPTION
37
38 An FS::cust_bill_pkg_void object represents a voided invoice line item.
39 FS::cust_bill_pkg_void inherits from FS::Record.  The following fields are
40 currently supported:
41
42 =over 4
43
44 =item billpkgnum
45
46 primary key
47
48 =item invnum
49
50 invnum
51
52 =item pkgnum
53
54 pkgnum
55
56 =item pkgpart_override
57
58 pkgpart_override
59
60 =item setup
61
62 setup
63
64 =item recur
65
66 recur
67
68 =item sdate
69
70 sdate
71
72 =item edate
73
74 edate
75
76 =item itemdesc
77
78 itemdesc
79
80 =item itemcomment
81
82 itemcomment
83
84 =item section
85
86 section
87
88 =item freq
89
90 freq
91
92 =item quantity
93
94 quantity
95
96 =item unitsetup
97
98 unitsetup
99
100 =item unitrecur
101
102 unitrecur
103
104 =item hidden
105
106 hidden
107
108
109 =back
110
111 =head1 METHODS
112
113 =over 4
114
115 =item new HASHREF
116
117 Creates a new record.  To add the record to the database, see L<"insert">.
118
119 Note that this stores the hash reference, not a distinct copy of the hash it
120 points to.  You can ask the object for a copy with the I<hash> method.
121
122 =cut
123
124 sub table { 'cust_bill_pkg_void'; }
125
126 sub detail_table            { 'cust_bill_pkg_detail_void'; }
127 sub display_table           { 'cust_bill_pkg_display_void'; }
128 sub discount_table          { 'cust_bill_pkg_discount_void'; }
129 #sub tax_location_table      { 'cust_bill_pkg_tax_location'; }
130 #sub tax_rate_location_table { 'cust_bill_pkg_tax_rate_location'; }
131 #sub tax_exempt_pkg_table    { 'cust_tax_exempt_pkg'; }
132
133 =item insert
134
135 Adds this record to the database.  If there is an error, returns the error,
136 otherwise returns false.
137
138 =item unvoid 
139
140 "Un-void"s this line item: Deletes the voided line item from the database and
141 adds back a normal line item (and related tables).
142
143 =cut
144
145 sub unvoid {
146   my $self = shift;
147
148   local $SIG{HUP} = 'IGNORE';
149   local $SIG{INT} = 'IGNORE';
150   local $SIG{QUIT} = 'IGNORE';
151   local $SIG{TERM} = 'IGNORE';
152   local $SIG{TSTP} = 'IGNORE';
153   local $SIG{PIPE} = 'IGNORE';
154
155   my $oldAutoCommit = $FS::UID::AutoCommit;
156   local $FS::UID::AutoCommit = 0;
157   my $dbh = dbh;
158
159   my $cust_bill_pkg = new FS::cust_bill_pkg ( {
160     map { $_ => $self->get($_) } fields('cust_bill_pkg')
161   } );
162   my $error = $cust_bill_pkg->insert;
163   if ( $error ) {
164     $dbh->rollback if $oldAutoCommit;
165     return $error;
166   }
167
168   foreach my $table (qw(
169     cust_bill_pkg_detail
170     cust_bill_pkg_display
171     cust_bill_pkg_discount
172     cust_bill_pkg_tax_location
173     cust_bill_pkg_tax_rate_location
174     cust_tax_exempt_pkg
175     cust_bill_pkg_fee
176   )) {
177
178     foreach my $voided (
179       qsearch($table.'_void', { billpkgnum=>$self->billpkgnum })
180     ) {
181
182       my $class = 'FS::'.$table;
183       my $unvoid = $class->new( {
184         map { $_ => $voided->get($_) } fields($table)
185       });
186       my $error = $unvoid->insert || $voided->delete;
187       if ( $error ) {
188         $dbh->rollback if $oldAutoCommit;
189         return $error;
190       }
191
192     }
193
194   }
195
196   $error = $self->delete;
197   if ( $error ) {
198     $dbh->rollback if $oldAutoCommit;
199     return $error;
200   }
201
202   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
203
204   '';
205
206 }
207
208 =item delete
209
210 Delete this record from the database.
211
212 =item replace OLD_RECORD
213
214 Replaces the OLD_RECORD with this one in the database.  If there is an error,
215 returns the error, otherwise returns false.
216
217 =item check
218
219 Checks all fields to make sure this is a valid record.  If there is
220 an error, returns the error, otherwise returns false.  Called by the insert
221 and replace methods.
222
223 =cut
224
225 sub check {
226   my $self = shift;
227
228   my $error = 
229     $self->ut_number('billpkgnum')
230     || $self->ut_snumber('pkgnum')
231     || $self->ut_number('invnum') #cust_bill or cust_bill_void, if we ever support line item voiding
232     || $self->ut_numbern('pkgpart_override')
233     || $self->ut_money('setup')
234     || $self->ut_money('recur')
235     || $self->ut_numbern('sdate')
236     || $self->ut_numbern('edate')
237     || $self->ut_textn('itemdesc')
238     || $self->ut_textn('itemcomment')
239     || $self->ut_textn('section')
240     || $self->ut_textn('freq')
241     || $self->ut_numbern('quantity')
242     || $self->ut_moneyn('unitsetup')
243     || $self->ut_moneyn('unitrecur')
244     || $self->ut_enum('hidden', [ '', 'Y' ])
245     || $self->ut_numbern('feepart')
246   ;
247   return $error if $error;
248
249   $self->SUPER::check;
250 }
251
252 =item cust_bill
253
254 Returns the voided invoice (see L<FS::cust_bill_void>) for this voided line
255 item.
256
257 =cut
258
259 sub cust_bill {
260   my $self = shift;
261   #cust_bill or cust_bill_void, if we ever support line item voiding
262   qsearchs( 'cust_bill_void', { 'invnum' => $self->invnum } );
263 }
264
265 sub cust_bill_pkg_fee {
266   my $self = shift;
267   qsearch( 'cust_bill_pkg_fee_void', { 'billpkgnum' => $self->billpkgnum } );
268 }
269
270 # _upgrade_data
271 #
272 # Used by FS::Upgrade to migrate to a new database.
273 sub _upgrade_data {  # class method
274   my ($class, %opts) = @_;
275
276   my $error;
277   # fix voids with tax from before July 2013, when the taxable_billpkgnum
278   # field was added to the void table
279   my $search = FS::Cursor->new({
280     'table'   => 'cust_bill_pkg_tax_location_void',
281     'hashref' => { 'taxable_billpkgnum' => '' }
282   });
283   while (my $void = $search->fetch) {
284     # the history for the unvoided record should have the correct
285     # taxable_billpkgnum
286     my $num = $void->billpkgtaxlocationnum;
287     my $unvoid = qsearchs({
288       'table'   => 'h_cust_bill_pkg_tax_location',
289       'hashref' => { 'billpkgtaxlocationnum' => $num },
290       'order_by' => ' ORDER BY history_date DESC LIMIT 1'
291     });
292     if (!$unvoid) {
293       # should never happen
294       die "billpkgtaxlocationnum $num: could not find pre-void history record to restore taxable_billpkgnum.";
295     }
296     if ($unvoid) {
297       $void->set('taxable_billpkgnum', $unvoid->taxable_billpkgnum);
298       $error = $void->replace;
299       die "billpkgtaxlocationnum $num: $error\n" if $error;
300     }
301   }
302
303 }
304
305 =back
306
307 =head1 BUGS
308
309 =head1 SEE ALSO
310
311 L<FS::Record>, schema.html from the base documentation.
312
313 =cut
314
315 1;
316