From fa19c2c557f7abd72f94357373290b99dd3297bf Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 25 Jun 2009 01:28:53 +0000 Subject: [PATCH] tax adjustments, RT#5595 --- FS/FS.pm | 2 + FS/FS/AccessRight.pm | 1 + FS/FS/Conf.pm | 7 ++ FS/FS/Schema.pm | 18 ++- FS/FS/cust_bill.pm | 8 +- FS/FS/cust_bill_pkg.pm | 16 ++- FS/FS/cust_main.pm | 30 +++++ FS/FS/cust_tax_adjustment.pm | 149 +++++++++++++++++++++++ FS/MANIFEST | 2 + FS/t/cust_tax_adjustment.t | 5 + httemplate/edit/cust_tax_adjustment.html | 102 ++++++++++++++++ httemplate/edit/process/cust_tax_adjustment.html | 41 +++++++ httemplate/search/cust_tax_adjustment.html | 52 ++++++++ httemplate/view/cust_main/payment_history.html | 29 ++++- 14 files changed, 452 insertions(+), 10 deletions(-) create mode 100644 FS/FS/cust_tax_adjustment.pm create mode 100644 FS/t/cust_tax_adjustment.t create mode 100644 httemplate/edit/cust_tax_adjustment.html create mode 100644 httemplate/edit/process/cust_tax_adjustment.html create mode 100644 httemplate/search/cust_tax_adjustment.html diff --git a/FS/FS.pm b/FS/FS.pm index e34b45db6..1477e985f 100644 --- a/FS/FS.pm +++ b/FS/FS.pm @@ -102,6 +102,8 @@ L - Locale (tax rate) class L - Tax exemption record class +L - Tax adjustment record class + L - Line-item specific tax exemption record class L - Service base class diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm index 146b9fa4e..3157d5ff0 100644 --- a/FS/FS/AccessRight.pm +++ b/FS/FS/AccessRight.pm @@ -152,6 +152,7 @@ tie my %rights, 'Tie::IxHash', 'View invoices', 'Resend invoices', #NEWNEW 'View customer tax exemptions', #yow + 'Add customer tax adjustment', #new, but no need to phase in 'View customer batched payments', #NEW 'View customer pending payments', #NEW 'Edit customer pending payments', #NEW diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index a9b891c8b..ac479da1e 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2851,6 +2851,13 @@ worry that config_items is freeside-specific and icky. ], }, + { + 'key' => 'enable_tax_adjustments', + 'section' => 'billing', + 'description' => 'Enable the ability to add manual tax adjustments.', + 'type' => 'checkbox', + }, + ); 1; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 0ca15a64d..aed8d6079 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -513,6 +513,7 @@ sub tables_hashref { 'sdate', @date_type, '', '', 'edate', @date_type, '', '', 'itemdesc', 'varchar', 'NULL', $char_d, '', '', + 'itemcomment', 'varchar', 'NULL', $char_d, '', '', 'section', 'varchar', 'NULL', $char_d, '', '', 'quantity', 'int', 'NULL', '', '', '', 'unitsetup', @money_typen, '', '', @@ -520,7 +521,7 @@ sub tables_hashref { ], 'primary_key' => 'billpkgnum', 'unique' => [], - 'index' => [ ['invnum'], [ 'pkgnum' ] ], + 'index' => [ ['invnum'], [ 'pkgnum' ], [ 'itemdesc' ], ], }, 'cust_bill_pkg_detail' => { @@ -800,6 +801,21 @@ sub tables_hashref { 'index' => [ [ 'custnum' ] ], }, + 'cust_tax_adjustment' => { + 'columns' => [ + 'adjustmentnum', 'serial', '', '', '', '', + 'custnum', 'int', '', '', '', '', + 'taxname', 'varchar', '', $char_d, '', '', + 'amount', @money_type, '', '', + 'comment', 'varchar', 'NULL', $char_d, '', '', + 'billpkgnum', 'int', 'NULL', '', '', '', + #more? no cust_bill_pkg_tax_location? + ], + 'primary_key' => 'adjustmentnum', + 'unique' => [], + 'index' => [ [ 'custnum' ], [ 'billpkgnum' ] ], + }, + 'cust_main_county' => { #county+state+country are checked off the #cust_main_county for validation and to provide # a tax rate. diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 737f68cc1..17b88c540 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -1457,11 +1457,9 @@ sub print_csv { } else { #pkgnum tax next unless $cust_bill_pkg->setup != 0; - my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc') - ? ( $cust_bill_pkg->itemdesc || 'Tax' ) - : 'Tax'; - ($pkg, $setup, $recur, $sdate, $edate) = - ( $itemdesc, sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' ); + $pkg = $cust_bill_pkg->desc; + $setup = sprintf('%10.2f', $cust_bill_pkg->setup ); + ( $sdate, $edate ) = ( '', '' ); } $csv->combine( diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index bb071739e..fbc67c542 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -175,6 +175,17 @@ sub insert { } } + my $cust_tax_adjustment = $self->get('cust_tax_adjustment'); + if ( $cust_tax_adjustment ) { + $cust_tax_adjustment->billpkgnum($self->billpkgnum); + $error = $cust_tax_adjustment->replace; + warn $error; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -224,6 +235,7 @@ sub check { || $self->ut_numbern('sdate') || $self->ut_numbern('edate') || $self->ut_textn('itemdesc') + || $self->ut_textn('itemcomment') ; return $error if $error; @@ -381,7 +393,9 @@ sub desc { if ( $self->pkgnum > 0 ) { $self->itemdesc || $self->part_pkg->pkg; } else { - $self->itemdesc || 'Tax'; + my $desc = $self->itemdesc || 'Tax'; + $desc .= ' '. $self->itemcomment if $self->itemcomment =~ /\S/; + $desc; } } diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index e7cdd21d4..dd99edab3 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -41,6 +41,7 @@ use FS::part_referral; use FS::cust_main_county; use FS::cust_location; use FS::cust_main_exemption; +use FS::cust_tax_adjustment; use FS::tax_rate; use FS::tax_rate_location; use FS::cust_tax_location; @@ -2661,6 +2662,35 @@ sub bill { } + #add tax adjustments + warn "adding tax adjustments...\n" if $DEBUG > 2; + foreach my $cust_tax_adjustment ( + qsearch('cust_tax_adjustment', { 'custnum' => $self->custnum, + 'billpkgnum' => '', + } + ) + ) { + + my $tax = sprintf('%.2f', $cust_tax_adjustment->amount ); + $total_setup = sprintf('%.2f', $total_setup+$tax ); + + my $itemdesc = $cust_tax_adjustment->taxname; + $itemdesc = '' if $itemdesc eq 'Tax'; + + push @cust_bill_pkg, new FS::cust_bill_pkg { + 'pkgnum' => 0, + 'setup' => $tax, + 'recur' => 0, + 'sdate' => '', + 'edate' => '', + 'itemdesc' => $itemdesc, + 'itemcomment' => $cust_tax_adjustment->comment, + 'cust_tax_adjustment' => $cust_tax_adjustment, + #'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location, + }; + + } + my $charged = sprintf('%.2f', $total_setup + $total_recur ); #create the new invoice diff --git a/FS/FS/cust_tax_adjustment.pm b/FS/FS/cust_tax_adjustment.pm new file mode 100644 index 000000000..5891368c5 --- /dev/null +++ b/FS/FS/cust_tax_adjustment.pm @@ -0,0 +1,149 @@ +package FS::cust_tax_adjustment; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); +use FS::cust_main; +use FS::cust_bill_pkg; + +=head1 NAME + +FS::cust_tax_adjustment - Object methods for cust_tax_adjustment records + +=head1 SYNOPSIS + + use FS::cust_tax_adjustment; + + $record = new FS::cust_tax_adjustment \%hash; + $record = new FS::cust_tax_adjustment { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_tax_adjustment object represents an taxation adjustment. +FS::cust_tax_adjustment inherits from FS::Record. The following fields are +currently supported: + +=over 4 + +=item adjustmentnum + +primary key + +=item custnum + +custnum + +=item taxname + +taxname + +=item amount + +amount + +=item comment + +comment + +=item billpkgnum + +billpkgnum + + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new record. To add the record to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'cust_tax_adjustment'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('adjustmentnum') + || $self->ut_foreign_key('custnum', 'cust_main', 'custnum' ) + || $self->ut_text('taxname') + || $self->ut_money('amount') + || $self->ut_textn('comment') + || $self->ut_foreign_keyn('billpkgnum', 'cust_bill_pkg', 'billpkgnum' ) + ; + return $error if $error; + + $self->SUPER::check; +} + +sub cust_bill_pkg { + my $self = shift; + qsearchs('cust_bill_pkg', { 'billpkgnum' => $self->billpkgnum } ); +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/MANIFEST b/FS/MANIFEST index b52d7b318..cc4f98e3e 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -442,3 +442,5 @@ FS/cust_recon.pm t/cust_recon.t FS/cust_main_exemption.pm t/cust_main_exemption.t +FS/cust_tax_adjustment.pm +t/cust_tax_adjustment.t diff --git a/FS/t/cust_tax_adjustment.t b/FS/t/cust_tax_adjustment.t new file mode 100644 index 000000000..cc5719a8c --- /dev/null +++ b/FS/t/cust_tax_adjustment.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::cust_tax_adjustment; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/edit/cust_tax_adjustment.html b/httemplate/edit/cust_tax_adjustment.html new file mode 100644 index 000000000..9d4afbc60 --- /dev/null +++ b/httemplate/edit/cust_tax_adjustment.html @@ -0,0 +1,102 @@ +<% include('/elements/header-popup.html', 'Tax adjustment' ) %> + +<% include('/elements/error.html') %> + + + +
+ + + + + + + + + + + + + + + + + + + + +
Tax + +
Amount + $ +
Comment + +
+ +
+param('error') ? '' :' DISABLED' %>> + +
+ + + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $sql = 'SELECT DISTINCT(taxname) FROM cust_main_county'; +my $sth = dbh->prepare($sql) or die dbh->errstr; +$sth->execute() or die $sth->errstr; +my @taxname = map { $_->[0] || 'Tax' } @{ $sth->fetchall_arrayref([]) }; + +my $conf = new FS::Conf; + +$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; +my $custnum = $1; + +my $amount = ''; +if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) { + $amount = $1; +} + +$cgi->param('comment') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ + or die 'illegal description'; +my $comment = $1; + + diff --git a/httemplate/edit/process/cust_tax_adjustment.html b/httemplate/edit/process/cust_tax_adjustment.html new file mode 100644 index 000000000..204b5b9f7 --- /dev/null +++ b/httemplate/edit/process/cust_tax_adjustment.html @@ -0,0 +1,41 @@ +% if ( $error ) { +% $cgi->param('error', $error ); +<% $cgi->redirect($p.'cust_tax_adjustment.html?'. $cgi->query_string) %> +% } else { +<% header("Tax adjustment added") %> + + +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $error = ''; +my $conf = new FS::conf; +my $param = $cgi->Vars; + +$param->{"custnum"} =~ /^(\d+)$/ + or $error .= "Illegal customer number " . $param->{"custnum"} . " "; +my $custnum = $1; + +$param->{"amount"} =~ /^\s*(\d*(?:\.?\d{1,2}))\s*$/ + or $error .= "Illegal amount " . $param->{"amount"} . " "; +my $amount = $1; + +unless ( $error ) { + + my $cust_tax_adjustment = new FS::cust_tax_adjustment { + 'custnum' => $custnum, + 'taxname' => $param->{'taxname'}, + 'amount' => $amount, + 'comment' => $param->{'comment'}, + }; + $error = $cust_tax_adjustment->insert; + +} + + diff --git a/httemplate/search/cust_tax_adjustment.html b/httemplate/search/cust_tax_adjustment.html new file mode 100644 index 000000000..dfc638e8b --- /dev/null +++ b/httemplate/search/cust_tax_adjustment.html @@ -0,0 +1,52 @@ +<% include( 'elements/search.html', + 'title' => $title, + 'name_singular' => 'tax adjustment', + 'query' => $query, + 'count_query' => $count_query, + 'header' => [ 'Tax', 'Amount', 'Comment', 'Invoice' ], + 'fields' => [ 'taxname', + sub { $money_char. shift->amount }, + 'comment', + sub { my $l = shift->cust_bill_pkg; + $l ? '#'.$l->invnum : ''; + }, + ], + 'links' => [ '', '', '', $ilink ], + ) +%> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +my $count_query = 'SELECT COUNT(*) FROM cust_tax_adjustment'; + +my $hashref = {}; + +my $custnum = $cgi->param('custnum'); +my $cust_main; +if ( $custnum ) { + $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); + $hashref->{'custnum'} = $custnum; +} + +my $title = 'Tax adjustments'; +$title .= ' for '. $cust_main->name if $cust_main; + +my $query = { 'table' => 'cust_tax_adjustment', + 'hashref' => $hashref, + }; + +my $ilink = [ $p.'view/cust_bill.cgi?', sub { my $l = shift->cust_bill_pkg; + $l ? $l->invnum : 'EXCEPTION'; + } + ]; + +#XXX would be nice to list customer fields on the report too, if we ever need +# to link to here without a custnum (i'm sure we will, eventually...) + + diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html index f2abe0eac..1711e1449 100644 --- a/httemplate/view/cust_main/payment_history.html +++ b/httemplate/view/cust_main/payment_history.html @@ -127,10 +127,33 @@ %# tax exemption link -% if ( $curuser->access_right('View customer tax exemptions') ) { - View tax exemptions +% my $view_exemptions = $curuser->access_right('View customer tax exemptions'); +% my $add_adjustment = ( $conf->exists('enable_tax_adjustments') +% && $curuser->access_right('Add customer tax adjustment') +% ); +% if ( $view_exemptions || $add_adjustment ) { + +% if ( $view_exemptions ) { + View tax exemptions + <% $add_adjustment ? '|' : '' %> +% } + +% if ( $add_adjustment ) { + <% include('/elements/popup_link.html', { + 'action' => $p.'edit/cust_tax_adjustment.html?custnum='. $cust_main->custnum, + 'label' => 'Add tax adjustment', + 'actionlabel' => 'Add tax adjustment', + #'color' => '#333399', + #'width' => 763, + 'height' => 200, + }) + %> + | + View tax adjustments +% } +
-% } +% } %# batched payment links -- 2.11.0