From: Ivan Kohler Date: Thu, 5 Nov 2015 18:44:38 +0000 (-0800) Subject: Merge branch 'master' of git.freeside.biz:/home/git/freeside X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=5da68ff1a7c638e30cbafbc9b0749f1e82b333df;hp=88ed72cdd96321a0eeaacc3107da413ab6e9dc98 Merge branch 'master' of git.freeside.biz:/home/git/freeside --- diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm index 95cf29a8b..a96a6cb41 100644 --- a/FS/FS/AccessRight.pm +++ b/FS/FS/AccessRight.pm @@ -232,7 +232,7 @@ tie my %rights, 'Tie::IxHash', 'Refund Echeck payment', 'Delete refund', #NEW 'Add on-the-fly credit reason', #NEW - 'Add on-the-fly void credit reason', + 'Add on-the-fly void reason', 'Add on-the-fly refund reason', #NEW ], diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 447302b36..6b5d6586c 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -735,8 +735,9 @@ sub tables_hashref { #void fields 'void_date', @date_type, '', '', - 'reason', 'varchar', 'NULL', $char_d, '', '', - 'void_usernum', 'int', 'NULL', '', '', '', + 'reason', 'varchar', 'NULL', $char_d, '', '', + 'reasonnum', 'int', 'NULL', '', '', '', + 'void_usernum', 'int', 'NULL', '', '', '', ], 'primary_key' => 'invnum', 'unique' => [ [ 'custnum', 'agent_invid' ] ], #agentnum? huh @@ -750,6 +751,9 @@ sub tables_hashref { { columns => [ 'statementnum' ], table => 'cust_statement', #_void? both? }, + { columns => [ 'reasonnum' ], + table => 'reason', + }, { columns => [ 'void_usernum' ], table => 'access_user', references => [ 'usernum' ], @@ -1197,8 +1201,9 @@ sub tables_hashref { 'feepart', 'int', 'NULL', '', '', '', #void fields 'void_date', @date_type, '', '', - 'reason', 'varchar', 'NULL', $char_d, '', '', - 'void_usernum', 'int', 'NULL', '', '', '', + 'reason', 'varchar', 'NULL', $char_d, '', '', + 'reasonnum', 'int', 'NULL', '', '', '', + 'void_usernum', 'int', 'NULL', '', '', '', ], 'primary_key' => 'billpkgnum', 'unique' => [], @@ -1209,6 +1214,9 @@ sub tables_hashref { { columns => [ 'invnum' ], table => 'cust_bill_void', }, + { columns => [ 'reasonnum' ], + table => 'reason', + }, #pkgnum 0 and -1 are used for special things #{ columns => [ 'pkgnum' ], # table => 'cust_pkg', @@ -2523,6 +2531,7 @@ sub tables_hashref { #void fields 'void_date', @date_type, '', '', 'reason', 'varchar', 'NULL', $char_d, '', '', + 'reasonnum', 'int', 'NULL', '', '', '', 'void_usernum', 'int', 'NULL', '', '', '', ], 'primary_key' => 'paynum', @@ -2544,6 +2553,9 @@ sub tables_hashref { { columns => [ 'gatewaynum' ], table => 'payment_gateway', }, + { columns => [ 'reasonnum' ], + table => 'reason', + }, { columns => [ 'void_usernum' ], table => 'access_user', references => [ 'usernum' ], diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm index 263230b34..bffda566f 100644 --- a/FS/FS/Upgrade.pm +++ b/FS/FS/Upgrade.pm @@ -350,6 +350,11 @@ sub upgrade_data { #customer credits 'cust_credit' => [], + # reason / void_reason migration to reasonnum / void_reasonnum + 'cust_credit_void' => [], + 'cust_bill_void' => [], + 'cust_bill_pkg_void' => [], + #duplicate history records 'h_cust_svc' => [], diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm index 40475ec00..0da718c64 100644 --- a/FS/FS/access_right.pm +++ b/FS/FS/access_right.pm @@ -251,9 +251,7 @@ sub _upgrade_data { # class method 'List customers' => 'List contacts', 'Backdate payment' => 'Backdate credit', 'Generate quotation' => 'Disable quotation', - 'Void credit' => 'Void credit', - 'Unvoid credit' => 'Unvoid credit', - 'Add on-the-fly void credit reason' => 'Add on-the-fly void credit reason', + 'Add on-the-fly void credit reason' => 'Add on-the-fly void reason', '_ALL' => 'Employee preference telephony integration', ); diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 6546bfa95..138d0fab8 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -37,6 +37,8 @@ use FS::cust_bill_pay_pkg; use FS::cust_credit_bill_pkg; use FS::discount_plan; use FS::cust_bill_void; +use FS::reason; +use FS::reason_type; use FS::L10N; $DEBUG = 0; @@ -212,7 +214,7 @@ sub insert { } -=item void +=item void [ REASON ] Voids this invoice: deletes the invoice and adds a record of the voided invoice to the FS::cust_bill_void table (and related tables starting from @@ -224,6 +226,14 @@ sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + unless (ref($reason) || !$reason) { + $reason = FS::reason->new_or_existing( + 'class' => 'X', + 'type' => 'Void invoice', + 'reason' => $reason + ); + } + local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; local $SIG{QUIT} = 'IGNORE'; @@ -238,7 +248,7 @@ sub void { my $cust_bill_void = new FS::cust_bill_void ( { map { $_ => $self->get($_) } $self->fields } ); - $cust_bill_void->reason($reason); + $cust_bill_void->reasonnum($reason->reasonnum) if $reason; my $error = $cust_bill_void->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index 5861ee47f..aea776a80 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -26,6 +26,8 @@ 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::reason; +use FS::reason_type; use FS::Cursor; @@ -322,7 +324,7 @@ sub insert { } -=item void +=item void [ REASON ] Voids this line item: deletes the line item and adds a record of the voided line item to the FS::cust_bill_pkg_void table (and related tables). @@ -333,6 +335,14 @@ sub void { my $self = shift; my $reason = scalar(@_) ? shift : ''; + unless (ref($reason) || !$reason) { + $reason = FS::reason->new_or_existing( + 'class' => 'X', + 'type' => 'Void invoice', + 'reason' => $reason + ); + } + local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; local $SIG{QUIT} = 'IGNORE'; @@ -347,7 +357,7 @@ sub void { my $cust_bill_pkg_void = new FS::cust_bill_pkg_void ( { map { $_ => $self->get($_) } $self->fields } ); - $cust_bill_pkg_void->reason($reason); + $cust_bill_pkg_void->reasonnum($reason->reasonnum) if $reason; my $error = $cust_bill_pkg_void->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; diff --git a/FS/FS/cust_bill_pkg_void.pm b/FS/FS/cust_bill_pkg_void.pm index 080452e19..991dd37dd 100644 --- a/FS/FS/cust_bill_pkg_void.pm +++ b/FS/FS/cust_bill_pkg_void.pm @@ -1,7 +1,8 @@ package FS::cust_bill_pkg_void; -use base qw( FS::TemplateItem_Mixin FS::Record ); +use base qw( FS::TemplateItem_Mixin FS::reason_Mixin FS::Record ); use strict; +use vars qw( $me $DEBUG ); use FS::Record qw( qsearch qsearchs dbh fields ); use FS::cust_bill_void; use FS::cust_bill_pkg_detail; @@ -13,6 +14,9 @@ use FS::cust_bill_pkg_tax_location; use FS::cust_bill_pkg_tax_rate_location; use FS::cust_tax_exempt_pkg; +$me = '[ FS::cust_bill_pkg_void ]'; +$DEBUG = 0; + =head1 NAME FS::cust_bill_pkg_void - Object methods for cust_bill_pkg_void records @@ -104,6 +108,13 @@ unitrecur hidden +=item reason + +freeform string (deprecated) + +=item reasonnum + +reason for voiding the payment (see L) =back @@ -134,6 +145,10 @@ sub discount_table { 'cust_bill_pkg_discount_void'; } Adds this record to the database. If there is an error, returns the error, otherwise returns false. +=item reason + +Returns the text of the associated void reason (see L) for this. + =item unvoid "Un-void"s this line item: Deletes the voided line item from the database and @@ -242,6 +257,8 @@ sub check { || $self->ut_moneyn('unitrecur') || $self->ut_enum('hidden', [ '', 'Y' ]) || $self->ut_numbern('feepart') + || $self->ut_textn('reason') + || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum') ; return $error if $error; @@ -266,6 +283,18 @@ sub cust_bill_pkg_fee { qsearch( 'cust_bill_pkg_fee_void', { 'billpkgnum' => $self->billpkgnum } ); } + +# _upgrade_data +# +# Used by FS::Upgrade to migrate to a new database. +sub _upgrade_data { # class method + my ($class, %opts) = @_; + + warn "$me upgrading $class\n" if $DEBUG; + + $class->_upgrade_reasonnum(%opts); +} + =back =head1 BUGS diff --git a/FS/FS/cust_bill_void.pm b/FS/FS/cust_bill_void.pm index f3dba9081..50f69c9fa 100644 --- a/FS/FS/cust_bill_void.pm +++ b/FS/FS/cust_bill_void.pm @@ -1,13 +1,18 @@ package FS::cust_bill_void; -use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin FS::Record ); +use base qw( FS::Template_Mixin FS::cust_main_Mixin FS::otaker_Mixin + FS::reason_Mixin FS::Record ); use strict; +use vars qw( $me $DEBUG ); use FS::Record qw( qsearch qsearchs dbh fields ); use FS::cust_statement; use FS::access_user; use FS::cust_bill_pkg_void; use FS::cust_bill; +$me = '[ FS::cust_bill_void ]'; +$DEBUG = 0; + =head1 NAME FS::cust_bill_void - Object methods for cust_bill_void records @@ -82,9 +87,13 @@ promised_date void_date -=item reason +=item reason + +freeform string (deprecated) -reason +=item reasonnum + +reason for voiding the payment (see L) =item void_usernum @@ -216,6 +225,7 @@ sub check { || $self->ut_numbern('void_date') || $self->ut_textn('reason') || $self->ut_numbern('void_usernum') + || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum') ; return $error if $error; @@ -259,6 +269,10 @@ sub void_access_user { =item cust_bill_pkg +=item reason + +Returns the text of the associated void reason (see L) for this. + =cut sub cust_bill_pkg { #actually cust_bill_pkg_void objects @@ -339,6 +353,17 @@ sub search_sql_where { sub enable_previous { 0 } +# _upgrade_data +# +# Used by FS::Upgrade to migrate to a new database. +sub _upgrade_data { # class method + my ($class, %opts) = @_; + + warn "$me upgrading $class\n" if $DEBUG; + + $class->_upgrade_reasonnum(%opts); +} + =back =head1 BUGS diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm index 2f2338ee4..032953532 100644 --- a/FS/FS/cust_credit.pm +++ b/FS/FS/cust_credit.pm @@ -377,7 +377,7 @@ sub void { my $cust_credit_void = new FS::cust_credit_void ( { map { $_ => $self->get($_) } $self->fields } ); - $cust_credit_void->set('void_reasonnum', $reason->reasonnum); + $cust_credit_void->set('void_reasonnum', $reason->reasonnum) if $reason; my $error = $cust_credit_void->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; diff --git a/FS/FS/cust_credit_void.pm b/FS/FS/cust_credit_void.pm index 9c92068eb..60beaa655 100644 --- a/FS/FS/cust_credit_void.pm +++ b/FS/FS/cust_credit_void.pm @@ -2,12 +2,16 @@ package FS::cust_credit_void; use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::reason_Mixin FS::Record ); use strict; +use vars qw( $me $DEBUG ); use FS::Record qw(qsearchs); # qsearch qsearchs); use FS::CurrentUser; use FS::access_user; use FS::cust_credit; use FS::UID qw( dbh ); +$me = '[ FS::cust_credit_void ]'; +$DEBUG = 0; + =head1 NAME FS::cust_credit_void - Object methods for cust_credit_void objects @@ -190,6 +194,17 @@ sub void_reason { return $reason_text; } +# _upgrade_data +# +# Used by FS::Upgrade to migrate to a new database. +sub _upgrade_data { # class method + my ( $class, %opts ) = @_; + + warn "$me upgrading $class\n" if $DEBUG; + + $class->_upgrade_reasonnum(%opts); +} + =back =head1 BUGS diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index 403d8dd1c..c700cf7f8 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -267,7 +267,10 @@ sub _bop_defaults { } } - $options->{payinfo} = $self->payinfo unless exists( $options->{payinfo} ); + unless ( exists( $options->{'payinfo'} ) ) { + $options->{'payinfo'} = $self->payinfo; + $options->{'paymask'} = $self->paymask; + } # Default invoice number if the customer has exactly one open invoice. if( ! $options->{'invnum'} ) { @@ -889,7 +892,7 @@ sub _realtime_bop_result { '_date' => '', 'payby' => $cust_pay_pending->payby, 'payinfo' => $options{'payinfo'}, - 'paymask' => $options{'paymask'}, + 'paymask' => $options{'paymask'} || $cust_pay_pending->paymask, 'paydate' => $cust_pay_pending->paydate, 'pkgnum' => $cust_pay_pending->pkgnum, 'discount_term' => $options{'discount_term'}, diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm index d9ae0d39e..4d06862d6 100644 --- a/FS/FS/cust_pay.pm +++ b/FS/FS/cust_pay.pm @@ -2,9 +2,9 @@ package FS::cust_pay; use strict; use base qw( FS::otaker_Mixin FS::payinfo_transaction_Mixin FS::cust_main_Mixin - FS::Record ); + FS::reason_Mixin FS::Record); use vars qw( $DEBUG $me $conf @encrypted_fields - $unsuspendauto $ignore_noapply + $unsuspendauto $ignore_noapply ); use Date::Format; use Business::CreditCard; @@ -24,6 +24,8 @@ use FS::cust_pkg; use FS::cust_pay_void; use FS::upgrade_journal; use FS::Cursor; +use FS::reason; +use FS::reason_type; $DEBUG = 0; @@ -438,6 +440,15 @@ adds a record of the voided payment to the FS::cust_pay_void table. sub void { my $self = shift; + my $reason = shift; + + unless (ref($reason) || !$reason) { + $reason = FS::reason->new_or_existing( + 'class' => 'X', + 'type' => 'Void payment', + 'reason' => $reason + ); + } local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -453,7 +464,7 @@ sub void { my $cust_pay_void = new FS::cust_pay_void ( { map { $_ => $self->get($_) } $self->fields } ); - $cust_pay_void->reason(shift) if scalar(@_); + $cust_pay_void->reasonnum($reason->reasonnum) if $reason; my $error = $cust_pay_void->insert; my $cust_pay_pending = @@ -1023,6 +1034,8 @@ sub _upgrade_data { #class method warn "$me upgrading $class\n" if $DEBUG; + $class->_upgrade_reasonnum(%opt); + local $FS::payinfo_Mixin::ignore_masked_payinfo = 1; ## diff --git a/FS/FS/cust_pay_void.pm b/FS/FS/cust_pay_void.pm index b2f777b32..8fd539616 100644 --- a/FS/FS/cust_pay_void.pm +++ b/FS/FS/cust_pay_void.pm @@ -1,6 +1,6 @@ package FS::cust_pay_void; use base qw( FS::otaker_Mixin FS::payinfo_transaction_Mixin FS::cust_main_Mixin - FS::Record ); + FS::reason_Mixin FS::Record ); use strict; use vars qw( @encrypted_fields $otaker_upgrade_kludge ); @@ -88,7 +88,9 @@ Desired pkgnum when using experimental package balances. =item void_date -=item reason +=item reason - a freeform string (deprecated) + +=item reasonnum - Reason for voiding the payment (see L) =back @@ -189,6 +191,7 @@ sub check { || $self->ut_numbern('void_date') || $self->ut_textn('reason') || $self->payinfo_check + || $self->ut_foreign_keyn('reasonnum', 'reason', 'reasonnum') ; return $error if $error; @@ -221,10 +224,18 @@ sub void_access_user { qsearchs('access_user', { 'usernum' => $self->void_usernum } ); } +=item reason + +Returns the text of the associated void reason (see L) for this. + +=cut + # Used by FS::Upgrade to migrate to a new database. sub _upgrade_data { # class method my ($class, %opts) = @_; + $class->_upgrade_reasonnum(%opts); + my $sql = "SELECT usernum FROM access_user WHERE username = ( SELECT history_user FROM h_cust_pay_void WHERE paynum = ? AND history_action = 'insert' ORDER BY history_date LIMIT 1 ) "; my $sth = dbh->prepare($sql) or die dbh->errstr; diff --git a/FS/FS/reason_Mixin.pm b/FS/FS/reason_Mixin.pm index a3975419c..af9aa50e6 100644 --- a/FS/FS/reason_Mixin.pm +++ b/FS/FS/reason_Mixin.pm @@ -6,18 +6,21 @@ use FS::Record qw( qsearch qsearchs dbdef ); use FS::access_user; use FS::UID qw( dbh ); use FS::reason; +use FS::reason_type; our $DEBUG = 0; our $me = '[FS::reason_Mixin]'; =item reason -Returns the text of the associated reason (see L) for this credit. +Returns the text of the associated reason (see L) for this credit / +voided payment / voided invoice. =cut sub reason { - my ($self, $value, %options) = @_; + my $self = shift; + my $reason_text; if ( $self->reasonnum ) { my $reason = FS::reason->by_key($self->reasonnum); @@ -36,61 +39,117 @@ sub reason { # FS::reason->new_or_existing # Used by FS::Upgrade to migrate reason text fields to reasonnum. -sub _upgrade_reasonnum { # class method - my $class = shift; - my $table = $class->table; - - if (defined dbdef->table($table)->column('reason')) { - - warn "$me Checking for unmigrated reasons\n" if $DEBUG; - - my @cust_refunds = qsearch({ 'table' => $table, - 'hashref' => {}, - 'extra_sql' => 'WHERE reason IS NOT NULL', - }); - - if (scalar(grep { $_->getfield('reason') =~ /\S/ } @cust_refunds)) { - warn "$me Found unmigrated reasons\n" if $DEBUG; - my $hashref = { 'class' => 'F', 'type' => 'Legacy' }; - my $reason_type = qsearchs( 'reason_type', $hashref ); - unless ($reason_type) { - $reason_type = new FS::reason_type( $hashref ); - my $error = $reason_type->insert(); - die "$class had error inserting FS::reason_type into database: $error\n" - if $error; - } - - $hashref = { 'reason_type' => $reason_type->typenum, - 'reason' => '(none)' - }; - my $noreason = qsearchs( 'reason', $hashref ); - unless ($noreason) { - $hashref->{'disabled'} = 'Y'; - $noreason = new FS::reason( $hashref ); - my $error = $noreason->insert(); - die "can't insert legacy reason '(none)' into database: $error\n" - if $error; - } - - foreach my $cust_refund ( @cust_refunds ) { - my $reason = $cust_refund->getfield('reason'); - warn "Contemplating reason $reason\n" if $DEBUG > 1; - if ($reason =~ /\S/) { - $cust_refund->reason($reason, 'reason_type' => $reason_type->typenum) - or die "can't insert legacy reason $reason into database\n"; - }else{ - $cust_refund->reasonnum($noreason->reasonnum); +sub _upgrade_reasonnum { # class method + my $class = shift; + my $table = $class->table; + + for my $fieldname (qw(reason void_reason)) { + if ( defined dbdef->table($table)->column($fieldname) + && defined dbdef->table($table)->column( $fieldname . 'num' ) ) + { + + warn "$me Checking for unmigrated reasons\n" if $DEBUG; + + my @legacy_reason_records = qsearch( + { + 'table' => $table, + 'hashref' => {}, + 'extra_sql' => 'WHERE ' . $fieldname . ' IS NOT NULL', + } + ); + + if ( + scalar( + grep { $_->getfield($fieldname) =~ /\S/ } + @legacy_reason_records + ) + ) + { + warn "$me Found unmigrated reasons\n" if $DEBUG; + + my $reason_type = + _upgrade_get_legacy_reason_type( $class, $table ); + my $noreason = _upgrade_get_no_reason( $class, $reason_type ); + + foreach my $record_to_upgrade (@legacy_reason_records) { + my $reason = $record_to_upgrade->getfield($fieldname); + warn "Contemplating reason $reason\n" if $DEBUG > 1; + if ( $reason =~ /\S/ ) { + my $reason = + _upgrade_get_reason( $class, $reason, $reason_type ); + $record_to_upgrade->set( $fieldname . 'num', + $reason->reasonnum ); + } + else { + $record_to_upgrade->set( $fieldname . 'num', + $noreason->reasonnum ); + } + + $record_to_upgrade->setfield( $fieldname, '' ); + my $error = $record_to_upgrade->replace; + + my $primary_key = $record_to_upgrade->primary_key; + warn "*** WARNING: error replacing $fieldname in $class " + . $record_to_upgrade->get($primary_key) + . ": $error ***\n" + if $error; + } + } } + } +} - $cust_refund->setfield('reason', ''); - my $error = $cust_refund->replace; - - warn "*** WARNING: error replacing reason in $class ". - $cust_refund->refundnum. ": $error ***\n" - if $error; - } +# _upgrade_get_legacy_reason_type is class method supposed to be used only +# within the reason_Mixin class which will either find or create a reason_type +sub _upgrade_get_legacy_reason_type { + + my $class = shift; + my $table = shift; + + my $reason_class = + ( $table =~ /void/ ) ? 'X' : 'F'; # see FS::reason_type (%class_name) + my $reason_type_params = { 'class' => $reason_class, 'type' => 'Legacy' }; + my $reason_type = qsearchs( 'reason_type', $reason_type_params ); + unless ($reason_type) { + $reason_type = new FS::reason_type($reason_type_params); + my $error = $reason_type->insert(); + die "$class had error inserting FS::reason_type into database: $error\n" + if $error; } - } + return $reason_type; +} + +# _upgrade_get_no_reason is class method supposed to be used only within the +# reason_Mixin class which will either find or create a default (no reason) +# reason +sub _upgrade_get_no_reason { + + my $class = shift; + my $reason_type = shift; + return _upgrade_get_reason( $class, '(none)', $reason_type ); +} + +# _upgrade_get_reason is class method supposed to be used only within the +# reason_Mixin class which will either find or create a reason +sub _upgrade_get_reason { + + my $class = shift; + my $reason_text = shift; + my $reason_type = shift; + + my $reason_params = { + 'reason_type' => $reason_type->typenum, + 'reason' => $reason_text + }; + my $reason = qsearchs( 'reason', $reason_params ); + unless ($reason) { + $reason_params->{'disabled'} = 'Y'; + $reason = new FS::reason($reason_params); + my $error = $reason->insert(); + die "can't insert legacy reason '$reason_text' into database: $error\n" + if $error; + } + return $reason; } 1; diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm index 17a716712..4042972b4 100644 --- a/FS/FS/reason_type.pm +++ b/FS/FS/reason_type.pm @@ -11,7 +11,7 @@ our %class_name = ( 'R' => 'credit', 'S' => 'suspend', 'F' => 'refund', - 'X' => 'void credit', + 'X' => 'void', # credit/invoice/payment ); our %class_purpose = ( @@ -19,7 +19,7 @@ our %class_purpose = ( 'R' => 'explain why a customer was credited', 'S' => 'explain why a customer package was suspended', 'F' => 'explain why a customer was refunded', - 'X' => 'explain why a credit was voided', + 'X' => 'explain why a transaction was voided', ); =head1 NAME diff --git a/FS/bin/freeside-paymentech-download b/FS/bin/freeside-paymentech-download index 1b2f95175..9a1f609bc 100755 --- a/FS/bin/freeside-paymentech-download +++ b/FS/bin/freeside-paymentech-download @@ -20,7 +20,7 @@ getopts('vta:'); sub log_and_die { my $message = shift; - my $log = FS::Log->new('freeside-paymenttech-download'); + my $log = FS::Log->new('freeside-paymentech-download'); $log->error($message); die $message; } diff --git a/FS/bin/freeside-paymentech-upload b/FS/bin/freeside-paymentech-upload index a6e6a5d28..5ae147d07 100755 --- a/FS/bin/freeside-paymentech-upload +++ b/FS/bin/freeside-paymentech-upload @@ -20,7 +20,7 @@ getopts('avtp:'); sub log_and_die { my $message = shift; - my $log = FS::Log->new('freeside-paymenttech-upload'); + my $log = FS::Log->new('freeside-paymentech-upload'); $log->error($message); die $message; } diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index dcc02c2b1..81c121259 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -721,6 +721,10 @@ if ( $curuser->access_right('Configuration') ) { $config_billing{'separator5'} = ''; #its a separator! $config_billing{'Refund reasons'} = [ $fsurl.'browse/reason.html?class=F', 'Refund reasons explain why a refund was issued.' ]; $config_billing{'Refund reason types'} = [ $fsurl.'browse/reason_type.html?class=F', 'Refund reason types define groups of reasons.' ]; + + $config_billing{'separator6'} = ''; #its a separator! + $config_billing{'Void reasons'} = [ $fsurl.'browse/reason.html?class=X', 'Void reasons explain why a void was issued.' ]; + $config_billing{'Void reason types'} = [ $fsurl.'browse/reason_type.html?class=X', 'Void reason types define groups of reasons.' ]; } #XXX also to be unified diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html index 125874694..37a34baba 100755 --- a/httemplate/elements/tr-select-reason.html +++ b/httemplate/elements/tr-select-reason.html @@ -199,7 +199,7 @@ if ($class eq 'C') { } elsif ($class eq 'F') { $add_access_right = 'Add on-the-fly refund reason'; } elsif ($class eq 'X') { - $add_access_right = 'Add on-the-fly void credit reason'; + $add_access_right = 'Add on-the-fly void reason'; } else { die "illegal class: $class"; } diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html index 7773b0ba9..c0f432b70 100755 --- a/httemplate/misc/process/void-cust_bill.html +++ b/httemplate/misc/process/void-cust_bill.html @@ -1,6 +1,6 @@ %if ( $error ) { % $cgi->param('error', $error); -<% $cgi->redirect(popurl(2). "void-cust_bill.html?". $cgi->query_string ) %> +<% $cgi->redirect(popurl(2). "void-cust_bill.cgi?". $cgi->query_string ) %> %} else { <& /elements/header-popup.html, 'Invoice voided' &> + + +%} else { +<& /elements/header-popup.html, mt('Void credit') &> + +<& /elements/error.html &> + +

