use Date::Parse;
use Storable qw( thaw );
use MIME::Base64;
-use FS::Record qw( qsearchs dbh );
+use FS::Record qw( qsearch qsearchs dbh );
use FS::tax_class;
use FS::cust_bill_pkg;
use FS::cust_tax_location;
use FS::part_pkg_taxrate;
+use FS::cust_main;
@ISA = qw( FS::Record );
|| $self->ut_enum('setuptax', [ '', 'Y' ] )
|| $self->ut_enum('recurtax', [ '', 'Y' ] )
|| $self->ut_enum('manual', [ '', 'Y' ] )
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
|| $self->SUPER::check
;
$tax_passtypes{$self->passtype};
}
-=item taxline CUST_BILL_PKG, ...
+=item taxline CUST_BILL_PKG|AMOUNT, ...
Returns a listref of a name and an amount of tax calculated for the list
-of packages. If an error occurs, a message is returned as a scalar.
+of packages/amounts. If an error occurs, a message is returned as a scalar.
=cut
sub taxline {
my $self = shift;
- my @cust_bill_pkg = @_;
+
+ my $name = $self->taxname;
+ $name = 'Other surcharges'
+ if ($self->passtype == 2);
+ my $amount = 0;
+
+ return [$name, $amount] # we always know how to handle disabled taxes
+ if $self->disabled;
+
+ my $taxable_charged = 0;
+ my @cust_bill_pkg = grep { $taxable_charged += $_ unless ref; ref; } @_;
+
+ warn "calculating taxes for ". $self->taxnum. " on ".
+ join (",", map { $_->pkgnum } @cust_bill_pkg)
+ if $DEBUG;
if ($self->passflag eq 'N') {
return "fatal: can't (yet) handle taxes not passed to the customer";
'" basis';
}
- my $name = $self->taxname;
- $name = 'Other surcharges'
- if ($self->passtype == 2);
- my $amount = 0;
-
- my $taxable_charged = 0;
unless ($self->setuptax =~ /^Y$/i) {
$taxable_charged += $_->setup foreach @cust_bill_pkg;
}
my $taxable_units = 0;
unless ($self->recurtax =~ /^Y$/i) {
- $taxable_units += $_->units foreach @cust_bill_pkg;
+ if ($self->unittype == 0) {
+ $taxable_units += $_->units foreach @cust_bill_pkg;
+ }elsif ($self->unittype == 1) {
+ return qq!fatal: can't (yet) handle fee with minute unit type!;
+ }elsif ($self->unittype == 2) {
+ $taxable_units = 1;
+ }else {
+ return qq!fatal: can't (yet) handle unknown unit type in tax!.
+ $self->taxnum;
+ }
}
#
$amount += $taxable_charged * $self->tax;
$amount += $taxable_units * $self->fee;
+ warn "calculated taxes as [ $name, $amount ]\n"
+ if $DEBUG;
+
return [$name, $amount];
}
+=item tax_on_tax CUST_MAIN
+
+Returns a list of taxes which are candidates for taxing taxes for the
+given customer (see L<FS::cust_main>)
+
+=cut
+
+sub tax_on_tax {
+ my $self = shift;
+ my $cust_main = shift;
+
+ warn "looking up taxes on tax ". $self->taxnum. " for customer ".
+ $cust_main->custnum
+ if $DEBUG;
+
+ my $geocode = $cust_main->geocode($self->data_vendor);
+
+ # CCH oddness in m2m
+ my $dbh = dbh;
+ my $extra_sql = ' AND ('.
+ join(' OR ', map{ 'geocode = '. $dbh->quote(substr($geocode, 0, $_)) }
+ qw(10 5 2)
+ ).
+ ')';
+
+ my $order_by = 'ORDER BY taxclassnum, length(geocode) desc';
+ my $select = 'DISTINCT ON(taxclassnum) *';
+
+ # should qsearch preface columns with the table to facilitate joins?
+ my @taxclassnums = map { $_->taxclassnum }
+ qsearch( { 'table' => 'part_pkg_taxrate',
+ 'select' => $select,
+ 'hashref' => { 'data_vendor' => $self->data_vendor,
+ 'taxclassnumtaxed' => $self->taxclassnum,
+ },
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $order_by,
+ } );
+
+ return () unless @taxclassnums;
+
+ $extra_sql =
+ "AND (". join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")";
+
+ qsearch({ 'table' => 'tax_rate',
+ 'hashref' => { 'geocode' => $geocode, },
+ 'extra_sql' => $extra_sql,
+ })
+
+}
+
=back
=head1 SUBROUTINES
=item process_batch
-Load an batch import as a queued JSRPC job
+Load a batch import as a queued JSRPC job
=cut
$error = "No $name supplied";
next;
}
- my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc;
my $filename = "$dir/". $files{$file};
open my $fh, "< $filename" or $error ||= "Can't open $name file: $!";
'PLUS4', 'plus4file', \&FS::cust_tax_location::batch_import,
'TXMATRIX', 'txmatrix', \&FS::part_pkg_taxrate::batch_import,
);
- my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
+ my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc;
while( scalar(@list) ) {
my ($name, $file, $import_sub) = (shift @list, shift @list, shift @list);
unless ($files{$file}) {
}
+=item browse_queries PARAMS
+
+Returns a list consisting of a hashref suited for use as the argument
+to qsearch, and sql query string. Each is based on the PARAMS hashref
+of keys and values which frequently would be passed as C<scalar($cgi->Vars)>
+from a form. This conveniently creates the query hashref and count_query
+string required by the browse and search elements. As a side effect,
+the PARAMS hashref is untainted and keys with unexpected values are removed.
+
+=cut
+
+sub browse_queries {
+ my $params = shift;
+
+ my $query = {
+ 'table' => 'tax_rate',
+ 'hashref' => {},
+ 'order_by' => 'ORDER BY geocode, taxclassnum',
+ },
+
+ my $extra_sql = '';
+
+ if ( $params->{data_vendor} =~ /^(\w+)$/ ) {
+ $extra_sql .= ' WHERE data_vendor = '. dbh->quote($1);
+ } else {
+ delete $params->{data_vendor};
+ }
+
+ if ( $params->{geocode} =~ /^(\w+)$/ ) {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ 'geocode LIKE '. dbh->quote($1.'%');
+ } else {
+ delete $params->{geocode};
+ }
+
+ if ( $params->{taxclassnum} =~ /^(\d+)$/ &&
+ qsearchs( 'tax_class', {'taxclassnum' => $1} )
+ )
+ {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ ' taxclassnum = '. dbh->quote($1)
+ } else {
+ delete $params->{taxclassnun};
+ }
+
+ my $tax_type = $1
+ if ( $params->{tax_type} =~ /^(\d+)$/ );
+ delete $params->{tax_type}
+ unless $tax_type;
+
+ my $tax_cat = $1
+ if ( $params->{tax_cat} =~ /^(\d+)$/ );
+ delete $params->{tax_cat}
+ unless $tax_cat;
+
+ my @taxclassnum = ();
+ if ($tax_type || $tax_cat ) {
+ my $compare = "LIKE '". ( $tax_type || "%" ). ":". ( $tax_cat || "%" ). "'";
+ $compare = "= '$tax_type:$tax_cat'" if ($tax_type && $tax_cat);
+ @taxclassnum = map { $_->taxclassnum }
+ qsearch({ 'table' => 'tax_class',
+ 'hashref' => {},
+ 'extra_sql' => "WHERE taxclass $compare",
+ });
+ }
+
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ). '( '.
+ join(' OR ', map { " taxclassnum = $_ " } @taxclassnum ). ' )'
+ if ( @taxclassnum );
+
+ unless ($params->{'showdisabled'}) {
+ $extra_sql .= ( $extra_sql =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
+ "( disabled = '' OR disabled IS NULL )";
+ }
+
+ $query->{extra_sql} = $extra_sql;
+
+ return ($query, "SELECT COUNT(*) FROM tax_rate $extra_sql");
+}
+
=back
=head1 BUGS