import customers with echeck payment info, RT#39863
[freeside.git] / FS / FS / cust_tax_exempt_pkg.pm
1 package FS::cust_tax_exempt_pkg;
2
3 use strict;
4 use vars qw( @ISA );
5 use FS::Record qw( qsearch qsearchs );
6 use FS::cust_main_Mixin;
7 use FS::cust_bill_pkg;
8 use FS::cust_main_county;
9 use FS::tax_rate;
10 use FS::cust_credit_bill_pkg;
11 use FS::UID qw(dbh);
12 use FS::upgrade_journal;
13
14 # some kind of common ancestor with cust_bill_pkg_tax_location would make sense
15
16 @ISA = qw( FS::cust_main_Mixin FS::Record );
17
18 =head1 NAME
19
20 FS::cust_tax_exempt_pkg - Object methods for cust_tax_exempt_pkg records
21
22 =head1 SYNOPSIS
23
24   use FS::cust_tax_exempt_pkg;
25
26   $record = new FS::cust_tax_exempt_pkg \%hash;
27   $record = new FS::cust_tax_exempt_pkg { 'column' => 'value' };
28
29   $error = $record->insert;
30
31   $error = $new_record->replace($old_record);
32
33   $error = $record->delete;
34
35   $error = $record->check;
36
37 =head1 DESCRIPTION
38
39 An FS::cust_tax_exempt_pkg object represents a record of a customer tax
40 exemption.  Whenever a package would be taxed (based on its location and
41 taxclass), but some or all of it is exempt from taxation, an 
42 FS::cust_tax_exempt_pkg record is created.
43
44 FS::cust_tax_exempt inherits from FS::Record.  The following fields are 
45 currently supported:
46
47 =over 4
48
49 =item exemptpkgnum - primary key
50
51 =item billpkgnum - invoice line item (see L<FS::cust_bill_pkg>) that 
52 was exempted from tax.
53
54 =item taxtype - the object class of the tax record ('FS::cust_main_county'
55 or 'FS::tax_rate').
56
57 =item taxnum - tax rate (see L<FS::cust_main_county>)
58
59 =item year - the year in which the exemption occurred.  NULL if this 
60 is a customer or package exemption rather than a monthly exemption.
61
62 =item month - the month in which the exemption occurred.  NULL if this
63 is a customer or package exemption.
64
65 =item amount - the amount of revenue exempted.  For monthly exemptions
66 this may be anything up to the monthly exemption limit defined in 
67 L<FS::cust_main_county> for this tax.  For customer exemptions it is 
68 always the full price of the line item.  For package exemptions it 
69 may be the setup fee, the recurring fee, or the sum of those.
70
71 =item exempt_cust - flag indicating that the customer is tax-exempt
72 (cust_main.tax = 'Y').
73
74 =item exempt_cust_taxname - flag indicating that the customer is exempt 
75 from the tax with this name (see L<FS::cust_main_exemption).
76
77 =item exempt_setup, exempt_recur: flag indicating that the package's setup
78 or recurring fee is not taxable (part_pkg.setuptax and part_pkg.recurtax).
79
80 =item exempt_monthly: flag indicating that this is a monthly per-customer
81 exemption (Texas tax).
82
83 =back
84
85 =head1 METHODS
86
87 =over 4
88
89 =item new HASHREF
90
91 Creates a new exemption record.  To add the examption record to the database,
92 see L<"insert">.
93
94 Note that this stores the hash reference, not a distinct copy of the hash it
95 points to.  You can ask the object for a copy with the I<hash> method.
96
97 =cut
98
99 # the new method can be inherited from FS::Record, if a table method is defined
100
101 sub table { 'cust_tax_exempt_pkg'; }
102
103 =item insert
104
105 Adds this record to the database.  If there is an error, returns the error,
106 otherwise returns false.
107
108 =cut
109
110 # the insert method can be inherited from FS::Record
111
112 =item delete
113
114 Delete this record from the database.
115
116 =cut
117
118 # the delete method can be inherited from FS::Record
119
120 =item replace OLD_RECORD
121
122 Replaces the OLD_RECORD with this one in the database.  If there is an error,
123 returns the error, otherwise returns false.
124
125 =cut
126
127 # the replace method can be inherited from FS::Record
128
129 =item check
130
131 Checks all fields to make sure this is a valid exemption record.  If there is
132 an error, returns the error, otherwise returns false.  Called by the insert
133 and replace methods.
134
135 =cut
136
137 # the check method should currently be supplied - FS::Record contains some
138 # data checking routines
139
140 sub check {
141   my $self = shift;
142
143   my $error = $self->ut_numbern('exemptnum')
144     || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum')
145     || $self->ut_enum('taxtype', [ 'FS::cust_main_county', 'FS::tax_rate' ])
146     || $self->ut_foreign_keyn('creditbillpkgnum',
147                               'cust_credit_bill_pkg',
148                               'creditbillpkgnum')
149     || $self->ut_numbern('year') #check better
150     || $self->ut_numbern('month') #check better
151     || $self->ut_money('amount')
152     || $self->ut_flag('exempt_cust')
153     || $self->ut_flag('exempt_setup')
154     || $self->ut_flag('exempt_recur')
155     || $self->ut_flag('exempt_cust_taxname')
156     || $self->SUPER::check
157   ;
158
159   $self->get('taxtype') =~ /^FS::(\w+)$/;
160   my $rate_table = $1;
161   $error ||= $self->ut_foreign_key('taxnum', $rate_table, 'taxnum');
162
163   return $error if $error;
164
165   if ( $self->get('exempt_cust') ) {
166     $self->set($_ => '') for qw(
167       exempt_cust_taxname exempt_setup exempt_recur exempt_monthly month year
168     );
169   } elsif ( $self->get('exempt_cust_taxname')  ) {
170     $self->set($_ => '') for qw(
171       exempt_setup exempt_recur exempt_monthly month year
172     );
173   } elsif ( $self->get('exempt_setup') || $self->get('exempt_recur') ) {
174     $self->set($_ => '') for qw(exempt_monthly month year);
175   } elsif ( $self->get('exempt_monthly') ) {
176     $self->year =~ /^\d{4}$/
177         or return "illegal exemption year: '".$self->year."'";
178     $self->month >= 1 && $self->month <= 12
179         or return "illegal exemption month: '".$self->month."'";
180   } else {
181     return "no exemption type selected";
182   }
183
184   '';
185 }
186
187 =item cust_main_county
188
189 =item tax_rate
190
191 Returns the associated tax definition if it still exists in the database.
192 Otherwise returns false.
193
194 =cut
195
196 sub cust_main_county {
197   my $self = shift;
198   my $class = $self->taxtype;
199   $class->by_key($self->taxnum);
200 }
201
202 sub tax_rate {
203   my $self = shift;
204   my $class = $self->taxtype;
205   $class->by_key($self->taxnum);
206 }
207
208 sub _upgrade_data {
209   my $class = shift;
210
211   my $journal = 'cust_tax_exempt_pkg_flags';
212   if ( !FS::upgrade_journal->is_done($journal) ) {
213     my $sql = "UPDATE cust_tax_exempt_pkg SET exempt_monthly = 'Y' ".
214               "WHERE month IS NOT NULL";
215     dbh->do($sql) or die dbh->errstr;
216     FS::upgrade_journal->set_done($journal);
217   }
218
219   $journal = 'cust_tax_exempt_pkg_taxtype';
220   if ( !FS::upgrade_journal->is_done($journal) ) {
221     my $sql = "UPDATE cust_tax_exempt_pkg ".
222               "SET taxtype = 'FS::cust_main_county' WHERE taxtype IS NULL";
223     dbh->do($sql) or die dbh->errstr;
224     $sql =    "UPDATE cust_tax_exempt_pkg_void ".
225               "SET taxtype = 'FS::cust_main_county' WHERE taxtype IS NULL";
226     dbh->do($sql) or die dbh->errstr;
227     FS::upgrade_journal->set_done($journal);
228   }
229
230
231 }
232
233 =back
234
235 =head1 BUGS
236
237 Texas tax is still a royal pain in the ass.
238
239 =head1 SEE ALSO
240
241 L<FS::cust_main_county>, L<FS::cust_bill_pkg>, L<FS::Record>, schema.html from
242 the base documentation.
243
244 =cut
245
246 1;
247