Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / FS / part_pkg_taxproduct.pm
1 package FS::part_pkg_taxproduct;
2
3 use strict;
4 use vars qw( @ISA $delete_kludge );
5 use FS::Record qw( qsearch dbh );
6
7 @ISA = qw(FS::Record);
8 $delete_kludge = 0;
9
10 =head1 NAME
11
12 FS::part_pkg_taxproduct - Object methods for part_pkg_taxproduct records
13
14 =head1 SYNOPSIS
15
16   use FS::part_pkg_taxproduct;
17
18   $record = new FS::part_pkg_taxproduct \%hash;
19   $record = new FS::part_pkg_taxproduct { '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::part_pkg_taxproduct object represents a tax product. 
32 FS::part_pkg_taxproduct inherits from FS::Record.  The following fields are
33 currently supported:
34
35 =over 4
36
37 =item taxproductnum
38
39 Primary key
40
41 =item data_vendor
42
43 Tax data vendor
44
45 =item taxproduct
46
47 Tax product id from the vendor
48
49 =item description
50
51 A human readable description of the id in taxproduct
52
53 =back
54
55 =head1 METHODS
56
57 =over 4
58
59 =item new HASHREF
60
61 Creates a new tax product.  To add the tax product to the database, see L<"insert">.
62
63 Note that this stores the hash reference, not a distinct copy of the hash it
64 points to.  You can ask the object for a copy with the I<hash> method.
65
66 =cut
67
68 sub table { 'part_pkg_taxproduct'; }
69
70 =item insert
71
72 Adds this record to the database.  If there is an error, returns the error,
73 otherwise returns false.
74
75 =cut
76
77 =item delete
78
79 Delete this record from the database.
80
81 =cut
82
83 sub delete {
84   my $self = shift;
85
86   return "Can't delete a tax product which has attached package tax rates!"
87     if qsearch( 'part_pkg_taxrate', { 'taxproductnum' => $self->taxproductnum } );
88
89   unless ( $delete_kludge ) {
90     return "Can't delete a tax product which has attached packages!"
91       if qsearch( 'part_pkg', { 'taxproductnum' => $self->taxproductnum } );
92   }
93
94   $self->SUPER::delete(@_);
95 }
96
97 =item replace OLD_RECORD
98
99 Replaces the OLD_RECORD with this one in the database.  If there is an error,
100 returns the error, otherwise returns false.
101
102 =cut
103
104 =item check
105
106 Checks all fields to make sure this is a valid tax product.  If there is
107 an error, returns the error, otherwise returns false.  Called by the insert
108 and replace methods.
109
110 =cut
111
112 sub check {
113   my $self = shift;
114
115   my $error = 
116     $self->ut_numbern('taxproductnum')
117     || $self->ut_textn('data_vendor')
118     || $self->ut_text('taxproduct')
119     || $self->ut_textn('description')
120   ;
121   return $error if $error;
122
123   $self->SUPER::check;
124 }
125
126 =item part_pkg_taxrate GEOCODE
127
128 Returns the L<FS::part_pkg_taxrate> records (tax definitions) that can apply 
129 to this tax product category in the location identified by GEOCODE.
130
131 =cut
132
133 # actually only returns one arbitrary record for each taxclassnum, making 
134 # it useful only for retrieving the taxclassnums
135
136 sub part_pkg_taxrate {
137   my $self = shift;
138   my $data_vendor = $self->data_vendor; # because duh
139   my $geocode = shift;
140
141   my $dbh = dbh;
142
143   # CCH oddness in m2m
144   my $extra_sql .= "AND part_pkg_taxrate.data_vendor = '$data_vendor' ".
145                    "AND (".
146     join(' OR ', map{ 'geocode = '. $dbh->quote(substr($geocode, 0, $_)) }
147                  qw(10 5 2)
148         ).
149     ')';
150   # much more CCH oddness in m2m -- this is kludgy
151   my $tpnums = join(',',
152     map { $_->taxproductnum }
153     $self->expand_cch_taxproduct
154   );
155   $extra_sql .= "AND taxproductnum IN($tpnums)";
156
157   my $addl_from = 'LEFT JOIN part_pkg_taxproduct USING ( taxproductnum )';
158   my $order_by = 'ORDER BY taxclassnum, length(geocode) desc, length(taxproduct) desc';
159   my $select   = 'DISTINCT ON(taxclassnum) *, taxproduct';
160
161   # should qsearch preface columns with the table to facilitate joins?
162   qsearch( { 'table'     => 'part_pkg_taxrate',
163              'select'    => $select,
164              'hashref'   => { 'taxable' => 'Y' },
165              'addl_from' => $addl_from,
166              'extra_sql' => $extra_sql,
167              'order_by'  => $order_by,
168          } );
169 }
170
171 =item expand_cch_taxproduct
172
173 Returns the full set of part_pkg_taxproduct records that are "implied" by 
174 this one.
175
176 =cut
177
178 sub expand_cch_taxproduct {
179   my $self = shift;
180   my $class = shift;
181
182   my ($a,$b,$c,$d) = split ':', $self->taxproduct;
183   $a = '' unless $a; $b = '' unless $b; $c = '' unless $c; $d = '' unless $d;
184   my $taxproducts = join(',',
185     "'${a}:${b}:${c}:${d}'",
186     "'${a}:${b}:${c}:'",
187     "'${a}:${b}::${d}'",
188     "'${a}:${b}::'"
189   );
190   qsearch( {
191       'table'     => 'part_pkg_taxproduct',
192       'hashref'   => { 'data_vendor'=>'cch' },
193       'extra_sql' => "AND taxproduct IN($taxproducts)",
194   } );
195 }
196
197
198 =back
199
200 =cut
201
202 =head1 BUGS
203
204 Confusingly named.  It has nothing to do with part_pkg.
205
206 =head1 SEE ALSO
207
208 L<FS::Record>, schema.html from the base documentation.
209
210 =cut
211
212 1;
213