new tax rating engine
[freeside.git] / httemplate / browse / tax_rate.cgi
1 <% include( 'elements/browse.html',
2      'title'          => "Tax Rates $title",
3      'name_singular'  => 'tax rate',
4      'menubar'        => \@menubar,
5      'html_init'      => $html_init,
6      'query'          => {
7                            'table'     => 'tax_rate',
8                            'hashref'   => $hashref,
9                            'order_by'  => 'ORDER BY geocode, taxclassnum',
10                            'extra_sql' => $extra_sql,
11                          },
12      'count_query'    => $count_query,
13      'header'         => \@header,
14      'header2'        => \@header2,
15      'fields'         => \@fields,
16      'align'          => $align,
17      'color'          => \@color,
18      'cell_style'     => \@cell_style,
19      'links'          => \@links,
20      'link_onclicks'  => \@link_onclicks,
21   )
22 %>
23 <%once>
24
25 my $conf = new FS::Conf;
26 my $money_char = $conf->config('money_char') || '$';
27
28 my $rate_sub = sub {
29   my $tax_rate = shift;
30
31   my $units = $tax_rate->unittype_name;
32   $units =~ s/ /&nbsp;/g;
33
34   my @rate = ();
35   push @rate,
36       ($tax_rate->tax * 100). '%&nbsp;<FONT SIZE="-1">(edit)</FONT>'
37     if $tax_rate->tax > 0 || $tax_rate->taxbase > 0;
38   push @rate,
39       ($tax_rate->excessrate * 100). '%&nbsp;<FONT SIZE="-1">(edit)</FONT>'
40     if $tax_rate->excessrate > 0;
41   push @rate,
42       $money_char. $tax_rate->fee.
43       qq!&nbsp;per&nbsp;$units<FONT SIZE="-1">(edit)</FONT>!
44     if $tax_rate->fee > 0 || $tax_rate->feebase > 0;
45   push @rate,
46       $money_char. $tax_rate->excessfee.
47       qq!&nbsp;per&nbsp;$units<FONT SIZE="-1">(edit)</FONT>!
48     if $tax_rate->excessfee > 0;
49
50
51   [ map [ {'data'=>$_} ], @rate ];
52 };
53
54 my $limit_sub = sub {
55   my $tax_rate = shift;
56
57   my $maxtype = $tax_rate->maxtype_name;
58   $maxtype =~ s/ /&nbsp;/g;
59
60   my $units = $tax_rate->unittype_name;
61   $units =~ s/ /&nbsp;/g;
62
63   my @limit = ();
64   push @limit,
65        sprintf("$money_char%.2f&nbsp%s", $tax_rate->taxbase, $maxtype )
66     if $tax_rate->taxbase > 0;
67   push @limit,
68        sprintf("$money_char%.2f&nbsp;tax", $tax_rate->taxmax )
69     if $tax_rate->taxmax > 0;
70   push @limit,
71        $tax_rate->feebase. "&nbsp;$units". ($tax_rate->feebase == 1 ? '' : 's')
72     if $tax_rate->feebase > 0;
73   push @limit,
74        $tax_rate->feemax. "&nbsp;$units". ($tax_rate->feebase == 1 ? '' : 's')
75     if $tax_rate->feemax > 0;
76
77   push @limit, 'Excluding&nbsp;setup&nbsp;fee'
78     if $tax_rate->setuptax =~ /^Y$/i;
79
80   push @limit, 'Excluding&nbsp;recurring&nbsp;fee'
81     if $tax_rate->recurtax =~ /^Y$/i;
82
83   [ map [ {'data'=>$_} ], @limit ];
84 };
85
86 my $oldrow;
87 my $cell_style;
88 my $cell_style_sub = sub {
89   my $row = shift;
90   if ( $oldrow ne $row ) {
91     if ( $oldrow ) {
92       if ( $oldrow->country ne $row->country ) {
93         $cell_style = 'border-top:1px solid #000000';
94       } elsif ( $oldrow->state ne $row->state ) {
95         $cell_style = 'border-top:1px solid #cccccc'; #default?
96       } elsif ( $oldrow->state eq $row->state ) {
97         #$cell_style = 'border-top:dashed 1px dark gray';
98         $cell_style = 'border-top:1px dashed #cccccc';
99       }
100     }
101     $oldrow = $row;
102   }
103   return $cell_style;
104 };
105
106 my $select_link = [ 'javascript:void(0);', sub { ''; } ];
107
108 my $select_onclick = sub {
109   my $row = shift;
110   my $taxnum = $row->taxnum;
111   my $color = '#333399';
112   qq!overlib( OLiframeContent('${p}edit/tax_rate.html?$taxnum', 540, 620, 'edit_tax_rate_popup' ), CAPTION, 'Edit tax rate', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, BGCOLOR, '$color', CGCOLOR, '$color' ); return false;!;
113 };
114
115 </%once>
116 <%init>
117
118 die "access denied"
119   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
120
121 my @menubar;
122 my $title = '';
123
124 my $data_vendor = '';
125 if ( $cgi->param('data_vendor') =~ /^(\w+)$/ ) {
126   $data_vendor = $1;
127   $title = "$data_vendor";
128 }
129 $cgi->delete('data_vendor');
130
131 my $geocode = '';
132 if ( $cgi->param('geocode') =~ /^(\w+)$/ ) {
133   $geocode = $1;
134   $title = " geocode $geocode";
135 }
136 $cgi->delete('geocode');
137
138 $title = " for $title" if $title;
139
140 my $taxclassnum = '';
141 if ( $cgi->param('taxclassnum') =~ /^(\d+)$/ ) {
142   $taxclassnum = $1;
143   my $tax_class = qsearchs('tax_class', {'taxclassnum' => $taxclassnum});
144   if ($tax_class) {
145     $title .= " for ". $tax_class->taxclass.
146               " (".  $tax_class->description. ") tax class";
147   }else{
148     $taxclassnum = '';
149   }
150 }
151 $cgi->delete('taxclassnum');
152
153 my $tax_type = $1
154   if ( $cgi->param('tax_type') =~ /^(\d+)$/ );
155 my $tax_cat = $1
156   if ( $cgi->param('tax_cat') =~ /^(\d+)$/ );
157
158 my @taxclassnum = ();
159 if ($tax_type || $tax_cat ) {
160   my $compare = "LIKE '". ( $tax_type || "%" ). ":". ( $tax_cat || "%" ). "'";
161   $compare = "= '$tax_type:$tax_cat'" if ($tax_type && $tax_cat);
162   my @tax_class =
163     qsearch({ 'table'     => 'tax_class',
164               'hashref'   => {},
165               'extra_sql' => "WHERE taxclass $compare",
166            });
167   if (@tax_class) {
168     @taxclassnum = map { $_->taxclassnum } @tax_class;
169     $tax_class[0]->description =~ /^(.*):(.*)/;
170     $title .= " for";
171     $title .= " $tax_type ($1) tax type" if $tax_type;
172     $title .= " and" if ($tax_type && $tax_cat);
173     $title .= " $tax_cat ($2) tax category" if $tax_cat;
174   }else{
175     $tax_type = '';
176     $tax_cat = '';
177   }
178 }
179 $cgi->delete('tax_type');
180 $cgi->delete('tax_cat');
181
182
183 if ( $geocode || $taxclassnum ) {
184   push @menubar, 'View all tax rates' => $p.'browse/tax_rate.cgi';
185 }
186
187 $cgi->param('dummy', 1);
188
189 #restore this so pagination works
190 $cgi->param('data_vendor',  $data_vendor) if $data_vendor;
191 $cgi->param('geocode',  $geocode) if $geocode;
192 $cgi->param('taxclassnum', $taxclassnum ) if $taxclassnum;
193 $cgi->param('tax_type', $tax_type ) if $tax_type;
194 $cgi->param('tax_cat', $tax_cat ) if $tax_cat;
195
196 my $hashref = {};
197 my $extra_sql = '';
198 if ( $data_vendor ) {
199   $extra_sql .= ' WHERE data_vendor = '. dbh->quote($data_vendor);
200 }
201
202 if ( $geocode ) {
203   $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
204                 ' geocode LIKE '. dbh->quote($geocode.'%');
205 }
206
207 if ( $taxclassnum ) {
208   $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
209                 ' taxclassnum  = '. dbh->quote($taxclassnum);
210 }
211
212 if ( @taxclassnum ) {
213   $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
214                 join(' OR ', map { " taxclassnum  = $_ " } @taxclassnum );
215 }
216
217 my $count_query = "SELECT COUNT(*) FROM tax_rate $extra_sql";
218
219 $cell_style = '';
220
221 my @header        = ( 'Location Code',  );
222 my @header2       = ( '', );
223 my @links         = ( '', );
224 my @link_onclicks = ( '', );
225 my $align = 'l';
226
227 my @fields = (
228   'geocode',
229 );
230
231 my @color = (
232   '000000',
233 );
234
235 push @header, qq!Tax class (<A HREF="${p}edit/tax_class.html">add new</A>)!;
236 push @header2, '(per-tax classification)';
237 push @fields, 'taxclass_description';
238 push @color, '000000';
239 push @links, '';
240 push @link_onclicks, '';
241 $align .= 'l';
242
243 push @header, 'Tax name',
244               'Rate', #'Tax',
245               'Limits',
246               ;
247
248 push @header2, '(printed on invoices)',
249                '',
250                '',
251                ;
252
253 push @fields, 
254   sub { shift->taxname || 'Tax' },
255   $rate_sub,
256   $limit_sub,
257 ;
258
259 push @color,
260   sub { shift->taxname ? '000000' : '666666' },
261   sub { shift->tax     ? '000000' : '666666' },
262   '000000',
263 ;
264
265 $align .= 'lrl';
266
267 my @cell_style = map $cell_style_sub, (1..scalar(@header));
268
269 push @links,         '', $select_link,    '';
270 push @link_onclicks, '', $select_onclick, '';
271
272 my $html_init = '';
273
274 $html_init .= qq(
275   <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws.js"></SCRIPT>
276   <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_iframe.js"></SCRIPT>
277   <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_draggable.js"></SCRIPT>
278   <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/iframecontentmws.js"></SCRIPT>
279
280 );
281
282 $html_init .= qq(
283   <FORM>
284     <TABLE>
285       <TR>
286         <TD><SELECT NAME="data_vendor" onChange="this.form.submit()">
287 );
288
289 my $sql = "SELECT DISTINCT data_vendor FROM tax_rate ORDER BY data_vendor";
290 my $dbh = dbh;
291 my $sth = $dbh->prepare($sql) or die $dbh->errstr;
292 $sth->execute or die $sth->errstr;
293 for (['(choose data vendor)'], @{$sth->fetchall_arrayref}) {
294   $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
295                 ($_->[0] eq $data_vendor ? " SELECTED" : "").
296                 '">'.  $_->[0];
297 }
298 $html_init .= qq(
299         </SELECT>
300
301         <TD><INPUT NAME="geocode" TYPE="text" SIZE="12" VALUE="$geocode"></TD>
302
303 <!-- generic
304         <TD><INPUT NAME="taxclassnum" TYPE="text" SIZE="12" VALUE="$taxclassnum"></TD>
305         <TD><INPUT TYPE="submit" VALUE="Filter by tax_class"></TD>
306 -->
307
308 <!-- cch specific -->
309         <TD><SELECT NAME="tax_type" onChange="this.form.submit()">
310 );
311
312 $sql = "SELECT DISTINCT ".
313        "substring(taxclass from 1 for position(':' in taxclass)-1),".
314        "substring(description from 1 for position(':' in description)-1) ".
315        "FROM tax_class WHERE data_vendor='cch' ORDER BY 2";
316
317 $sth = $dbh->prepare($sql) or die $dbh->errstr;
318 $sth->execute or die $sth->errstr;
319 for (['', '(choose tax type)'], @{$sth->fetchall_arrayref}) {
320   $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
321                  ($_->[0] eq $tax_type ? " SELECTED" : "").
322                  '">'. $_->[1];
323 }
324
325 $html_init .= qq(
326         </SELECT>
327
328         <TD><SELECT NAME="tax_cat" onChange="this.form.submit()">
329 );
330
331 $sql = "SELECT DISTINCT ".
332        "substring(taxclass from position(':' in taxclass)+1),".
333        "substring(description from position(':' in description)+1) ".
334        "from tax_class WHERE data_vendor='cch' ORDER BY 2";
335
336 $sth = $dbh->prepare($sql) or die $dbh->errstr;
337 $sth->execute or die $sth->errstr;
338 for (['', '(choose tax category)'], @{$sth->fetchall_arrayref}) {
339   $html_init .= '<OPTION VALUE="'. $_->[0]. '"'.
340                  ($_->[0] eq $tax_cat ? " SELECTED" : "").
341                  '">'.  $_->[1];
342 }
343
344 $html_init .= qq(
345         </SELECT>
346
347       </TR>
348       <TR>
349         <TD></TD>
350         <TD><INPUT TYPE="submit" VALUE="Filter by geocode"></TD>
351         <TD></TD>
352         <TD></TD>
353       </TR>
354     </TABLE>
355   </FORM>
356
357 );
358
359 </%init>