From e18263db61c9695eb4c139f23c79730fd7659ad6 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 19 Mar 2015 13:43:34 -0700 Subject: [PATCH] correctly void invoices with fees, #32862 --- FS/FS/Schema.pm | 59 +++++++++++++++++----------- FS/FS/TemplateItem_Mixin.pm | 24 ++++++++++++ FS/FS/cust_bill_pkg.pm | 23 ++--------- FS/FS/cust_bill_pkg_fee_void.pm | 85 +++++++++++++++++++++++++++++++++++++++++ FS/FS/cust_bill_pkg_void.pm | 8 ++++ FS/t/cust_bill_pkg_fee_void.t | 5 +++ httemplate/view/cust_bill.cgi | 56 ++++++++++++++++++--------- 7 files changed, 199 insertions(+), 61 deletions(-) create mode 100644 FS/FS/cust_bill_pkg_fee_void.pm create mode 100644 FS/t/cust_bill_pkg_fee_void.t diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 378a5213a..a048d3e24 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -947,9 +947,9 @@ sub tables_hashref { { columns => [ 'eventnum' ], table => 'cust_event', }, - { columns => [ 'billpkgnum' ], - table => 'cust_bill_pkg', - }, + #{ columns => [ 'billpkgnum' ], + # table => 'cust_bill_pkg', + #}, { columns => [ 'feepart' ], table => 'part_fee', }, @@ -1078,17 +1078,31 @@ sub tables_hashref { { columns => [ 'billpkgnum' ], table => 'cust_bill_pkg', }, - { columns => [ 'base_billpkgnum' ], - table => 'cust_bill_pkg', - references => [ 'billpkgnum' ], - }, - { columns => [ 'base_invnum' ], - table => 'cust_bill', - references => [ 'invnum' ], + ], + }, + + 'cust_bill_pkg_fee_void' => { + 'columns' => [ + 'billpkgfeenum', 'serial', '', '', '', '', + 'billpkgnum', 'int', '', '', '', '', + 'base_invnum', 'int', '', '', '', '', + 'base_billpkgnum', 'int', 'NULL', '', '', '', + 'amount', @money_type, '', '', + ], + 'primary_key' => 'billpkgfeenum', + 'unique' => [], + 'index' => [ ['billpkgnum'], + ['base_invnum'], + ['base_billpkgnum'], + ], + 'foreign_keys' => [ + { columns => [ 'billpkgnum' ], + table => 'cust_bill_pkg_void', }, ], }, + 'cust_bill_pkg_tax_location' => { 'columns' => [ 'billpkgtaxlocationnum', 'serial', '', '', '', '', @@ -1119,10 +1133,10 @@ sub tables_hashref { { columns => [ 'locationnum' ], table => 'cust_location', }, - { columns => [ 'taxable_billpkgnum' ], - table => 'cust_bill_pkg', - references => [ 'billpkgnum' ], - }, + #{ columns => [ 'taxable_billpkgnum' ], + # table => 'cust_bill_pkg', + # references => [ 'billpkgnum' ], + #}, ], }, @@ -1150,10 +1164,10 @@ sub tables_hashref { { columns => [ 'taxratelocationnum' ], table => 'tax_rate_location', }, - { columns => [ 'taxable_billpkgnum' ], - table => 'cust_bill_pkg', - references => [ 'billpkgnum' ], - }, + #{ columns => [ 'taxable_billpkgnum' ], + # table => 'cust_bill_pkg', + # references => [ 'billpkgnum' ], + #}, ], }, @@ -1177,6 +1191,7 @@ sub tables_hashref { 'unitsetup', @money_typen, '', '', 'unitrecur', @money_typen, '', '', 'hidden', 'char', 'NULL', 1, '', '', + 'feepart', 'int', 'NULL', '', '', '', #void fields 'void_date', @date_type, '', '', 'reason', 'varchar', 'NULL', $char_d, '', '', @@ -1286,10 +1301,10 @@ sub tables_hashref { { columns => [ 'locationnum' ], table => 'cust_location', }, - { columns => [ 'taxable_billpkgnum' ], - table => 'cust_bill_pkg_void', - references => [ 'billpkgnum' ], - }, + #{ columns => [ 'taxable_billpkgnum' ], + # table => 'cust_bill_pkg_void', + # references => [ 'billpkgnum' ], + #}, ], }, diff --git a/FS/FS/TemplateItem_Mixin.pm b/FS/FS/TemplateItem_Mixin.pm index 27b8f1bdb..dcd7ab3fb 100644 --- a/FS/FS/TemplateItem_Mixin.pm +++ b/FS/FS/TemplateItem_Mixin.pm @@ -45,7 +45,31 @@ sub part_pkg { $part_pkg = $cust_pkg->part_pkg if $cust_pkg; $part_pkg; } +} + +=item part_fee +Returns the fee definition for this line item, if there is one. + +=cut + +sub part_fee { + my $self = shift; + $self->feepart + ? FS::part_fee->by_key($self->feepart) + : ''; +} + +=item part_X + +Returns L or L, whichever is applicable (or nothing, +if called on a tax line item). + +=cut + +sub part_X { + my $self = shift; + $self->part_pkg || $self->part_fee; } =item desc LOCALE diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 9b9cf925e..7257a9bb8 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -25,6 +25,7 @@ use FS::cust_bill_pkg_discount_void; use FS::cust_bill_pkg_tax_location_void; use FS::cust_bill_pkg_tax_rate_location_void; use FS::cust_tax_exempt_pkg_void; +use FS::cust_bill_pkg_fee_void; use FS::Cursor; @@ -358,6 +359,7 @@ sub void { cust_bill_pkg_tax_location cust_bill_pkg_tax_rate_location cust_tax_exempt_pkg + cust_bill_pkg_fee )) { foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) { @@ -417,6 +419,7 @@ sub delete { cust_tax_exempt_pkg cust_bill_pay_pkg cust_credit_bill_pkg + cust_bill_pkg_fee )) { foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) { @@ -1026,26 +1029,6 @@ sub tax_location { } } -=item part_X - -Returns the L or L object that defines this -charge. If called on a tax line, returns nothing. - -=cut - -sub part_X { - my $self = shift; - if ( $self->pkgpart_override ) { - return FS::part_pkg->by_key($self->pkgpart_override); - } elsif ( $self->pkgnum ) { - return $self->cust_pkg->part_pkg; - } elsif ( $self->feepart ) { - return $self->part_fee; - } else { - return; - } -} - =back =head1 CLASS METHODS diff --git a/FS/FS/cust_bill_pkg_fee_void.pm b/FS/FS/cust_bill_pkg_fee_void.pm new file mode 100644 index 000000000..51f5a3f56 --- /dev/null +++ b/FS/FS/cust_bill_pkg_fee_void.pm @@ -0,0 +1,85 @@ +package FS::cust_bill_pkg_fee_void; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::cust_bill_pkg_fee - Object methods for cust_bill_pkg_fee_void records + +=head1 SYNOPSIS + + use FS::cust_bill_pkg_fee; + + $record = new FS::cust_bill_pkg_fee \%hash; + $record = new FS::cust_bill_pkg_fee { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_bill_pkg_fee_void object records the origin of a fee that +appears on a voided invoice. FS::cust_bill_pkg_fee_void inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item billpkgfeenum - primary key + +=item billpkgnum - the billpkgnum of the fee line item + +=item base_invnum - the invoice number (L) that caused (this +portion of) the fee to be charged. + +=item base_billpkgnum - the invoice line item (L) that +caused (this portion of) the fee to be charged. May be null. + +=item amount - the fee amount + +=back + +=head1 METHODS + +=over 4 + +=cut + +sub table { 'cust_bill_pkg_fee_void'; } + +=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 + +sub check { my $self = shift; + + my $error = $self->ut_numbern('billpkgfeenum') + || $self->ut_number('billpkgnum') + || $self->ut_foreign_key('base_invnum', 'cust_bill', 'invnum') + || $self->ut_foreign_keyn('base_billpkgnum', 'cust_bill_pkg', 'billpkgnum') + || $self->ut_money('amount') + ; + return $error if $error; + + $self->SUPER::check; } + +=back + +=head1 SEE ALSO + +L + +=cut + +1; + diff --git a/FS/FS/cust_bill_pkg_void.pm b/FS/FS/cust_bill_pkg_void.pm index 8949ba7a3..080452e19 100644 --- a/FS/FS/cust_bill_pkg_void.pm +++ b/FS/FS/cust_bill_pkg_void.pm @@ -8,6 +8,7 @@ use FS::cust_bill_pkg_detail; use FS::cust_bill_pkg_display; use FS::cust_bill_pkg_discount; use FS::cust_bill_pkg; +use FS::cust_bill_pkg_fee; use FS::cust_bill_pkg_tax_location; use FS::cust_bill_pkg_tax_rate_location; use FS::cust_tax_exempt_pkg; @@ -170,6 +171,7 @@ sub unvoid { cust_bill_pkg_tax_location cust_bill_pkg_tax_rate_location cust_tax_exempt_pkg + cust_bill_pkg_fee )) { foreach my $voided ( @@ -239,6 +241,7 @@ sub check { || $self->ut_moneyn('unitsetup') || $self->ut_moneyn('unitrecur') || $self->ut_enum('hidden', [ '', 'Y' ]) + || $self->ut_numbern('feepart') ; return $error if $error; @@ -258,6 +261,11 @@ sub cust_bill { qsearchs( 'cust_bill_void', { 'invnum' => $self->invnum } ); } +sub cust_bill_pkg_fee { + my $self = shift; + qsearch( 'cust_bill_pkg_fee_void', { 'billpkgnum' => $self->billpkgnum } ); +} + =back =head1 BUGS diff --git a/FS/t/cust_bill_pkg_fee_void.t b/FS/t/cust_bill_pkg_fee_void.t new file mode 100644 index 000000000..a2a51dc2f --- /dev/null +++ b/FS/t/cust_bill_pkg_fee_void.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::cust_bill_pkg_fee_void; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi index 6bc499a35..f1bcf4d52 100755 --- a/httemplate/view/cust_bill.cgi +++ b/httemplate/view/cust_bill.cgi @@ -2,26 +2,37 @@ emt("View this customer (#[_1])",$display_custnum) => "${p}view/cust_main.cgi?$custnum", ) &> -% if ( $conf->exists('deleteinvoices') -% && $curuser->access_right('Delete invoices' ) -% ) -% { - - - - <% mt('Delete this invoice') |h %> -

