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