<% mt('Void this credit?') |h %> + +

+ + + +<& /elements/tr-select-reason.html, + 'field' => 'reasonnum', + 'reason_class' => 'X', + 'cgi' => $cgi +&> +
+ +
+

+ +         +" onClick="parent.cClick();"> + +

+ + + +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Void credit'); + +#untaint crednum +my $crednum = $cgi->param('crednum'); +if ($crednum) { + $crednum =~ /^(\d+)$/ || die "Illegal crednum"; +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)/ || die "Illegal crednum"; + $crednum = $1; +} + +my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum}) || die "Credit not found"; + +my $success = 0; +if ($cgi->param('confirm_void_credit')) { + + #untaint reasonnum / create new reason + my ($reasonnum, $error) = $m->comp('process/elements/reason'); + if (!$reasonnum) { + $error = 'Reason required'; + } else { + my $reason = qsearchs('reason', { 'reasonnum' => $reasonnum }) + || die "Reason num $reasonnum not found in database"; + $error = $cust_credit->void($reason) unless $error; + } + + if ($error) { + $cgi->param('error',$error); + } else { + $success = 1; + } +} + + diff --git a/httemplate/misc/void-cust_credit.html b/httemplate/misc/void-cust_credit.html deleted file mode 100755 index 1e71f0030..000000000 --- a/httemplate/misc/void-cust_credit.html +++ /dev/null @@ -1,74 +0,0 @@ -%if ( $success ) { -<& /elements/header-popup.html, mt("Credit voided") &> - - - -%} else { -<& /elements/header-popup.html, mt('Void credit') &> - -<& /elements/error.html &> - -

