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