so Search.tsf and Search.rdf work
[freeside.git] / FS / FS / cust_bill_pkg.pm
1 package FS::cust_bill_pkg;
2
3 use strict;
4 use vars qw( @ISA );
5 use FS::Record qw( qsearch qsearchs dbdef dbh );
6 use FS::cust_pkg;
7 use FS::cust_bill;
8 use FS::cust_bill_pkg_detail;
9
10 @ISA = qw( FS::Record );
11
12 =head1 NAME
13
14 FS::cust_bill_pkg - Object methods for cust_bill_pkg records
15
16 =head1 SYNOPSIS
17
18   use FS::cust_bill_pkg;
19
20   $record = new FS::cust_bill_pkg \%hash;
21   $record = new FS::cust_bill_pkg { '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_pkg object represents an invoice line item.
34 FS::cust_bill_pkg inherits from FS::Record.  The following fields are currently
35 supported:
36
37 =over 4
38
39 =item billpkgnum - primary key
40
41 =item invnum - invoice (see L<FS::cust_bill>)
42
43 =item pkgnum - package (see L<FS::cust_pkg>) or 0 for the special virtual sales tax package
44
45 =item setup - setup fee
46
47 =item recur - recurring fee
48
49 =item sdate - starting date of recurring fee
50
51 =item edate - ending date of recurring fee
52
53 =item itemdesc - Line item description (currentlty used only when pkgnum is 0)
54
55 =back
56
57 sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">.  Also
58 see L<Time::Local> and L<Date::Parse> for conversion functions.
59
60 =head1 METHODS
61
62 =over 4
63
64 =item new HASHREF
65
66 Creates a new line item.  To add the line item to the database, see
67 L<"insert">.  Line items are normally created by calling the bill method of a
68 customer object (see L<FS::cust_main>).
69
70 =cut
71
72 sub table { 'cust_bill_pkg'; }
73
74 =item insert
75
76 Adds this line item to the database.  If there is an error, returns the error,
77 otherwise returns false.
78
79 =cut
80
81 sub insert {
82   my $self = shift;
83
84   local $SIG{HUP} = 'IGNORE';
85   local $SIG{INT} = 'IGNORE';
86   local $SIG{QUIT} = 'IGNORE';
87   local $SIG{TERM} = 'IGNORE';
88   local $SIG{TSTP} = 'IGNORE';
89   local $SIG{PIPE} = 'IGNORE';
90
91   my $oldAutoCommit = $FS::UID::AutoCommit;
92   local $FS::UID::AutoCommit = 0;
93   my $dbh = dbh;
94
95   my $error = $self->SUPER::insert;
96   if ( $error ) {
97     $dbh->rollback if $oldAutoCommit;
98     return $error;
99   }
100
101   unless ( defined dbdef->table('cust_bill_pkg_detail') && $self->get('details') ) {
102     $dbh->commit or die $dbh->errstr if $oldAutoCommit;
103     return '';
104   }
105
106   foreach my $detail ( @{$self->get('details')} ) {
107     my $cust_bill_pkg_detail = new FS::cust_bill_pkg_detail {
108       'pkgnum' => $self->pkgnum,
109       'invnum' => $self->invnum,
110       'detail' => $detail,
111     };
112     $error = $cust_bill_pkg_detail->insert;
113     if ( $error ) {
114       $dbh->rollback if $oldAutoCommit;
115       return $error;
116     }
117   }
118
119   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
120   '';
121
122 }
123
124 =item delete
125
126 Currently unimplemented.  I don't remove line items because there would then be
127 no record the items ever existed (which is bad, no?)
128
129 =cut
130
131 sub delete {
132   return "Can't delete cust_bill_pkg records!";
133 }
134
135 =item replace OLD_RECORD
136
137 Currently unimplemented.  This would be even more of an accounting nightmare
138 than deleteing the items.  Just don't do it.
139
140 =cut
141
142 sub replace {
143   return "Can't modify cust_bill_pkg records!";
144 }
145
146 =item check
147
148 Checks all fields to make sure this is a valid line item.  If there is an
149 error, returns the error, otherwise returns false.  Called by the insert
150 method.
151
152 =cut
153
154 sub check {
155   my $self = shift;
156
157   my $error =
158          $self->ut_numbern('billpkgnum')
159       || $self->ut_number('pkgnum')
160       || $self->ut_number('invnum')
161       || $self->ut_money('setup')
162       || $self->ut_money('recur')
163       || $self->ut_numbern('sdate')
164       || $self->ut_numbern('edate')
165       || $self->ut_textn('itemdesc')
166   ;
167   return $error if $error;
168
169   if ( $self->pkgnum != 0 ) { #allow unchecked pkgnum 0 for tax! (add to part_pkg?)
170     return "Unknown pkgnum ". $self->pkgnum
171       unless qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
172   }
173
174   return "Unknown invnum"
175     unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
176
177   $self->SUPER::check;
178 }
179
180 =item cust_pkg
181
182 Returns the package (see L<FS::cust_pkg>) for this invoice line item.
183
184 =cut
185
186 sub cust_pkg {
187   my $self = shift;
188   qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
189 }
190
191 =item details
192
193 Returns an array of detail information for the invoice line item.
194
195 =cut
196
197 sub details {
198   my $self = shift;
199   return () unless defined dbdef->table('cust_bill_pkg_detail');
200   map { $_->detail }
201     qsearch ( 'cust_bill_pkg_detail', { 'pkgnum' => $self->pkgnum,
202                                         'invnum' => $self->invnum, } );
203     #qsearch ( 'cust_bill_pkg_detail', { 'lineitemnum' => $self->lineitemnum });
204 }
205
206 =back
207
208 =head1 BUGS
209
210 =head1 SEE ALSO
211
212 L<FS::Record>, L<FS::cust_bill>, L<FS::cust_pkg>, L<FS::cust_main>, schema.html
213 from the base documentation.
214
215 =cut
216
217 1;
218