summaryrefslogtreecommitdiff
path: root/FS/FS
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2016-02-22 17:14:07 -0600
committerJonathan Prykop <jonathan@freeside.biz>2016-02-22 17:14:07 -0600
commitb1b3d6b3c0db38909b6f569e08877d2678587d22 (patch)
tree55ab377b72c5d2e3ebba9653024ceb23d75e3dda /FS/FS
parent308694b3b6219171858f336681f4b265327fdc78 (diff)
RT#39586 Manual check refunds cannot be unapplied [source_paynum field, reason bug fixes, link text]
Diffstat (limited to 'FS/FS')
-rw-r--r--FS/FS/Schema.pm5
-rw-r--r--FS/FS/cust_credit.pm2
-rw-r--r--FS/FS/cust_main/Billing_Realtime.pm17
-rw-r--r--FS/FS/cust_pay.pm4
-rw-r--r--FS/FS/cust_refund.pm24
5 files changed, 37 insertions, 15 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 66575fa13..ff6a9241a 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3061,6 +3061,7 @@ sub tables_hashref {
'paymask', 'varchar', 'NULL', $char_d, '', '',
'paybatch', 'varchar', 'NULL', $char_d, '', '',
'closed', 'char', 'NULL', 1, '', '',
+ 'source_paynum', 'int', 'NULL', '', '', '', # link to cust_payby, to prevent unapply of gateway-generated refunds
# credit card/EFT fields (formerly in paybatch)
'gatewaynum', 'int', 'NULL', '', '', '', # payment_gateway FK
'processor', 'varchar', 'NULL', $char_d, '', '', # module name
@@ -3083,6 +3084,10 @@ sub tables_hashref {
{ columns => [ 'gatewaynum' ],
table => 'payment_gateway',
},
+ { columns => [ 'source_paynum' ],
+ table => 'cust_pay',
+ references => [ 'paynum' ],
+ },
],
},
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index a598b37a3..4be4b177a 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -1045,7 +1045,7 @@ sub refund_to_unapply {
'table' => 'cust_credit_refund',
'hashref' => { 'crednum' => $self->crednum },
'addl_from' => 'LEFT JOIN cust_refund USING (refundnum)',
- 'extra_sql' => "AND (cust_refund.closed = '' OR cust_refund.closed IS NULL)",
+ 'extra_sql' => "AND cust_refund.closed IS NULL AND cust_refund.source_paynum IS NULL",
});
}
diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index 3396ec4a0..747f4af5d 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -1323,14 +1323,14 @@ L<http://420.am/business-onlinepayment> for supported gateways.
Available methods are: I<CC>, I<ECHECK> and I<LEC>
-Available options are: I<amount>, I<reason>, I<paynum>, I<paydate>
+Available options are: I<amount>, I<reasonnum>, I<paynum>, I<paydate>
Most gateways require a reference to an original payment transaction to refund,
so you probably need to specify a I<paynum>.
I<amount> defaults to the original amount of the payment if not specified.
-I<reason> specifies a reason for the refund.
+I<reasonnum> specified an existing refund reason for the refund
I<paydate> specifies the expiration date for a credit card overriding the
value from the customer record or the payment record. Specified as yyyy-mm-dd
@@ -1373,6 +1373,8 @@ sub realtime_refund_bop {
warn " $_ => $options{$_}\n" foreach keys %options;
}
+ return "No reason specified" unless $options{'reasonnum'} =~ /^\d+$/;
+
my %content = ();
###
@@ -1531,7 +1533,12 @@ sub realtime_refund_bop {
if $conf->exists('business-onlinepayment-test_transaction');
$void->submit();
if ( $void->is_success ) {
- my $error = $cust_pay->void($options{'reason'});
+ # specified as a refund reason, but now we want a payment void reason
+ # extract just the reason text, let cust_pay::void handle new_or_existing
+ my $reason = qsearchs('reason',{ 'reasonnum' => $options{'reasonnum'} });
+ my $error;
+ $error = 'Reason could not be loaded' unless $reason;
+ $error = $cust_pay->void($reason->reason) unless $error;
if ( $error ) {
# gah, even with transactions.
my $e = 'WARNING: Card/ACH voided but database not updated - '.
@@ -1652,11 +1659,12 @@ sub realtime_refund_bop {
my $cust_refund = new FS::cust_refund ( {
'custnum' => $self->custnum,
'paynum' => $options{'paynum'},
+ 'source_paynum' => $options{'paynum'},
'refund' => $amount,
'_date' => '',
'payby' => $bop_method2payby{$options{method}},
'payinfo' => $payinfo,
- 'reason' => $options{'reason'} || 'card or ACH refund',
+ 'reasonnum' => $options{'reasonnum'},
'gatewaynum' => $gatewaynum, # may be null
'processor' => $processor,
'auth' => $refund->authorization,
@@ -1665,6 +1673,7 @@ sub realtime_refund_bop {
my $error = $cust_refund->insert;
if ( $error ) {
$cust_refund->paynum(''); #try again with no specific paynum
+ $cust_refund->source_paynum('');
my $error2 = $cust_refund->insert;
if ( $error2 ) {
# gah, even with transactions.
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index af76b8990..620f6c629 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -444,7 +444,7 @@ sub void {
unless (ref($reason) || !$reason) {
$reason = FS::reason->new_or_existing(
- 'class' => 'X',
+ 'class' => 'P',
'type' => 'Void payment',
'reason' => $reason
);
@@ -920,7 +920,7 @@ sub refund_to_unapply {
'table' => 'cust_pay_refund',
'hashref' => { 'paynum' => $self->paynum },
'addl_from' => 'LEFT JOIN cust_refund USING (refundnum)',
- 'extra_sql' => "AND (cust_refund.closed = '' OR cust_refund.closed IS NULL)",
+ 'extra_sql' => "AND cust_refund.closed IS NULL AND cust_refund.source_paynum IS NULL",
});
}
diff --git a/FS/FS/cust_refund.pm b/FS/FS/cust_refund.pm
index efbdceeb0..ced954036 100644
--- a/FS/FS/cust_refund.pm
+++ b/FS/FS/cust_refund.pm
@@ -143,16 +143,23 @@ sub insert {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- unless ($self->reasonnum) {
- my $result = $self->reason( $self->getfield('reason'),
- exists($options{ 'reason_type' })
- ? ('reason_type' => $options{ 'reason_type' })
- : (),
- );
- unless($result) {
+ if (!$self->reasonnum) {
+ my $reason_text = $self->get('reason')
+ or return "reason text or existing reason required";
+ my $reason_type = $options{'reason_type'}
+ or return "reason type required";
+
+ local $@;
+ my $reason = FS::reason->new_or_existing(
+ reason => $reason_text,
+ type => $reason_type,
+ class => 'F',
+ );
+ if ($@) {
$dbh->rollback if $oldAutoCommit;
- return "failed to set reason for $me"; #: ". $dbh->errstr;
+ return "failed to set refund reason: $@";
}
+ $self->set('reasonnum', $reason->reasonnum);
}
$self->setfield('reason', '');
@@ -303,6 +310,7 @@ sub check {
|| $self->ut_numbern('_date')
|| $self->ut_textn('paybatch')
|| $self->ut_enum('closed', [ '', 'Y' ])
+ || $self->ut_foreign_keyn('source_paynum', 'cust_pay', 'paynum')
;
return $error if $error;