<% mt('Void this credit?') |h %> - -

- - - -<& /elements/tr-select-reason.html, - 'field' => 'reasonnum', - 'reason_class' => 'X', - 'cgi' => $cgi -&> -
- -
-

- -         -" onClick="parent.cClick();"> - -

- - - -%} -<%init> - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Void credit'); - -#untaint crednum -my $crednum = $cgi->param('crednum'); -if ($crednum) { - $crednum =~ /^(\d+)$/ || die "Illegal crednum"; -} else { - my($query) = $cgi->keywords; - $query =~ /^(\d+)/ || die "Illegal crednum"; - $crednum = $1; -} - -my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum}) || die "Credit not found"; - -my $success = 0; -if ($cgi->param('confirm_void_credit')) { - - #untaint reasonnum / create new reason - my ($reasonnum, $error) = $m->comp('process/elements/reason'); - if (!$reasonnum) { - $error = 'Reason required'; - } else { - my $reason = qsearchs('reason', { 'reasonnum' => $reasonnum }) - || die "Reason num $reasonnum not found in database"; - $error = $cust_credit->void($reason) unless $error; - } - - if ($error) { - $cgi->param('error',$error); - } else { - $success = 1; - } -} - - diff --git a/httemplate/misc/void-cust_pay.cgi b/httemplate/misc/void-cust_pay.cgi index 31b7a6201..784bb9a94 100755 --- a/httemplate/misc/void-cust_pay.cgi +++ b/httemplate/misc/void-cust_pay.cgi @@ -1,16 +1,52 @@ -%if ( $error ) { -% errorpage($error); +%if ( $success ) { +<& /elements/header-popup.html, mt("Payment voided") &> + + + %} else { -<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %> +<& /elements/header-popup.html, mt('Void payment') &> + +<& /elements/error.html &> + +

