summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rwxr-xr-xhttemplate/browse/tax_rate.cgi205
-rwxr-xr-xhttemplate/edit/part_pkg.cgi42
-rw-r--r--httemplate/edit/part_pkg_taxoverride.html61
-rw-r--r--httemplate/edit/part_pkg_taxproduct.html42
-rwxr-xr-xhttemplate/edit/process/part_pkg.cgi13
-rw-r--r--httemplate/edit/process/tax_class.html49
-rw-r--r--httemplate/edit/process/tax_rate.html13
-rw-r--r--httemplate/edit/tax_class.html36
-rw-r--r--httemplate/edit/tax_rate.html105
-rw-r--r--httemplate/elements/menu.html1
-rwxr-xr-xhttemplate/misc/process/recharge_svc.new85
-rw-r--r--httemplate/misc/process/tax-import.cgi58
-rw-r--r--httemplate/misc/tax-import.cgi91
13 files changed, 799 insertions, 2 deletions
diff --git a/httemplate/browse/tax_rate.cgi b/httemplate/browse/tax_rate.cgi
new file mode 100755
index 000000000..b401b3786
--- /dev/null
+++ b/httemplate/browse/tax_rate.cgi
@@ -0,0 +1,205 @@
+<% include( 'elements/browse.html',
+ 'title' => "Tax Rates $title",
+ 'name_singular' => 'tax rate',
+ 'menubar' => \@menubar,
+ 'html_init' => $html_init,
+ 'query' => {
+ 'table' => 'tax_rate',
+ 'hashref' => $hashref,
+ 'order_by' => 'ORDER BY geocode, taxclassnum',
+ },
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'header2' => \@header2,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'color' => \@color,
+ 'cell_style' => \@cell_style,
+ 'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $exempt_sub = sub {
+ my $tax_rate = shift;
+
+ my @exempt = ();
+ push @exempt,
+ sprintf("$money_char%.2f&nbsp;per&nbsp;month", $tax_rate->exempt_amount )
+ if $tax_rate->exempt_amount > 0;
+
+ push @exempt, 'Setup&nbsp;fee'
+ if $tax_rate->setuptax =~ /^Y$/i;
+
+ push @exempt, 'Recurring&nbsp;fee'
+ if $tax_rate->recurtax =~ /^Y$/i;
+
+ [ map [ {'data'=>$_} ], @exempt ];
+};
+
+my $oldrow;
+my $cell_style;
+my $cell_style_sub = sub {
+ my $row = shift;
+ if ( $oldrow ne $row ) {
+ if ( $oldrow ) {
+ if ( $oldrow->country ne $row->country ) {
+ $cell_style = 'border-top:1px solid #000000';
+ } elsif ( $oldrow->state ne $row->state ) {
+ $cell_style = 'border-top:1px solid #cccccc'; #default?
+ } elsif ( $oldrow->state eq $row->state ) {
+ #$cell_style = 'border-top:dashed 1px dark gray';
+ $cell_style = 'border-top:1px dashed #cccccc';
+ }
+ }
+ $oldrow = $row;
+ }
+ return $cell_style;
+};
+
+my $select_link = [ 'javascript:void(0);', sub { ''; } ];
+
+my $select_onclick = sub {
+ my $row = shift;
+ my $taxnum = $row->taxnum;
+ my $color = '#333399';
+ qq!overlib( OLiframeContent('${p}edit/tax_rate.html?$taxnum', 540, 420, 'edit_tax_rate_popup' ), CAPTION, 'Edit tax rate', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, BGCOLOR, '$color', CGCOLOR, '$color' ); return false;!;
+};
+
+my $separate_taxclasses_link = sub {
+ my( $row ) = @_;
+ my $taxnum = $row->taxnum;
+ my $url = "${p}edit/process/tax_rate-expand.cgi?taxclassnum=1;taxnum=$taxnum";
+
+ qq!<FONT SIZE="-1"><A HREF="$url">!;
+};
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my @menubar;
+
+my $html_init =
+ "Click on <u>geocodes</u> to specify rates for a new area.";
+$html_init .= "<BR>Click on <u>separate taxclasses</u> to specify taxes per taxclass.";
+$html_init .= '<BR><BR>';
+
+$html_init .= qq(
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_iframe.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/overlibmws_draggable.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="${fsurl}elements/iframecontentmws.js"></SCRIPT>
+);
+
+my $title = '';
+my $select_word = 'edit';
+
+my $geocode = '';
+if ( $cgi->param('geocode') =~ /^(\w+)$/ ) {
+ $geocode = $1;
+ $title = "$geocode";
+}
+$cgi->delete('geocode');
+
+$title = " for $title" if $title;
+
+my $taxclassnum = '';
+if ( $cgi->param('taxclassnum') =~ /^(\d+)$/ ) {
+ $taxclassnum = $1;
+ my $tax_class = qsearchs('tax_class', {'taxclassnum' => $taxclassnum});
+ if ($tax_class) {
+ $title .= " for ". $tax_class->taxclass.
+ " (". $tax_class->description. ") tax class";
+ }else{
+ $taxclassnum = '';
+ }
+}
+$cgi->delete('taxclassnum');
+
+if ( $geocode || $taxclassnum ) {
+ push @menubar, 'View all tax rates' => $p.'browse/tax_rate.cgi';
+}
+
+$cgi->param('dummy', 1);
+
+#restore this so pagination works
+$cgi->param('geocode', $geocode) if $geocode;
+$cgi->param('taxclassnum', $taxclassnum ) if $taxclassnum;
+
+my $hashref = {};
+my $count_query = 'SELECT COUNT(*) FROM tax_rate';
+if ( $geocode ) {
+ $hashref->{'geocode'} = $geocode;
+ $count_query .= ' WHERE geocode = '. dbh->quote($geocode);
+}
+if ( $taxclassnum ) {
+ $hashref->{'taxclassnum'} = $taxclassnum;
+ $count_query .= ( $count_query =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ ' taxclassnum = '. dbh->quote($taxclassnum);
+}
+
+
+$cell_style = '';
+
+my @header = ( 'Location Code', );
+my @header2 = ( '', );
+my @links = ( '', );
+my @link_onclicks = ( '', );
+my $align = 'l';
+
+my @fields = (
+ 'geocode',
+);
+
+my @color = (
+ '000000',
+);
+
+push @header, qq!Tax class (<A HREF="${p}edit/tax_class.html">add new</A>)!;
+push @header2, '(per-tax classification)';
+push @fields, sub { $_[0]->taxclass_description || '(all)&nbsp'.
+ &{$separate_taxclasses_link}($_[0], 'Separate Taxclasses').
+ 'separate&nbsp;taxclasses</A></FONT>'
+ };
+push @color, sub { shift->taxclass ? '000000' : '999999' };
+push @links, '';
+push @link_onclicks, '';
+$align .= 'l';
+
+push @header, 'Tax name',
+ 'Rate', #'Tax',
+ 'Exemptions',
+ ;
+
+push @header2, '(printed on invoices)',
+ '',
+ '',
+ ;
+
+push @fields,
+ sub { shift->taxname || 'Tax' },
+ sub { shift->tax. '%&nbsp;<FONT SIZE="-1">('. $select_word. ')</FONT>' },
+ $exempt_sub,
+;
+
+push @color,
+ sub { shift->taxname ? '000000' : '666666' },
+ sub { shift->tax ? '000000' : '666666' },
+ '000000',
+;
+
+$align .= 'lrl';
+
+my @cell_style = map $cell_style_sub, (1..scalar(@header));
+
+push @links, '', $select_link, '';
+push @link_onclicks, '', $select_onclick, '';
+
+</%init>
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
index b1851a7ba..84f7498e1 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -3,6 +3,11 @@
)) %>
% #), ' onLoad="visualize()"');
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/overlibmws.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/overlibmws_iframe.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/overlibmws_draggable.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/iframecontentmws.js"></SCRIPT>
+
<% include('/elements/error.html') %>
<FORM NAME="dummy">
@@ -81,6 +86,34 @@ Tax information
% }
+% if ( $conf->exists('enable_taxproducts') ) {
+
+ <TR><TD colspan="2">
+ <% ntable("#cccccc", 2) %>
+ <TR>
+ <TD align="right">Tax product</TD>
+ <TD>
+ <INPUT name="part_pkg_taxproduct_taxproductnum" id="taxproductnum" type="hidden" value="<% $hashref->{'taxproductnum'}%>">
+ <INPUT name="part_pkg_taxproduct_description" id="taxproduct_description" type="text" value="<% $taxproduct_description %>" size="12" onclick="overlib( OLiframeContent('part_pkg_taxproduct.html?'+document.getElementById('taxproductnum').value, 800, 400, 'tax_product_popup'), CAPTION, 'Select product', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK); return false;">
+ </TD>
+ </TR>
+ <TR>
+ <TD colspan="2" align="right">
+ <INPUT name="tax_override" id="tax_override" type="hidden" value="<% $tax_override %>">
+ <A href="javascript:void(0)" onclick="overlib( OLiframeContent('part_pkg_taxoverride.html?'+document.getElementById('tax_override').value, 800, 400, 'tax_product_popup'), CAPTION, 'Edit product tax overrides', STICKY, AUTOSTATUSCAP, MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK); return false;">
+ <% $tax_override ? 'Edit tax overrides' : 'Override taxes' %>
+ </A>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD></TR>
+
+% } else {
+
+ <INPUT TYPE="hidden" NAME="taxproductnum" VALUE="<% $hashref->{taxproductnum} %>">
+
+% }
+
</TABLE>
<BR>
@@ -234,7 +267,7 @@ Line-item revenue recognition
%#} else {
%# push @fixups, 'taxclass'; #hidden
%#}
-%my @form_elements = ( 'classnum', 'taxclass', 'agent_type' );
+%my @form_elements = ( 'classnum', 'taxclass', 'agent_type', 'tax_override' );
%
%my @form_radio = ( 'pkg_svc_primary' );
%
@@ -252,6 +285,7 @@ Line-item revenue recognition
% 'form_elements' => \@form_elements,
% 'form_text' => [ qw(pkg comment promo_code clone pkgnum pkgpart),
% qw(pay_weight credit_weight), #keys(%weight),
+% qw(taxproductnum),
% @fixups,
% ],
% 'form_checkbox' => [ qw(setuptax recurtax disabled) ],
@@ -409,12 +443,14 @@ my ($query) = $cgi->keywords;
my $conf = new FS::Conf;
my $part_pkg = '';
my @agent_type = ();
+my $tax_override;
my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
if ( $cgi->param('error') ) {
$part_pkg = new FS::part_pkg ( {
map { $_, scalar($cgi->param($_)) } fields('part_pkg')
} );
(@agent_type) = $cgi->param('agent_type');
+ $tax_override = $cgi->param('tax_override');
}
my $action = '';
@@ -430,6 +466,9 @@ if ( $cgi->param('clone') ) {
} elsif ( $query && $query =~ /^(\d+)$/ ) {
(@agent_type) = map {$_->typenum} qsearch('type_pkgs',{'pkgpart'=>$1})
unless $part_pkg;
+ $tax_override =
+ join (",", map {$_->taxnum} qsearch('part_pkg_taxoverride',{'pkgpart'=>$1}))
+ unless $part_pkg;
$part_pkg ||= qsearchs('part_pkg',{'pkgpart'=>$1});
$pkgpart = $part_pkg->pkgpart;
} else {
@@ -447,5 +486,6 @@ unless ( $part_pkg->plan ) { #backwards-compat
}
$action ||= $part_pkg->pkgpart ? 'Edit' : 'Add';
my $hashref = $part_pkg->hashref;
+my $taxproduct_description = $part_pkg->taxproduct_description;
</%init>
diff --git a/httemplate/edit/part_pkg_taxoverride.html b/httemplate/edit/part_pkg_taxoverride.html
new file mode 100644
index 000000000..61cca1fbc
--- /dev/null
+++ b/httemplate/edit/part_pkg_taxoverride.html
@@ -0,0 +1,61 @@
+<%doc>
+
+ The crappy version
+
+</%doc>
+<% include('/elements/header-popup.html', 'Select tax product') %>
+<SCRIPT>
+ function saveit2() {
+ var num = parent.document.getElementById('tax_override');
+ var sel = document.getElementById('taxoverride_popup_select');
+ var value = '';
+ for (i=0; i< sel.length; i++) {
+ if (sel.options[i].selected) {
+ value = value + sel.options[i].value + ",";
+ }
+ }
+ if (value.length > 0) {
+ value = value.substr(0, value.length-1);
+ }
+
+ num.value = value;
+ parent.cClick();
+ }
+</SCRIPT>
+<FORM="dummy" METHOD="POST" onsubmit="saveit2();return false;" >
+
+<% ntable("#cccccc", 2) %>
+<TR>
+ <TD align="left">Tax override</TD>
+ <TD>
+ <% include( '/elements/select-table.html',
+ 'table' => 'tax_rate',
+ 'name_col' => 'taxname',
+ 'curr_value' => \@curr_value,
+ 'element_etc' => "id='taxoverride_popup_select'",
+ 'multiple' => '1',
+ )
+ %>
+ </TD>
+</TR>
+</TABLE>
+<BR><BR>
+<CENTER><INPUT type="submit" value="Select"></CENTER>
+</FORM>
+<% include('/elements/footer.html') %>
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my @curr_value;
+my ( $query ) = $cgi->keywords;
+$query =~ /^([\d,]+)$/;
+push @curr_value, split ',', $1
+ if $1;
+
+unless (scalar(@curr_value)) {
+ #push @curr_value, map {$_=>taxnum} $part_pkg->tax_rate;
+}
+
+</%init>
diff --git a/httemplate/edit/part_pkg_taxproduct.html b/httemplate/edit/part_pkg_taxproduct.html
new file mode 100644
index 000000000..033c37f56
--- /dev/null
+++ b/httemplate/edit/part_pkg_taxproduct.html
@@ -0,0 +1,42 @@
+<% include('/elements/header-popup.html', 'Select tax product') %>
+<SCRIPT>
+ function saveit() {
+ var num = parent.document.getElementById('taxproductnum');
+ var disp = parent.document.getElementById('taxproduct_description');
+ var sel = document.getElementById('taxproduct_popup_select');
+ num.value = sel.options[sel.selectedIndex].value;
+ disp.value = sel.options[sel.selectedIndex].text;
+ parent.cClick();
+ }
+</SCRIPT>
+<FORM="dummy" METHOD="POST" onsubmit="saveit();return false;" >
+
+<% ntable("#cccccc", 2) %>
+<TR>
+ <TD align="left">Tax product</TD>
+ <TD>
+ <% include( '/elements/select-table.html',
+ 'empty_label' => '(select product)',
+ 'table' => 'part_pkg_taxproduct',
+ 'name_col' => 'description',
+ 'curr_value' => $curr_value,
+ 'element_etc' => "id='taxproduct_popup_select'",
+ )
+ %>
+ </TD>
+</TR>
+</TABLE>
+<BR><BR>
+<CENTER><INPUT type="submit" value="Select"></CENTER>
+</FORM>
+<% include('/elements/footer.html') %>
+
+<%init>
+
+my $conf = new FS::Conf;
+
+my ( $query ) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $curr_value = $1;
+
+</%init>
diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi
index 36debfce0..eac20af57 100755
--- a/httemplate/edit/process/part_pkg.cgi
+++ b/httemplate/edit/process/part_pkg.cgi
@@ -53,6 +53,9 @@ $error = "At least one agent type must be specified."
!$pkgpart && $conf->exists('agent-defaultpkg')
);
+$cgi->param('tax_override') =~ /^([\d,]+)$/;
+my (@tax_overrides) = (grep "$_", split (",", $1));
+
my $new = new FS::part_pkg ( {
map {
$_ => scalar($cgi->param($_));
@@ -103,11 +106,19 @@ if ( $error ) {
}
unless ( $error || $conf->exists('agent_defaultpkg') ) {
- my $error = $new->process_m2m(
+ $error = $new->process_m2m(
'link_table' => 'type_pkgs',
'target_table' => 'agent_type',
'params' => \@agents,
);
}
+unless ( $error ) {
+ $error = $new->process_m2m(
+ 'link_table' => 'part_pkg_taxoverride',
+ 'target_table' => 'tax_rate',
+ 'params' => \@tax_overrides,
+ );
+}
+
</%init>
diff --git a/httemplate/edit/process/tax_class.html b/httemplate/edit/process/tax_class.html
new file mode 100644
index 000000000..339c9083e
--- /dev/null
+++ b/httemplate/edit/process/tax_class.html
@@ -0,0 +1,49 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(2). "tax_class.html?". $cgi->query_string ) %>
+%} else {
+<% $cgi->redirect(popurl(3). "browse/tax_rate.cgi?taxclassnum=". uri_escape($tax_class->taxclassnum) ) %>
+%}
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $tax_class = new FS::tax_class {
+ 'taxclass' => $cgi->param('taxclass'),
+ 'description' => $cgi->param('description'),
+};
+
+#maybe this whole thing should be in a transaction. at some point, no biggie
+#none of the follow-up stuff will fail unless there's a more serious problem
+#than a hanging record in tax_class...
+
+my $error = $tax_class->insert;
+
+# all of this is highly dubious at the moment
+
+#unless ( $error ) {
+# #auto-add the new taxclass to any regions that have taxclasses already
+#
+# my $sth = dbh->prepare("
+# SELECT geocode FROM tax_rate
+# WHERE taxclass IS NOT NULL AND taxclass != ''
+# GROUP BY geocode
+# ") or die dbh->errstr;
+# $sth->execute or die $sth->errstr;
+#
+# while ( my $row = $sth->fetchrow_hashref ) {
+# warn "inserting for $row";
+# my $cust_main_county = new FS::tax_rate {
+# 'geocode' => $row->{geocode},
+# 'tax' => 0,
+# 'taxclassnum' => $tax_class->taxclassnum,
+# };
+# $error = $cust_main_county->insert;
+# #last if $error;
+# die $error if $error;
+# }
+#
+#}
+
+</%init>
diff --git a/httemplate/edit/process/tax_rate.html b/httemplate/edit/process/tax_rate.html
new file mode 100644
index 000000000..933bf07d6
--- /dev/null
+++ b/httemplate/edit/process/tax_rate.html
@@ -0,0 +1,13 @@
+<% include( 'elements/process.html',
+ 'table' => 'tax_rate',
+ 'popup_reload' => 'Tax changed', #a popup "parent reload" for now
+ #someday change the individual element and go away instead
+ )
+%>
+<%init>
+
+my $conf = new FS::Conf;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/tax_class.html b/httemplate/edit/tax_class.html
new file mode 100644
index 000000000..d3e2e821f
--- /dev/null
+++ b/httemplate/edit/tax_class.html
@@ -0,0 +1,36 @@
+<% include('/elements/header.html', "$action taxclass") %>
+
+<% include('/elements/error.html') %>
+
+<FORM ACTION="<% $p1 %>process/tax_class.html" METHOD=POST>
+
+<INPUT TYPE="hidden" NAME="taxclassnum" VALUE="">
+<INPUT TYPE="hidden" NAME="data_vendor" VALUE="">
+
+Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |h %>"><BR>
+Description <INPUT TYPE="text" NAME="description" VALUE="<% $description |h %>">
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $action %> taxclass">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxclass = '';
+my $description = '';
+if ( $cgi->param('error') ) {
+ $taxclass = $cgi->param('taxclass');
+ $description = $cgi->param('description');
+}
+
+my $action = 'Add';
+
+my $p1 = popurl(1);
+
+</%init>
diff --git a/httemplate/edit/tax_rate.html b/httemplate/edit/tax_rate.html
new file mode 100644
index 000000000..e1d8d4f93
--- /dev/null
+++ b/httemplate/edit/tax_rate.html
@@ -0,0 +1,105 @@
+<% include('elements/edit.html',
+ 'popup' => 1,
+ 'name' => 'Tax rate', #Edit tax rate
+ 'table' => 'tax_rate',
+ 'labels' => $labels,
+ 'fields' => \@fields,
+ )
+%>
+<%once>
+
+my $conf = new FS::Conf;
+
+</%once>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $taxnum;
+if ( $cgi->param('error') ) {
+ $cgi->param('taxnum') =~ /^(\d+)$/ or die 'error, but no taxnum';
+ $taxnum = $1;
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die 'no taxnum';
+ $taxnum = $1;
+}
+
+my $tax_rate = qsearchs('tax_rate', { 'taxnum' => $taxnum })
+ or die "unknown taxnum $1";
+
+my $labels = { 'taxnum' => 'Tax',
+ 'data_vendor' => 'Data vendor',
+ 'geocode' => 'Vendor location code',
+ 'location' => 'Tax auth loc code',
+ 'taxclass_description' => 'Tax class',
+ 'taxname' => 'Tax name',
+ 'effective_date' => 'Effective date',
+ 'tax' => 'Tax rate (1st bracket)',
+ 'excessrate' => 'Tax rate (2nd bracket)',
+ 'taxbase' => 'First bracket',
+ 'taxmax' => 'Max tax',
+ 'usetax' => 'Use tax rate (1st bracket)',
+ 'useexcessrate' => 'Use tax rate (2nd bracket)',
+ 'unittype_name' => 'Units',
+ 'fee' => 'Fee per unit (1st bracket)',
+ 'excessfee' => 'Fee per unit (2st bracket)',
+ 'feebase' => 'Units in first bracket',
+ 'feemax' => 'Max Units',
+ 'maxtype_name' => 'Threshold accumulation',
+ 'taxauth_name', => 'Tax authority',
+ 'basetype_name' => 'Basis',
+ 'passtype_name' => 'Passthru',
+ 'passflag' => 'Passable',
+ 'setuptax' => 'This tax not applicable to setup fees',
+ 'recurtax' => 'This tax not applicable to recurring fees',
+ };
+
+my @fields = (
+ { type=>'tablebreak-tr-title', value=>'Location' },
+ { field=>'data_vendor', type=>'hidden',},
+ { field=>'geocode', type=>'fixed' },
+ { field=>'taxclassnum', type=>'hidden' } ,
+ { field=>'taxclass_description', type=>'fixed' } ,
+ { field=>'taxname', type=>'text' } ,
+ { field=>'effective_date', type=>'fixed' } ,
+ { field=>'location', type=>'text' },
+ { type=>'tablebreak-tr-title', value=>'Money based rates' },
+ { field=>'tax', type=>'percentage' } ,
+ { field=>'excessrate', type=>'percentage' } ,
+ { field=>'taxbase', type=>'money' } ,
+ { field=>'taxmax', type=>'money' } ,
+ { field=>'usetax', type=>'percentage' } ,
+ { field=>'useexcessrate', type=>'percentage' } ,
+ { type=>'tablebreak-tr-title', value=>'Service based rates' },
+ { field=>'unittype', type=>'hidden' } ,
+ { field=>'unittype_name', type=>'fixed' } ,
+ { field=>'fee', type=>'money' } ,
+ { field=>'excessfee', type=>'money' } ,
+ { field=>'feebase', type=>'text' } ,
+ { field=>'feemax', type=>'text' } ,
+ { type=>'tablebreak-tr-title', value=>'Taxation rules' },
+ { field=>'maxtype', type=>'hidden' } ,
+ { field=>'maxtype_name', type=>'fixed' } ,
+ { field=>'taxauth', type=>'hidden' } ,
+ { field=>'taxauth_name', type=>'fixed' } ,
+ { field=>'basetype', type=>'hidden' } ,
+ { field=>'basetype_name', type=>'fixed' } ,
+ { field=>'passtype', type=>'hidden' } ,
+ { field=>'passtype_name', type=>'fixed' } ,
+ { field=>'passflag', type=>'fixed' } ,
+ { field=>'setuptax', type=>'checkbox' } ,
+ { field=>'recurtax', type=>'checkbox' } ,
+ { field=>'manual', type=>'hidden', value=>'Y' } ,
+);
+
+#push @fields,
+# { type=>'tablebreak-tr-title', value=>'Exemptions' },
+# { field=>'setuptax', type=>'checkbox', value=>'Y', },
+# { field=>'recurtax', type=>'checkbox', value=>'Y', },
+# { field=>'exempt_amount', type=>'money', },
+#;
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 047671ae5..4e4d68719 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -217,6 +217,7 @@ $report_menu{'SQL Query'} = [ $fsurl.'search/report_sql.html', 'SQL Query' ]
if $curuser->access_right('Raw SQL');
tie my %tools_importing, 'Tie::IxHash',
+ 'Import tax rates from CSV files' => [ $fsurl.'misc/tax-import.cgi', '' ],
'Import customers from CSV file' => [ $fsurl.'misc/cust_main-import.cgi', '' ],
'Import payments from CSV file' => [ $fsurl.'misc/cust_pay-import.cgi', '' ],
diff --git a/httemplate/misc/process/recharge_svc.new b/httemplate/misc/process/recharge_svc.new
new file mode 100755
index 000000000..bc916e5da
--- /dev/null
+++ b/httemplate/misc/process/recharge_svc.new
@@ -0,0 +1,85 @@
+%
+%
+%#untaint svcnum
+%my $svcnum = $cgi->param('svcnum');
+%$svcnum =~ /^(\d+)$/ || die "Illegal svcnum";
+%$svcnum = $1;
+%
+%#untaint prepaid
+%my $prepaid = $cgi->param('prepaid');
+%$prepaid =~ /^(\w*)$/;
+%$prepaid = $1;
+
+%#untaint payby
+%my $payby = $cgi->param('payby');
+%$payby =~ /^([A-Z]*)$/;
+%$payby = $1;
+%
+%my $error = '';
+%my $svc_acct = qsearchs( 'svc_acct', {'svcnum'=>$svcnum} );
+%$error = "Can't recharge service $svcnum. " unless $svc_acct;
+%
+%my $cust_main = $svc_acct->cust_svc->cust_pkg->cust_main;
+%
+%my $oldAutoCommit = $FS::UID::AutoCommit;
+%local $FS::UID::AutoCommit = 0;
+%my $dbh = dbh;
+%
+%
+%unless ($error) {
+%
+% my ($amount, $seconds, $up, $down, $total) = (0, 0, 0, 0, 0);
+% #should probably use payby.pm but whatever
+% if ($payby eq 'PREP') {
+% $error = $cust_main->get_prepay($prepaid, \$amount, \$seconds, \$up, \$down, \$total)
+% || $svc_acct->increment_seconds($seconds)
+% || $svc_acct->increment_upbytes($up)
+% || $svc_acct->increment_downbytes($down)
+% || $svc_acct->increment_totalbytes($total)
+% || $cust_main->insert_cust_pay_prepay( $amount, $prepaid );
+% } elsif ( $payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP)$/ ) {
+% my $part_pkg = $svc_acct->cust_svc->cust_pkg->part_pkg;
+% $amount = $part_pkg->option('recharge_amount', 1);
+% my %rhash = map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_, 1) }
+% qw ( recharge_seconds recharge_upbytes recharge_downbytes
+% recharge_totalbytes );
+%
+% my $description = "Recharge";
+% $description .= " $rhash{seconds}s" if $rhash{seconds};
+% $description .= " $rhash{upbytes} up" if $rhash{upbytes};
+% $description .= " $rhash{downbytes} down" if $rhash{downbytes};
+% $description .= " $rhash{totalbytes} total" if $rhash{totalbytes};
+%
+% $error = $cust_main->charge($amount, "Recharge " . $svc_acct->label,
+% $description, $part_pkg->taxclass);
+%
+% $error ||= $svc_acct->recharge(\%rhash);
+%
+% my $old_balance = $cust_main->balance;
+% $error ||= $cust_main->bill;
+% $error ||= $cust_main->apply_payments_and_credits;
+% my $bill_error = $cust_main->collect('realtime' => 1) unless $error;
+% $error ||= "Failed to collect - $bill_error"
+% if $cust_main->balance > $old_balance && $cust_main->balance > 0
+% && $payby ne 'BILL';
+%
+% } else {
+% $error = "fatal error - unknown payby: $payby";
+% }
+%}
+%
+%if ($error) {
+% $cgi->param('error', $error);
+% $dbh->rollback if $oldAutoCommit;
+% print $cgi->redirect(popurl(2). "recharge_svc.html?". $cgi->query_string );
+%}
+%$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+%
+<% header("Package recharged") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+<%init>
+my $conf = new FS::Conf;
+</%init>
diff --git a/httemplate/misc/process/tax-import.cgi b/httemplate/misc/process/tax-import.cgi
new file mode 100644
index 000000000..77fba61f5
--- /dev/null
+++ b/httemplate/misc/process/tax-import.cgi
@@ -0,0 +1,58 @@
+% if ( $error ) {
+% warn $error;
+% errorpage($error);
+% } else {
+ <% include('/elements/header.html','Import successful') %>
+ <% include('/elements/footer.html') %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $cfh = $cgi->upload('codefile');
+my $zfh = $cgi->upload('plus4file');
+my $tfh = $cgi->upload('txmatrix');
+my $dfh = $cgi->upload('detail');
+#warn $cgi;
+#warn $fh;
+
+my $oldAutoCommit = $FS::UID::AutoCommit;
+local $FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+my $error = defined($cfh)
+ ? FS::tax_class::batch_import( {
+ filehandle => $cfh,
+ 'format' => scalar($cgi->param('format')),
+ } )
+ : 'No code file';
+
+$error ||= defined($zfh)
+ ? FS::cust_tax_location::batch_import( {
+ filehandle => $zfh,
+ 'format' => scalar($cgi->param('format')),
+ } )
+ : 'No plus4 file';
+
+$error ||= defined($tfh)
+ ? FS::part_pkg_taxrate::batch_import( {
+ filehandle => $tfh,
+ 'format' => scalar($cgi->param('format')),
+ } )
+ : 'No tax matrix file';
+
+$error ||= defined($dfh)
+ ? FS::tax_rate::batch_import( {
+ filehandle => $dfh,
+ 'format' => scalar($cgi->param('format')),
+ } )
+ : 'No tax detail file';
+
+if ($error) {
+ $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+}else{
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+}
+
+</%init>
diff --git a/httemplate/misc/tax-import.cgi b/httemplate/misc/tax-import.cgi
new file mode 100644
index 000000000..1f60dbe00
--- /dev/null
+++ b/httemplate/misc/tax-import.cgi
@@ -0,0 +1,91 @@
+<% include("/elements/header.html",'Batch Tax Rate Import') %>
+
+Import a CSV file set containing tax rate records.
+<BR><BR>
+
+<FORM ACTION="process/tax-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+<% &ntable("#cccccc", 2) %>
+
+<TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="cch">CCH
+<!-- <OPTION VALUE="extended" SELECTED>Extended
+ <OPTION VALUE="extended-plus_company">Extended plus company -->
+ </SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">code CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="codefile"></TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">plus4 CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="plus4file"></TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">txmatrix CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="txmatrix"></TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">detail CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="detail"></TD>
+</TR>
+
+
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV files"></TD></TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+<!-- Simple file format is CSV, with the following field order: <i>cust_pkg.setup, dayphone, first, last, address1, address2, city, state, zip, comments</i>
+<BR><BR> -->
+
+<%$req%> Required fields
+<BR><BR>
+
+Field information:
+
+<ul>
+
+ <li><i>refnum</i>: Advertising source number - where a customer heard about your service. Configuration -&gt; Miscellaneous -&gt; View/Edit advertising sources. This field has special treatment upon import: If a string is passed instead
+of an integer, the string is searched for and if necessary auto-created in the
+advertising source table.
+
+ <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing.
+
+ <li><i>paycvv</i>: CVV2 number (three digits on the back of the credit card)
+
+ <li><i>paydate</i>: Credit card expiration date, MM/YYYY or MM/YY (M/YY and M/YYYY are also accepted).
+
+ <li><i>invoicing_list</i>: Email address for invoices, or POST for postal invoices.
+
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Provisioning, services and packages -&gt; View/Edit package definitions
+
+ <li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified.
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $req = qq!<font color="#ff0000">*</font>!;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+</%init>