c6484cacd589fb0aaa752c9b26d29c0ff91b6bd7
[freeside.git] / httemplate / browse / cust_main_county.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      'html_posttotal' => $html_posttotal,
7      'html_form'      => '<FORM NAME="taxesForm">',
8      'html_foot'      => $html_foot,
9      'query'          => {
10                            'table'    => 'cust_main_county',
11                            'hashref'  => $hashref,
12                            'order_by' =>
13                              'ORDER BY country, state, county, city, taxclass',
14                          },
15      'count_query'    => $count_query,
16      'header'         => \@header,
17      'header2'        => \@header2,
18      'fields'         => \@fields,
19      'align'          => $align,
20      'color'          => \@color,
21      'cell_style'     => \@cell_style,
22      'links'          => \@links,
23      'link_onclicks'  => \@link_onclicks,
24   )
25 %>
26 <%once>
27
28 my $conf = new FS::Conf;
29 my $money_char = $conf->config('money_char') || '$';
30
31 my $exempt_sub = sub {
32   my $cust_main_county = shift;
33
34   my @exempt = ();
35   push @exempt,
36        sprintf("$money_char%.2f&nbsp;per&nbsp;month", $cust_main_county->exempt_amount )
37     if $cust_main_county->exempt_amount > 0;
38
39   push @exempt, 'Setup&nbsp;fee'
40     if $cust_main_county->setuptax =~ /^Y$/i;
41
42   push @exempt, 'Recurring&nbsp;fee'
43     if $cust_main_county->recurtax =~ /^Y$/i;
44
45   [ map [ {'data'=>$_} ], @exempt ];
46 };
47
48 my $cs_oldrow;
49 my $cell_style = '';
50 my $cell_style_sub = sub {
51   my $row = shift;
52   if ( $cs_oldrow ne $row ) {
53     if ( $cs_oldrow ) {
54       if ( $cs_oldrow->country ne $row->country ) {
55         $cell_style = 'border-top:2px solid #000000';
56       } elsif ( $cs_oldrow->state ne $row->state ) {
57         $cell_style = 'border-top:1px solid #888888';
58       } elsif ( $cs_oldrow->county ne $row->county ) {
59         $cell_style = 'border-top:1px solid #cccccc';
60       } else { 
61         $cell_style = '';
62       }
63     }
64     $cs_oldrow = $row;
65   }
66   return $cell_style;
67 };
68
69 #my $edit_link = [ "${p}edit/cust_main_county.html", 'taxnum' ];
70 my $edit_link = [ 'javascript:void(0);', sub { ''; } ];
71
72 my $edit_onclick = sub {
73   my $row = shift;
74   my $taxnum = $row->taxnum;
75   include( '/elements/popup_link_onclick.html',
76              'action'      => "${p}edit/cust_main_county.html?$taxnum",
77              'actionlabel' => 'Edit tax rate',
78              'height'      => 420,
79              #default# 'width'  => 540,
80              #default# 'color' => '#333399',
81          );
82 };
83
84 my $ex_oldrow;
85 sub expand_link {
86   my %param = @_;
87
88   if ( $ex_oldrow eq $param{'row'} ) {
89     return '';
90   } else {
91     $ex_oldrow = $param{'row'};
92   }
93
94   my $taxnum = $param{'row'}->taxnum;
95   my $url = "${p}edit/cust_main_county-expand.cgi?$taxnum";
96
97   '<FONT SIZE="-1">'.
98     include( '/elements/popup_link.html',
99                'label'       => $param{'label'},
100                'action'      => $url,
101                'actionlabel' => $param{'desc'},
102                'height'      => 420,
103                #default# 'width'  => 540,
104                #default# 'color' => '#333399',
105            ).
106   '</FONT>';
107 }
108
109 sub add_link {
110   my %param = @_;
111
112   #if ( $ex_oldrow eq $param{'row'} ) {
113   #  return '';
114   #} else {
115   #  $ex_oldrow = $param{'row'};
116   #}
117
118   my %below = ( 'county' => 'city',
119                 'state'  => 'county',
120               );
121   my $what = $below{ $param{'col' } };
122
123   my $taxnum = $param{'row'}->taxnum;
124   my $url = "${p}edit/cust_main_county-add.cgi?taxnum=$taxnum;what=$what";
125
126   '<FONT SIZE="-1">'.
127     include( '/elements/popup_link.html',
128                'label'       => $param{'label'},
129                'action'      => $url,
130                'actionlabel' => $param{'desc'},
131                'height'      => 420,
132                #default# 'width'  => 540,
133                #default# 'color' => '#333399',
134            ).
135   '</FONT>';
136 }
137
138 sub collapse_link {
139   my %param = @_;
140
141   my $row = $param{'row'};
142   my $col = $param{'col'};
143 #  return ''
144 #    if $col eq 'state' and $row->city
145 #                            || qsearch({
146 #                                 'table'   => 'cust_main_county',
147 #                                 'hashref' => {
148 #                                   'country' => $row->country,
149 #                                   'state'   => $row->state,
150 #                                   'city'    => { op=>'!=', value=>'' },
151 #                                 },
152 #                                 'order_by' => 'LIMIT 1',
153 #                               });
154
155   my %below = ( 'county' => 'city',
156                 'state'  => 'county',
157               );
158
159   #XXX can still show the link when you have some counties broken down into
160   #cities and others not :/
161
162   my $taxnum = $param{'row'}->taxnum;
163   my $url = "${p}edit/process/cust_main_county-collapse.cgi?taxnum=$taxnum;".
164               'country='. uri_escape($cgi->param('country')). ';'.
165               'state='.   uri_escape($cgi->param('state')).   ';'.
166               'county='.  uri_escape($cgi->param('county'));
167   $url = "javascript:collapse_areyousure('$url', '$below{$col}', '$col')";
168
169   qq(<FONT SIZE="-1"><A HREF="$url">$param{'label'}</A></FONT>);
170 }
171
172 sub remove_link {
173   my %param = @_;
174
175   my $row = $param{'row'};
176   my $col = $param{'col'};
177  
178   my $taxnum = $param{'row'}->taxnum;
179   my $url = "${p}edit/process/cust_main_county-remove.cgi?taxnum=$taxnum;".
180               'country='. uri_escape($cgi->param('country')). ';'.
181               'state='.   uri_escape($cgi->param('state')).   ';'.
182               'county='.  uri_escape($cgi->param('county'));
183   $url = "javascript:remove_areyousure('$url', '$col')";
184
185   qq(<FONT SIZE="-1"><A HREF="$url">$param{'label'}</A></FONT>);
186
187 }
188
189 sub separate_taxclasses_link {
190   my( $row ) = @_;
191   my $taxnum = $row->taxnum;
192   my $url = "${p}edit/process/cust_main_county-expand.cgi?taxclass=1;taxnum=$taxnum";
193
194   qq!<FONT SIZE="-1"><A HREF="$url">!;
195 }
196
197 #un-separate taxclasses too
198
199 </%once>
200 <%init>
201
202 die "access denied"
203   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
204
205 #my $conf = new FS::Conf;
206 #my $money_char = $conf->config('money_char') || '$';
207 my $enable_taxclasses = $conf->exists('enable_taxclasses');
208
209 my @menubar;
210
211 my $html_init = <<END;
212   <SCRIPT>
213     function collapse_areyousure(href,col,above) {
214      if (confirm('Are you sure you want to remove all ' + col + ' tax rates for this ' + above + '?') == true)
215        window.location.href = href;
216     }
217     function remove_areyousure(href,col) {
218      if (confirm('Are you sure you want to remove this ' + col + '?') == true)
219        window.location.href = href;
220     }
221   </SCRIPT>
222 END
223
224 $html_init .= "<BR>Click on <u>separate taxclasses</u> to specify taxes per taxclass."
225   if $enable_taxclasses;
226 $html_init .= '<BR><BR>';
227
228 $html_init .= include('/elements/init_overlib.html');
229
230 my $title = '';
231
232 my $country = '';
233 if ( $cgi->param('country') =~ /^(\w\w)$/ ) {
234   $country = $1;
235   $title = $country;
236 }
237 $cgi->delete('country');
238
239 my $state = '';
240 if ( $country && $cgi->param('state') =~ /^([\w \-\'\[\]]+)$/ ) {
241   $state = $1;
242   $title = "$state, $title";
243 }
244 $cgi->delete('state');
245
246 my $county = '';
247 if ( $country && $state &&
248      $cgi->param('county') =~
249        /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)$/
250    )
251 {
252   $county = $1;
253   if ( $county eq '__NONE__' ) {
254     $title = "No county, $title";
255   } else {
256     $title = "$county county, $title";
257   }
258 }
259 $cgi->delete('county');
260
261 $title = " for $title" if $title;
262
263 my $taxclass = '';
264 if ( $cgi->param('taxclass') =~ /^([\w \-]+)$/ ) {
265   $taxclass = $1;
266   $title .= " for $taxclass tax class";
267 }
268 $cgi->delete('taxclass');
269
270 if ( $country || $taxclass ) {
271   push @menubar, 'View all tax rates' => $p.'browse/cust_main_county.cgi';
272 }
273
274 $cgi->param('dummy', 1);
275
276 my $filter_change =
277   "window.location = '". $cgi->self_url.
278   ";country=' + encodeURIComponent( document.getElementById('country').options[document.getElementById('country').selectedIndex].value ) + ".
279   "';state='   + encodeURIComponent( document.getElementById('state').options[document.getElementById('state').selectedIndex].value ) +".
280   "';county='  + encodeURIComponent( document.getElementById('county').options[document.getElementById('county').selectedIndex].value );";
281
282 #restore this so pagination works
283 $cgi->param('country',  $country) if $country;
284 $cgi->param('state',    $state  ) if $state;
285 $cgi->param('county',   $county ) if $county;
286 $cgi->param('taxclass', $county ) if $taxclass;
287
288 my $html_posttotal =
289   '<BR>( show country: '.
290   include('/elements/select-country.html',
291             'country'             => $country,
292             'onchange'            => $filter_change,
293             'empty_label'         => '(all)',
294             'disable_empty'       => 0,
295             'disable_stateupdate' => 1,
296          );
297
298 my %states_hash = $country ? states_hash($country) : ();
299 if ( scalar(keys(%states_hash)) > 1 ) {
300   $html_posttotal .=
301     ' show state: '.
302     include('/elements/select-state.html',
303               'country'              => $country,
304               'state'                => $state,
305               'onchange'             => $filter_change,
306               'empty_label'          => '(all)',
307               'disable_empty'        => 0,
308               'disable_countyupdate' => 1,
309            );
310 } else {
311   $html_posttotal .=
312     '<SELECT NAME="state" ID="state" STYLE="display:none">'.
313     '  <OPTION VALUE="" SELECTED>'.
314     '</SELECT>';
315 }
316
317 my @counties = ( $country && $state ) ? counties($state, $country) : ();
318 if ( scalar(@counties) > 1 ) {
319   $html_posttotal .=
320     ' show county: '.
321     include('/elements/select-county.html',
322               'country'              => $country,
323               'state'                => $state,
324               'county'               => $county,
325               'onchange'             => $filter_change,
326               'empty_label'          => '(all)',
327               'empty_data_label'     => '(none)',
328               'empty_data_value'     => '__NONE__',
329               'disable_empty'        => 0,
330               'disable_cityupdate'   => 1,
331            );
332 } else {
333   $html_posttotal .=
334     '<SELECT NAME="county" ID="county" STYLE="display:none">'.
335     '  <OPTION VALUE="" SELECTED>'.
336     '</SELECT>';
337 }
338
339 $html_posttotal .= ' )';
340
341 my $bulk_popup_link = 
342   include( '/elements/popup_link_onclick.html',
343              'action'      => "${p}edit/bulk-cust_main_county.html?taxnum=MAGIC_taxnum_MAGIC",
344              'actionlabel' => 'Bulk add new tax',
345              'nofalse'     => 1,
346              'height'      => 420,
347              #default# 'width'  => 540,
348              #default# 'color' => '#333399',
349          );
350
351 my $html_foot = <<END;
352 <SCRIPT TYPE="text/javascript">
353
354   function setAll(setTo) {
355     theForm = document.taxesForm;
356     for (i=0,n=theForm.elements.length;i<n;i++) {
357       if (theForm.elements[i].name.indexOf("cust_main_county") != -1) {
358         theForm.elements[i].checked = setTo;
359       }
360     }
361   }
362
363   function toggleAll() {
364     theForm = document.taxesForm;
365     for (i=0,n=theForm.elements.length;i<n;i++) {
366       if (theForm.elements[i].name.indexOf("cust_main_county") != -1) {
367         if ( theForm.elements[i].checked == true ) {
368           theForm.elements[i].checked = false;
369         } else {
370           theForm.elements[i].checked = true;
371         }
372       }
373     }
374   }
375
376   function bulkPopup(action) {
377     var bulk_popup_link = "$bulk_popup_link";
378     var bulkstring = '';
379     theForm = document.taxesForm;
380     for (i=0,n=theForm.elements.length;i<n;i++) {
381       if (    theForm.elements[i].name.indexOf("cust_main_county") != -1
382            && theForm.elements[i].checked == true
383          ) {
384         var name = theForm.elements[i].name;
385         var taxnum = name.replace(/cust_main_county/, '');
386         if ( bulkstring != '' ) {
387           bulkstring = bulkstring + ',';
388         }
389         bulkstring = bulkstring + taxnum;
390        
391       }
392     }
393     bulkstring = bulkstring + ';action=' + action;
394     if ( bulk_popup_link.length > 1920 ) { // IE 2083 URL limit
395       alert('Too many selections'); // should do some session thing...
396       return false;
397     }
398     bulk_popup_link = bulk_popup_link.replace(/MAGIC_taxnum_MAGIC/, bulkstring);
399     eval(bulk_popup_link);
400   }
401
402 </SCRIPT>
403
404 <BR>
405 <A HREF="javascript:setAll(true)">select all</A> |
406 <A HREF="javascript:setAll(false)">unselect all</A> |
407 <A HREF="javascript:toggleAll()">toggle all</A>
408 <BR><BR>
409 <A HREF="javascript:void(0);" onClick="bulkPopup('add');">Add new tax to selected</A>
410 |
411 <A HREF="javascript:void(0);" onClick="bulkPopup('edit');">Bulk edit selected</A>
412
413 END
414
415 my $hashref = {};
416 my $count_query = 'SELECT COUNT(*) FROM cust_main_county';
417 if ( $country ) {
418   $hashref->{'country'} = $country;
419   $count_query .= ' WHERE country = '. dbh->quote($country);
420 }
421 if ( $state ) {
422   $hashref->{'state'} = $state;
423   $count_query .= ' AND state   = '. dbh->quote($state);
424 }
425 if ( $county ) {
426   if ( $county eq '__NONE__' ) {
427     $hashref->{'county'} = '';
428     $count_query .= " AND ( county = '' OR county IS NULL ) ";
429   } else {
430     $hashref->{'county'} = $county;
431     $count_query .= ' AND county  = '. dbh->quote($county);
432   }
433 }
434 if ( $taxclass ) {
435   $hashref->{'taxclass'} = $taxclass;
436   $count_query .= ( $count_query =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
437                   ' taxclass  = '. dbh->quote($taxclass);
438 }
439
440
441 $cell_style = '';
442
443 my @header        = ( 'Country', 'State/Province', 'County', 'City' );
444 my @header2       = ( '', '', '', '', );
445 my @links         = ( '', '', '', '', );
446 my @link_onclicks = ( '', '', '', '', );
447 my $align = 'llll';
448
449 my %seen_country = ();
450 my %seen_state = ();
451 my %seen_county = ();
452
453 my @fields = (
454   sub { my $country = shift->country;
455         return '' if $seen_country{$country}++;
456         code2country($country). "&nbsp;($country)";
457       },
458
459   #state
460   sub { my $label = $seen_state{$_[0]->country}->{$_[0]->state}++
461                       ? '' : state_label($_[0]->state, $_[0]->country);
462
463         my $countylinks = ( $_[0]->county && $label )
464                              ? '&nbsp;'. add_link(
465                                  desc => 'Add more counties',
466                                  col  => 'state',
467                                  label=> 'add&nbsp;more&nbsp;counties',
468                                  row  => $_[0],
469                                  cgi  => $cgi,
470                                ).
471                                ' '. collapse_link(
472                                  col  => 'state',
473                                  label=> 'remove&nbsp;all&nbsp;counties',
474                                  row  => $_[0],
475                                  cgi  => $cgi,
476                                )
477                              : '';
478
479         my $addlink = 
480           ( $_[0]->state
481               ? ''
482               : '&nbsp;'. expand_link( desc  => 'Add States',
483                                        row   => $_[0],
484                                        label => 'add&nbsp;states',
485                                        cgi  => $cgi,
486                                      )
487           );
488
489         $label.$countylinks.$addlink;
490       },
491
492   #county
493   sub { my $label =
494           $seen_county{$_[0]->country}->{$_[0]->state}->{$_[0]->county}++ 
495             ? '' : $_[0]->county;
496
497         my $citylinks = '';
498         if ( $label ) {
499           $citylinks = $_[0]->city
500                        ? '&nbsp;'. add_link(
501                            desc => 'Add more cities',
502                            col  => 'county',
503                            label=> 'add&nbsp;more&nbsp;cities',
504                            row  => $_[0],
505                            cgi  => $cgi,
506                          ).
507                          ' '. collapse_link(
508                            col  => 'county',
509                            label=> 'remove&nbsp;all&nbsp;cities',
510                            row  => $_[0],
511                            cgi  => $cgi,
512                          )
513                        : '&nbsp;'. remove_link( col  => 'county',
514                                                 label=> 'remove&nbsp;county',
515                                                 row  => $_[0],
516                                                 cgi  => $cgi,
517                                               );
518         }
519
520         $_[0]->county
521           ? $label.$citylinks
522           : '(all)&nbsp;'.
523               expand_link(   desc  => 'Add Counties',
524                              row   => $_[0],
525                              label => 'add&nbsp;counties',
526                              cgi  => $cgi,
527                          );
528       },
529
530   #city
531   sub {
532         my $r = shift;
533         if ( $r->city ) {
534
535           if ( $r->taxclass ) { #but if it has a taxclass, can't remove
536             $r->city;
537           } else {
538             $r->city. '&nbsp;'.
539               remove_link( col  => 'city',
540                            label=> 'remove&nbsp;city',
541                            row  => $r,
542                            cgi  => $cgi,
543                          );
544           }
545         } else {
546           '(all)&nbsp;'.
547             expand_link(   desc  => 'Add Cities',
548                            row   => $r,
549                            label => 'add&nbsp;cities',
550                            cgi  => $cgi,
551                        );
552         }
553       },
554 );
555
556 my @color = (
557   '000000',
558   sub { shift->state  ? '000000' : '999999' },
559   sub { shift->county ? '000000' : '999999' },
560   sub { shift->city   ? '000000' : '999999' },
561 );
562
563 if ( $conf->exists('enable_taxclasses') ) {
564   push @header, qq!Tax class (<A HREF="${p}edit/part_pkg_taxclass.html">add new</A>)!;
565   push @header2, '(per-package classification)';
566   push @fields, sub {
567     my $r = shift;
568     if ( $r->taxclass ) {
569       $r->taxclass;
570     } else {
571       my $sql = 'SELECT COUNT(*) FROM cust_main_county
572                    WHERE country = ? AND state = ? AND county = ?
573                      AND city = ? AND taxclass IS NOT NULL';
574       if ( FS::Record->scalar_sql($sql, map $r->$_,
575                                             qw( country state county city) ) ) {
576         '(none)';
577       } else {
578         '(all)&nbsp;'.
579           separate_taxclasses_link($r, 'Separate Taxclasses').
580           'separate&nbsp;taxclasses</A></FONT>';
581       }
582     }
583   };
584   push @color, sub { shift->taxclass ? '000000' : '999999' };
585   push @links, '';
586   push @link_onclicks, '';
587   $align .= 'l';
588 }
589
590 push @header,
591               '', #checkbox column
592               'Tax name',
593               'Rate', #'Tax',
594               'Exemptions',
595               ;
596
597 push @header2,
598                '',
599                '(printed on invoices)',
600                '',
601                '',
602                ;
603
604 my $newregion = 1;
605 my $cb_oldrow = '';
606 my $cb_sub = sub {
607   my $cust_main_county = shift;
608
609   if ( $cb_oldrow ) {
610     if (    $cb_oldrow->city     ne $cust_main_county->city 
611          || $cb_oldrow->county   ne $cust_main_county->county  
612          || $cb_oldrow->state    ne $cust_main_county->state  
613          || $cb_oldrow->country  ne $cust_main_county->country 
614          || $cb_oldrow->taxclass ne $cust_main_county->taxclass )
615     {
616       $newregion = 1;
617     } else {
618       $newregion = 0;
619     }  
620     
621   } else {
622     $newregion = 1;
623   }
624   $cb_oldrow = $cust_main_county;
625
626   if ( $newregion ) {
627     my $taxnum = $cust_main_county->taxnum;
628     qq!<INPUT NAME="cust_main_county$taxnum" TYPE="checkbox" VALUE="1">!;
629   } else {
630     '';
631   }
632 };
633
634 push @fields, 
635   $cb_sub,
636   sub { shift->taxname || 'Tax' },
637   sub { shift->tax. '%&nbsp;<FONT SIZE="-1">(edit)</FONT>' },
638   $exempt_sub,
639 ;
640
641 push @color,
642   '000000',
643   sub { shift->taxname ? '000000' : '666666' },
644   sub { shift->tax     ? '000000' : '666666' },
645   '000000',
646 ;
647
648 $align .= 'clrl';
649
650 my @cell_style = map $cell_style_sub, (1..scalar(@header));
651
652 push @links,         '', '', $edit_link,    '';
653 push @link_onclicks, '', '', $edit_onclick, '';
654
655 </%init>