<% mt('Void this payment?') |h %> + +

+ + + +<& /elements/tr-select-reason.html, + 'field' => 'reasonnum', + 'reason_class' => 'X', + 'cgi' => $cgi +&> +
+ +
+

+ +         +" onClick="parent.cClick();"> + +

+ + + %} <%init> #untaint paynum -my($query) = $cgi->keywords; -$query =~ /^(\d+)$/ || die "Illegal paynum"; -my $paynum = $1; +my $paynum = $cgi->param('paynum'); +if ($paynum) { + $paynum =~ /^(\d+)$/ || die "Illegal paynum"; +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)/ || die "Illegal paynum"; + $paynum = $1; +} -my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum}); +my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum}) || die "Payment not found"; my $right = 'Void payments'; $right = 'Credit card void' if $cust_pay->payby eq 'CARD'; @@ -19,8 +55,24 @@ $right = 'Echeck void' if $cust_pay->payby eq 'CHEK'; die "access denied" unless $FS::CurrentUser::CurrentUser->access_right($right); -my $custnum = $cust_pay->custnum; +my $success = 0; +if ($cgi->param('confirm_void_payment')) { + + #untaint reasonnum / create new reason + my ($reasonnum, $error) = $m->comp('process/elements/reason'); + if (!$reasonnum) { + $error = 'Reason required'; + } else { + my $reason = qsearchs('reason', { 'reasonnum' => $reasonnum }) + || die "Reason num $reasonnum not found in database"; + $error = $cust_pay->void($reason) unless $error; + } -my $error = $cust_pay->void; + if ($error) { + $cgi->param('error',$error); + } else { + $success = 1; + } +} diff --git a/httemplate/search/report_tax-xls.cgi b/httemplate/search/report_tax-xls.cgi index 07fcf7cfe..c914d5adc 100755 --- a/httemplate/search/report_tax-xls.cgi +++ b/httemplate/search/report_tax-xls.cgi @@ -24,8 +24,9 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { $agentname = $agent->agentname; } -if ( $cgi->param('taxname') =~ /^([\w ]+)$/ ) { - $params{taxname} = $1; +# allow anything in here; FS::Report::Tax will treat it as unsafe +if ( length($cgi->param('taxname')) ) { + $params{taxname} = $cgi->param('taxname'); } else { die "taxname required"; } diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi index 8884ddea4..6d139747a 100755 --- a/httemplate/view/cust_bill.cgi +++ b/httemplate/view/cust_bill.cgi @@ -9,13 +9,30 @@ function areyousure(href, message) { } -% if ( !$cust_bill->closed && $curuser->access_right('Void invoices') ) { +% 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, + 'action' => $p.'misc/void-cust_bill.cgi?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 diff --git a/httemplate/view/cust_main/payment_history/credit.html b/httemplate/view/cust_main/payment_history/credit.html index db2e5e582..81be1cd7a 100644 --- a/httemplate/view/cust_main/payment_history/credit.html +++ b/httemplate/view/cust_main/payment_history/credit.html @@ -130,7 +130,7 @@ my $void = ''; $void = ' ('. include( '/elements/popup_link.html', 'label' => emt('void'), - 'action' => "${p}misc/void-cust_credit.html?". + 'action' => "${p}misc/void-cust_credit.cgi?". $cust_credit->crednum, 'actionlabel' => emt('Void credit'), ). diff --git a/httemplate/view/cust_main/payment_history/invoice.html b/httemplate/view/cust_main/payment_history/invoice.html index 613936e00..ca59c15fd 100644 --- a/httemplate/view/cust_main/payment_history/invoice.html +++ b/httemplate/view/cust_main/payment_history/invoice.html @@ -27,7 +27,7 @@ if ( $cust_bill->closed !~ /^Y/i && $opt{'Void invoices'} ) { $void = ' ('. include('/elements/popup_link.html', 'label' => emt('void'), - 'action' => "${p}misc/void-cust_bill.html?;invnum=". + 'action' => "${p}misc/void-cust_bill.cgi?;invnum=". $cust_bill->invnum, 'actionlabel' => emt('Void Invoice'), ). diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html index d72e34b38..77013140e 100644 --- a/httemplate/view/cust_main/payment_history/payment.html +++ b/httemplate/view/cust_main/payment_history/payment.html @@ -169,8 +169,9 @@ if ( $cust_pay->closed !~ /^Y/i && scalar(@refund_right) ) { my $refundtitle = ($cust_pay->payby =~ /^(CARD|CHEK)$/) - ? emt('Send a refund for this payment to the payment gateway') - : emt('Record a refund for this payment'); + ? emt('Send a refund for this payment to the payment gateway') + : emt('Record a refund for this payment'); + $refund = qq! (payby =~ /^(CARD|CHEK|TOKN)$/ ? ' (' . emt('do not send anything to the payment gateway').')' : ''; -$void = areyousure_link("${p}misc/void-cust_pay.cgi?".$cust_pay->paynum, - emt('Are you sure you want to void this payment?'), - emt('Void this payment from the database') . $voidmsg, - emt('void') - ) +$void = ' ('. + include( '/elements/popup_link.html', + 'label' => emt('void'), + 'action' => "${p}misc/void-cust_pay.cgi?".$cust_pay->paynum, + 'actionlabel' => emt('Void payment'), + ). + ')' if $cust_pay->closed !~ /^Y/i && ( ( $cust_pay->payby eq 'CARD' && $opt{'Credit card void'} ) || ( $cust_pay->payby eq 'CHEK' && $opt{'Echeck void'} ) diff --git a/httemplate/view/cust_main/payment_history/voided_invoice.html b/httemplate/view/cust_main/payment_history/voided_invoice.html index ea61f8446..ff4d12f58 100644 --- a/httemplate/view/cust_main/payment_history/voided_invoice.html +++ b/httemplate/view/cust_main/payment_history/voided_invoice.html @@ -6,7 +6,7 @@ % } % my $reason = $cust_bill_void->reason; % if ($reason) { - (<% $reason %>) + (<% $reason |h %>) % } <% mt("on [_1]", time2str($date_format, $cust_bill_void->void_date) ) |h %> diff --git a/httemplate/view/cust_main/payment_history/voided_payment.html b/httemplate/view/cust_main/payment_history/voided_payment.html index 5c43c91e5..e295f9b3b 100644 --- a/httemplate/view/cust_main/payment_history/voided_payment.html +++ b/httemplate/view/cust_main/payment_history/voided_payment.html @@ -6,7 +6,7 @@ % } % my $reason = $cust_pay_void->reason; % if ($reason) { - (<% $reason %>) + (<% $reason |h %>) % } <% mt("on [_1]", time2str($date_format, $cust_pay_void->void_date) ) |h %>