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