diff --git a/src/VirtoCommerce.OrdersModule.Data/Handlers/RefundChangedOrderChangedEventHandler.cs b/src/VirtoCommerce.OrdersModule.Data/Handlers/RefundChangedOrderChangedEventHandler.cs index 5590d048..c7a0020a 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Handlers/RefundChangedOrderChangedEventHandler.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Handlers/RefundChangedOrderChangedEventHandler.cs @@ -92,16 +92,25 @@ public virtual async Task ProcessRefundChangesAsync(RefundChangedJobArgument[] j { "IsUpdate", "true" }, }; + var previousStatus = refund.Status; try { var result = await payment.PaymentMethod.RefundProcessPaymentAsync(refundRequest); if (result.IsSuccess) { refund.Status = result.NewRefundStatus.ToString(); + if (!string.IsNullOrEmpty(result.OuterId)) + { + refund.OuterId = result.OuterId; + } + refund.RejectReasonMessage = null; } else { - refund.Status = nameof(RefundStatus.Rejected); + // A failed modification must not invalidate the underlying refund state. + // NewRefundStatus is non-nullable and defaults to Pending, so we cannot distinguish + // "provider didn't set it" from "provider explicitly reports Pending" -- preserve prior state. + refund.Status = previousStatus; refund.RejectReasonMessage = result.ErrorMessage; } @@ -166,10 +175,14 @@ protected virtual RefundChangedJobArgument[] GetJobArgumentsForChangedEntry(Gene protected virtual bool HasRefundFieldChanges(Refund oldRefund, Refund newRefund) { + // Status and OuterId are intentionally NOT tracked here: both are written by the provider response + // (via PaymentFlowService.SaveResultToRefundDocument or this handler), so tracking them would + // re-trigger the handler from its own SaveChangesAsync and cause duplicate provider calls. + // Manual status edits on submitted refunds are blocked at the UI. return oldRefund.Amount != newRefund.Amount || oldRefund.ReasonCode != newRefund.ReasonCode || oldRefund.ReasonMessage != newRefund.ReasonMessage - || oldRefund.OuterId != newRefund.OuterId; + || oldRefund.IsCancelled != newRefund.IsCancelled; } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs index cff2f7a1..125cbe05 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs @@ -242,6 +242,10 @@ protected virtual async Task SaveResultToRefundDocumen if (refundResult.IsSuccess) { refund.Status = refundResult.NewRefundStatus.ToString(); + if (!string.IsNullOrEmpty(refundResult.OuterId)) + { + refund.OuterId = refundResult.OuterId; + } result.RefundStatus = refund.Status; result.Succeeded = true; } @@ -343,6 +347,10 @@ public virtual async Task SaveResultToCaptureDocument if (captureResult.IsSuccess) { capture.Status = nameof(CaptureStatus.Processed); + if (!string.IsNullOrEmpty(captureResult.OuterId)) + { + capture.OuterId = captureResult.OuterId; + } paymentInfo.Payment.Status = captureResult.NewPaymentStatus.ToString(); result.PaymentStatus = paymentInfo.Payment.Status; result.Succeeded = true; diff --git a/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/operation-detail.js b/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/operation-detail.js index f45e7533..eb127325 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/operation-detail.js +++ b/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/operation-detail.js @@ -271,6 +271,11 @@ angular.module('virtoCommerce.orderModule') || blade.currentEntity.cancelledState === 'Completed' || blade.currentEntity.cancelledState === 'Requested'); break; + case 'Refund': + result = !blade.currentEntity.isCancelled + && blade.currentEntity.status !== 'Processed' + && !blade.currentEntity.outerId; + break; default: result = !blade.currentEntity.isCancelled; break; diff --git a/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/refund-detail.html b/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/refund-detail.html index 97161839..dd1efeb1 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/refund-detail.html +++ b/src/VirtoCommerce.OrdersModule.Web/Scripts/blades/refund-detail.html @@ -19,7 +19,7 @@ placeholder="'orders.blades.refund-details.placeholders.status' | translate" setting="'Refund.Status'" ng-model="blade.currentEntity.status" - disabled="blade.isLocked"> + disabled="blade.isLocked || blade.currentEntity.outerId || blade.currentEntity.status === 'Processed'">
diff --git a/tests/VirtoCommerce.OrdersModule.Tests/VirtoCommerce.OrdersModule.Tests.csproj b/tests/VirtoCommerce.OrdersModule.Tests/VirtoCommerce.OrdersModule.Tests.csproj index 18ee62be..82a5904f 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/VirtoCommerce.OrdersModule.Tests.csproj +++ b/tests/VirtoCommerce.OrdersModule.Tests/VirtoCommerce.OrdersModule.Tests.csproj @@ -5,7 +5,7 @@ false - + runtime; build; native; contentfiles; analyzers; buildtransitive all