+ +% if ( !$cust_bill->closed ) { # otherwise allow no changes +% my $can_delete = $conf->exists('deleteinvoices') +% && $curuser->access_right('Delete invoices'); +% my $can_void = $curuser->access_right('Void invoices'); +% if ( $can_void ) { + <& /elements/popup_link.html, + 'label' => emt('Void this invoice'), + 'actionlabel' => emt('Void this invoice'), + 'action' => $p.'misc/void-cust_bill.html?invnum='.$invnum, + &> +% } +% if ( $can_void and $can_delete ) { +  |  +% } +% if ( $can_delete ) { + \ + <% emt('Delete this invoice') |h %> +% } +% if ( $can_void or $can_delete ) { +

+% } % } % if ( $cust_bill->owed > 0 @@ -185,6 +196,13 @@ my $cust_bill = qsearchs({ 'hashref' => { 'invnum' => $invnum }, 'extra_sql' => ' AND '. $curuser->agentnums_sql, }); +# if we're asked for a voided invnum, redirect appropriately +if (!$cust_bill and FS::cust_bill_void->row_exists("invnum = $invnum") ) { + $m->clear_buffer; + my $url = $p.'view/cust_bill_void.html?'.$cgi->query_string; + $m->print( $cgi->redirect($url) ); + $m->abort; +} die "Invoice #$invnum not found!" unless $cust_bill; $cust_bill->set('mode' => $mode); -- 2.11.0