diff --git a/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java b/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java index 063f342..cf28c9b 100644 --- a/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java +++ b/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java @@ -10,7 +10,7 @@ public interface CallbackNotifier { void sendDisputePoolingExpired(Dispute dispute); - void sendDisputeReadyForCreateAdjustment(List disputes); + void sendDisputesReadyForCreateAdjustment(List disputes); void sendDisputeFailedReviewRequired(Dispute dispute, String errorCode, String errorDescription); diff --git a/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java b/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java index 6c9fdbd..4799b99 100644 --- a/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java +++ b/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java @@ -34,13 +34,13 @@ public class DisputesTgBotCallbackNotifierImpl implements CallbackNotifier { } @Override - public void sendDisputeReadyForCreateAdjustment(List disputes) { + public void sendDisputesReadyForCreateAdjustment(List disputes) { var disputeReadyForCreateAdjustments = disputes.stream() .map(Dispute::getId) .map(UUID::toString) .map(DisputeReadyForCreateAdjustment::new) .toList(); - disputesTgBotService.sendDisputeReadyForCreateAdjustment(disputeReadyForCreateAdjustments); + disputesTgBotService.sendDisputesReadyForCreateAdjustment(disputeReadyForCreateAdjustments); } @Override diff --git a/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java b/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java index b09691b..64c8e2f 100644 --- a/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java +++ b/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java @@ -26,7 +26,7 @@ public class DummyCallbackNotifierImpl implements CallbackNotifier { } @Override - public void sendDisputeReadyForCreateAdjustment(List disputes) { + public void sendDisputesReadyForCreateAdjustment(List disputes) { log.debug("Trying to call DummyCallbackNotifierImpl.sendDisputeReadyForCreateAdjustment() {}", disputes.size()); } diff --git a/src/main/java/dev/vality/disputes/api/controller/ErrorControllerAdvice.java b/src/main/java/dev/vality/disputes/api/controller/ErrorControllerAdvice.java index e1a99d4..191ad99 100644 --- a/src/main/java/dev/vality/disputes/api/controller/ErrorControllerAdvice.java +++ b/src/main/java/dev/vality/disputes/api/controller/ErrorControllerAdvice.java @@ -1,6 +1,7 @@ package dev.vality.disputes.api.controller; import dev.vality.disputes.exception.AuthorizationException; +import dev.vality.disputes.exception.InvoicingPaymentStatusPendingException; import dev.vality.disputes.exception.NotFoundException; import dev.vality.disputes.exception.TokenKeeperException; import dev.vality.swag.disputes.model.GeneralError; @@ -35,6 +36,14 @@ public class ErrorControllerAdvice { // ----------------- 4xx ----------------------------------------------------- + @ExceptionHandler({InvoicingPaymentStatusPendingException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Object handleInvalidMimeTypeException(InvoicingPaymentStatusPendingException e) { + log.warn("<- Res [400]: Payment has non-final status", e); + return new GeneralError() + .message("Payment has non-final status"); + } + @ExceptionHandler({InvalidMimeTypeException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public Object handleInvalidMimeTypeException(InvalidMimeTypeException e) { diff --git a/src/main/java/dev/vality/disputes/exception/InvoicingPaymentStatusPendingException.java b/src/main/java/dev/vality/disputes/exception/InvoicingPaymentStatusPendingException.java index 6911a7f..c2f0237 100644 --- a/src/main/java/dev/vality/disputes/exception/InvoicingPaymentStatusPendingException.java +++ b/src/main/java/dev/vality/disputes/exception/InvoicingPaymentStatusPendingException.java @@ -5,4 +5,7 @@ public class InvoicingPaymentStatusPendingException extends RuntimeException { public InvoicingPaymentStatusPendingException(Throwable cause) { super(cause); } + + public InvoicingPaymentStatusPendingException() { + } } diff --git a/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/AdjustmentsReadyNotificationTask.java similarity index 76% rename from src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java rename to src/main/java/dev/vality/disputes/schedule/AdjustmentsReadyNotificationTask.java index f9cb83a..7dff43c 100644 --- a/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java +++ b/src/main/java/dev/vality/disputes/schedule/AdjustmentsReadyNotificationTask.java @@ -2,7 +2,7 @@ package dev.vality.disputes.schedule; import dev.vality.disputes.admin.callback.CallbackNotifier; import dev.vality.disputes.admin.management.MdcTopicProducer; -import dev.vality.disputes.schedule.service.CreateAdjustmentsService; +import dev.vality.disputes.schedule.core.AdjustmentsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -14,18 +14,18 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor @SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"}) -public class TaskReadyForCreateAdjustmentsService { +public class AdjustmentsReadyNotificationTask { - private final CreateAdjustmentsService createAdjustmentsService; + private final AdjustmentsService adjustmentsService; private final CallbackNotifier callbackNotifier; private final MdcTopicProducer mdcTopicProducer; @Scheduled(fixedDelayString = "${dispute.fixedDelayReadyForCreateAdjustments}", initialDelayString = "${dispute.initialDelayReadyForCreateAdjustments}") public void processPending() { log.debug("Processing ReadyForCreateAdjustments get started"); - var disputes = createAdjustmentsService.getReadyDisputesForCreateAdjustment(); + var disputes = adjustmentsService.getReadyDisputesForCreateAdjustment(); mdcTopicProducer.sendReadyForCreateAdjustments(disputes); - callbackNotifier.sendDisputeReadyForCreateAdjustment(disputes); + callbackNotifier.sendDisputesReadyForCreateAdjustment(disputes); log.info("ReadyForCreateAdjustments were processed"); } } diff --git a/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/AdjustmentsTask.java similarity index 80% rename from src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java rename to src/main/java/dev/vality/disputes/schedule/AdjustmentsTask.java index a6b14ae..a3d8e52 100644 --- a/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java +++ b/src/main/java/dev/vality/disputes/schedule/AdjustmentsTask.java @@ -1,8 +1,8 @@ package dev.vality.disputes.schedule; import dev.vality.disputes.domain.tables.pojos.Dispute; -import dev.vality.disputes.schedule.handler.CreateAdjustmentHandler; -import dev.vality.disputes.schedule.service.CreateAdjustmentsService; +import dev.vality.disputes.schedule.core.AdjustmentsService; +import dev.vality.disputes.schedule.handler.AdjustmentHandler; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -20,10 +20,10 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor @SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"}) -public class TaskCreateAdjustmentsService { +public class AdjustmentsTask { private final ExecutorService disputesThreadPool; - private final CreateAdjustmentsService createAdjustmentsService; + private final AdjustmentsService adjustmentsService; @Value("${dispute.batchSize}") private int batchSize; @@ -31,7 +31,7 @@ public class TaskCreateAdjustmentsService { public void processPending() { log.debug("Processing create adjustments get started"); try { - var disputes = createAdjustmentsService.getDisputesForHgCall(batchSize); + var disputes = adjustmentsService.getDisputesForHgCall(batchSize); var callables = disputes.stream() .map(this::handleCreateAdjustment) .collect(Collectors.toList()); @@ -46,6 +46,6 @@ public class TaskCreateAdjustmentsService { } private Callable handleCreateAdjustment(Dispute dispute) { - return () -> new CreateAdjustmentHandler(createAdjustmentsService).handle(dispute); + return () -> new AdjustmentHandler(adjustmentsService).handle(dispute); } } diff --git a/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java b/src/main/java/dev/vality/disputes/schedule/CreatedDisputesTask.java similarity index 94% rename from src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java rename to src/main/java/dev/vality/disputes/schedule/CreatedDisputesTask.java index 34f5575..9adf737 100644 --- a/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java +++ b/src/main/java/dev/vality/disputes/schedule/CreatedDisputesTask.java @@ -1,8 +1,8 @@ package dev.vality.disputes.schedule; import dev.vality.disputes.domain.tables.pojos.Dispute; +import dev.vality.disputes.schedule.core.CreatedDisputesService; import dev.vality.disputes.schedule.handler.CreatedDisputeHandler; -import dev.vality.disputes.schedule.service.CreatedDisputesService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -19,7 +19,7 @@ import java.util.stream.Collectors; @ConditionalOnProperty(value = "dispute.isScheduleCreatedEnabled", havingValue = "true") @Service @RequiredArgsConstructor -public class TaskCreatedDisputesService { +public class CreatedDisputesTask { private final ExecutorService disputesThreadPool; private final CreatedDisputesService createdDisputesService; diff --git a/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java b/src/main/java/dev/vality/disputes/schedule/PendingDisputesTask.java similarity index 94% rename from src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java rename to src/main/java/dev/vality/disputes/schedule/PendingDisputesTask.java index 4d06616..3361697 100644 --- a/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java +++ b/src/main/java/dev/vality/disputes/schedule/PendingDisputesTask.java @@ -1,8 +1,8 @@ package dev.vality.disputes.schedule; import dev.vality.disputes.domain.tables.pojos.Dispute; +import dev.vality.disputes.schedule.core.PendingDisputesService; import dev.vality.disputes.schedule.handler.PendingDisputeHandler; -import dev.vality.disputes.schedule.service.PendingDisputesService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -19,7 +19,7 @@ import java.util.stream.Collectors; @ConditionalOnProperty(value = "dispute.isSchedulePendingEnabled", havingValue = "true") @Service @RequiredArgsConstructor -public class TaskPendingDisputesService { +public class PendingDisputesTask { private final ExecutorService disputesThreadPool; private final PendingDisputesService pendingDisputesService; diff --git a/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentAdjustmentParamsConverter.java b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCapturedAdjustmentParamsConverter.java similarity index 56% rename from src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentAdjustmentParamsConverter.java rename to src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCapturedAdjustmentParamsConverter.java index 965863d..a1574b6 100644 --- a/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentAdjustmentParamsConverter.java +++ b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCapturedAdjustmentParamsConverter.java @@ -1,39 +1,31 @@ package dev.vality.disputes.schedule.converter; -import dev.vality.damsel.domain.*; +import dev.vality.damsel.domain.InvoicePaymentAdjustmentStatusChange; +import dev.vality.damsel.domain.InvoicePaymentCaptured; +import dev.vality.damsel.domain.InvoicePaymentStatus; import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentParams; import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentScenario; import dev.vality.disputes.domain.tables.pojos.Dispute; +import dev.vality.disputes.schedule.service.AdjustmentExtractor; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.Optional; - @Component -public class InvoicePaymentAdjustmentParamsConverter { +@RequiredArgsConstructor +public class InvoicePaymentCapturedAdjustmentParamsConverter { - public static final String DISPUTE_MASK = "disputeId=%s"; + private final AdjustmentExtractor adjustmentExtractor; public InvoicePaymentAdjustmentParams convert(Dispute dispute) { var captured = new InvoicePaymentCaptured(); - var reason = getReason(dispute); + var reason = adjustmentExtractor.getReason(dispute); captured.setReason(reason); - var changedAmount = dispute.getChangedAmount(); - if (changedAmount != null) { - var cost = new Cash(changedAmount, new CurrencyRef(dispute.getCurrencySymbolicCode())); - captured.setCost(cost); - } var params = new InvoicePaymentAdjustmentParams(); params.setReason(reason); params.setScenario(getInvoicePaymentAdjustmentScenario(captured)); return params; } - public String getReason(Dispute dispute) { - return Optional.ofNullable(dispute.getReason()) - .map(s -> String.format(DISPUTE_MASK + ", reason=%s", dispute.getId(), s)) - .orElse(String.format(DISPUTE_MASK, dispute.getId())); - } - private InvoicePaymentAdjustmentScenario getInvoicePaymentAdjustmentScenario(InvoicePaymentCaptured captured) { return InvoicePaymentAdjustmentScenario.status_change(new InvoicePaymentAdjustmentStatusChange( InvoicePaymentStatus.captured(captured))); diff --git a/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCashFlowAdjustmentParamsConverter.java b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCashFlowAdjustmentParamsConverter.java new file mode 100644 index 0000000..0dc96a7 --- /dev/null +++ b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentCashFlowAdjustmentParamsConverter.java @@ -0,0 +1,32 @@ +package dev.vality.disputes.schedule.converter; + +import dev.vality.damsel.domain.InvoicePaymentAdjustmentCashFlow; +import dev.vality.damsel.domain.InvoicePaymentCaptured; +import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentParams; +import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentScenario; +import dev.vality.disputes.domain.tables.pojos.Dispute; +import dev.vality.disputes.schedule.service.AdjustmentExtractor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class InvoicePaymentCashFlowAdjustmentParamsConverter { + + private final AdjustmentExtractor adjustmentExtractor; + + public InvoicePaymentAdjustmentParams convert(Dispute dispute) { + var captured = new InvoicePaymentCaptured(); + var reason = adjustmentExtractor.getReason(dispute); + captured.setReason(reason); + var params = new InvoicePaymentAdjustmentParams(); + params.setReason(reason); + params.setScenario(getInvoicePaymentAdjustmentScenario(dispute.getChangedAmount())); + return params; + } + + private InvoicePaymentAdjustmentScenario getInvoicePaymentAdjustmentScenario(Long changedAmount) { + return InvoicePaymentAdjustmentScenario.cash_flow(new InvoicePaymentAdjustmentCashFlow() + .setNewAmount(changedAmount)); + } +} diff --git a/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentFailedAdjustmentParamsConverter.java b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentFailedAdjustmentParamsConverter.java new file mode 100644 index 0000000..95cc276 --- /dev/null +++ b/src/main/java/dev/vality/disputes/schedule/converter/InvoicePaymentFailedAdjustmentParamsConverter.java @@ -0,0 +1,32 @@ +package dev.vality.disputes.schedule.converter; + +import dev.vality.damsel.domain.*; +import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentParams; +import dev.vality.damsel.payment_processing.InvoicePaymentAdjustmentScenario; +import dev.vality.disputes.domain.tables.pojos.Dispute; +import dev.vality.disputes.schedule.service.AdjustmentExtractor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class InvoicePaymentFailedAdjustmentParamsConverter { + + private final AdjustmentExtractor adjustmentExtractor; + + public InvoicePaymentAdjustmentParams convert(Dispute dispute) { + var invoicePaymentFailed = new InvoicePaymentFailed(); + var reason = adjustmentExtractor.getReason(dispute); + invoicePaymentFailed.setFailure(OperationFailure.failure( + new Failure("failed_by_disputes_api").setReason(reason))); + var params = new InvoicePaymentAdjustmentParams(); + params.setReason(reason); + params.setScenario(getInvoicePaymentAdjustmentScenario(invoicePaymentFailed)); + return params; + } + + private InvoicePaymentAdjustmentScenario getInvoicePaymentAdjustmentScenario(InvoicePaymentFailed failed) { + return InvoicePaymentAdjustmentScenario.status_change(new InvoicePaymentAdjustmentStatusChange( + InvoicePaymentStatus.failed(failed))); + } +} diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/core/AdjustmentsService.java similarity index 55% rename from src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java rename to src/main/java/dev/vality/disputes/schedule/core/AdjustmentsService.java index b10d033..bed028d 100644 --- a/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java +++ b/src/main/java/dev/vality/disputes/schedule/core/AdjustmentsService.java @@ -1,4 +1,4 @@ -package dev.vality.disputes.schedule.service; +package dev.vality.disputes.schedule.core; import dev.vality.damsel.domain.InvoicePaymentAdjustment; import dev.vality.damsel.payment_processing.InvoicePayment; @@ -7,8 +7,10 @@ import dev.vality.disputes.constant.ErrorReason; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; import dev.vality.disputes.domain.tables.pojos.Dispute; -import dev.vality.disputes.exception.InvoicingPaymentStatusPendingException; -import dev.vality.disputes.schedule.converter.InvoicePaymentAdjustmentParamsConverter; +import dev.vality.disputes.schedule.converter.InvoicePaymentCapturedAdjustmentParamsConverter; +import dev.vality.disputes.schedule.converter.InvoicePaymentCashFlowAdjustmentParamsConverter; +import dev.vality.disputes.schedule.converter.InvoicePaymentFailedAdjustmentParamsConverter; +import dev.vality.disputes.schedule.service.AdjustmentExtractor; import dev.vality.disputes.service.external.InvoicingService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,16 +20,19 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Objects; @Slf4j @Service @RequiredArgsConstructor @SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"}) -public class CreateAdjustmentsService { +public class AdjustmentsService { private final DisputeDao disputeDao; private final InvoicingService invoicingService; - private final InvoicePaymentAdjustmentParamsConverter invoicePaymentAdjustmentParamsConverter; + private final InvoicePaymentCapturedAdjustmentParamsConverter invoicePaymentCapturedAdjustmentParamsConverter; + private final InvoicePaymentCashFlowAdjustmentParamsConverter invoicePaymentCashFlowAdjustmentParamsConverter; + private final InvoicePaymentFailedAdjustmentParamsConverter invoicePaymentFailedAdjustmentParamsConverter; private final AdjustmentExtractor adjustmentExtractor; @Transactional(propagation = Propagation.REQUIRED) @@ -57,42 +62,51 @@ public class CreateAdjustmentsService { log.debug("GetDisputeForUpdateSkipLocked has been found {}", dispute); var invoicePayment = getInvoicePayment(dispute); if (invoicePayment == null || !invoicePayment.isSetRoute()) { - log.error("Trying to set failed Dispute status with PAYMENT_NOT_FOUND error reason {}", dispute.getId()); - disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.PAYMENT_NOT_FOUND); - log.debug("Dispute status has been set to failed {}", dispute.getId()); + updateFailed(dispute, ErrorReason.PAYMENT_NOT_FOUND); return; } - var invoicePaymentAdjustment = adjustmentExtractor.searchAdjustmentByDispute(invoicePayment, dispute); - if (invoicePaymentAdjustment.isPresent()) { - var changedAmount = adjustmentExtractor.getChangedAmount(invoicePaymentAdjustment.get(), dispute.getChangedAmount()); - log.info("Trying to set succeeded Dispute status {}", dispute); - disputeDao.update(dispute.getId(), DisputeStatus.succeeded, changedAmount); - log.debug("Dispute status has been set to succeeded {}", dispute.getId()); - return; - } - try { - var params = invoicePaymentAdjustmentParamsConverter.convert(dispute); + if (!adjustmentExtractor.isCashFlowAdjustmentByDisputeExist(invoicePayment, dispute) + && !Objects.equals(dispute.getAmount(), dispute.getChangedAmount())) { + var params = invoicePaymentCashFlowAdjustmentParamsConverter.convert(dispute); var paymentAdjustment = createAdjustment(dispute, params); if (paymentAdjustment == null) { - log.error("Trying to set failed Dispute status with INVOICE_NOT_FOUND error reason {}", dispute.getId()); - disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.INVOICE_NOT_FOUND); - log.debug("Dispute status has been set to failed {}", dispute.getId()); + var errorReason = ErrorReason.INVOICE_NOT_FOUND; + updateFailed(dispute, errorReason); return; } - } catch (InvoicingPaymentStatusPendingException e) { - // в теории 0%, что сюда попадает выполнение кода, но если попадет, то: - // платеж с не финальным статусом будет заблочен для создания корректировок на стороне хелгейта - // и тогда диспут будет пулиться, пока платеж не зафиналится, - // и тк никакой записи в коде выше нет, то пуллинг не проблема - // а запрос в checkDisputeStatus по идемпотентности просто вернет тот же success - log.error("Error when hg.createPaymentAdjustment() got payments status pending {}", dispute.getId(), e); - return; + } else { + log.info("Creating CashFlowAdjustment was skipped {}", dispute); + } + if (!adjustmentExtractor.isCapturedAdjustmentByDisputeExist(invoicePayment, dispute)) { + if (invoicePayment.getPayment().getStatus().isSetCaptured()) { + var params = invoicePaymentFailedAdjustmentParamsConverter.convert(dispute); + var paymentAdjustment = createAdjustment(dispute, params); + if (paymentAdjustment == null) { + updateFailed(dispute, ErrorReason.INVOICE_NOT_FOUND); + return; + } + } + var params = invoicePaymentCapturedAdjustmentParamsConverter.convert(dispute); + var paymentAdjustment = createAdjustment(dispute, params); + if (paymentAdjustment == null) { + updateFailed(dispute, ErrorReason.INVOICE_NOT_FOUND); + return; + } + } else { + log.info("Creating CapturedAdjustment was skipped {}", dispute); } log.info("Trying to set succeeded Dispute status {}", dispute); disputeDao.update(dispute.getId(), DisputeStatus.succeeded); log.debug("Dispute status has been set to succeeded {}", dispute.getId()); } + @Transactional(propagation = Propagation.REQUIRED) + void updateFailed(Dispute dispute, String errorReason) { + log.error("Trying to set failed Dispute status with {} error reason {}", errorReason, dispute.getId()); + disputeDao.update(dispute.getId(), DisputeStatus.failed, errorReason); + log.debug("Dispute status has been set to failed {}", dispute.getId()); + } + private InvoicePaymentAdjustment createAdjustment(Dispute dispute, InvoicePaymentAdjustmentParams params) { return invoicingService.createPaymentAdjustment(dispute.getInvoiceId(), dispute.getPaymentId(), params); } diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java b/src/main/java/dev/vality/disputes/schedule/core/CreatedDisputesService.java similarity index 91% rename from src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java rename to src/main/java/dev/vality/disputes/schedule/core/CreatedDisputesService.java index 484ba33..0abf144 100644 --- a/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java +++ b/src/main/java/dev/vality/disputes/schedule/core/CreatedDisputesService.java @@ -1,4 +1,4 @@ -package dev.vality.disputes.schedule.service; +package dev.vality.disputes.schedule.core; import dev.vality.damsel.payment_processing.InvoicePayment; import dev.vality.disputes.constant.ErrorReason; @@ -11,6 +11,8 @@ import dev.vality.disputes.schedule.client.DefaultRemoteClient; import dev.vality.disputes.schedule.client.RemoteClient; import dev.vality.disputes.schedule.handler.DisputeCreateResultHandler; import dev.vality.disputes.schedule.model.ProviderData; +import dev.vality.disputes.schedule.service.AttachmentsService; +import dev.vality.disputes.schedule.service.ProviderDataService; import dev.vality.disputes.service.external.InvoicingService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -33,7 +35,7 @@ public class CreatedDisputesService { private final RemoteClient remoteClient; private final DisputeDao disputeDao; - private final CreatedAttachmentsService createdAttachmentsService; + private final AttachmentsService attachmentsService; private final InvoicingService invoicingService; private final ProviderDataService providerDataService; private final DefaultRemoteClient defaultRemoteClient; @@ -64,13 +66,7 @@ public class CreatedDisputesService { log.debug("Dispute status has been set to failed {}", dispute.getId()); return; } - var status = invoicePayment.getPayment().getStatus(); - if (!status.isSetCaptured() && !status.isSetCancelled() && !status.isSetFailed()) { - // не создаем диспут, пока платеж не финален - log.warn("Payment has non-final status {} {}", status, dispute.getId()); - return; - } - var attachments = createdAttachmentsService.getAttachments(dispute); + var attachments = attachmentsService.getAttachments(dispute); if (attachments == null || attachments.isEmpty()) { log.error("Trying to set failed Dispute status with NO_ATTACHMENTS error reason {}", dispute.getId()); disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.NO_ATTACHMENTS); @@ -79,7 +75,7 @@ public class CreatedDisputesService { } var providerData = providerDataService.getProviderData(dispute.getProviderId(), dispute.getTerminalId()); var options = providerData.getOptions(); - if ((status.isSetCaptured() && isCapturedBlockedForDispute(options)) + if ((invoicePayment.getPayment().getStatus().isSetCaptured() && isCapturedBlockedForDispute(options)) || isNotProvidersDisputesApiExist(options)) { // отправлять на ручной разбор, если выставлена опция // DISPUTE_FLOW_CAPTURED_BLOCKED или не выставлена DISPUTE_FLOW_PROVIDERS_API_EXIST diff --git a/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java b/src/main/java/dev/vality/disputes/schedule/core/PendingDisputesService.java similarity index 97% rename from src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java rename to src/main/java/dev/vality/disputes/schedule/core/PendingDisputesService.java index 6ed4c7c..31a750b 100644 --- a/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java +++ b/src/main/java/dev/vality/disputes/schedule/core/PendingDisputesService.java @@ -1,4 +1,4 @@ -package dev.vality.disputes.schedule.service; +package dev.vality.disputes.schedule.core; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.dao.ProviderDisputeDao; @@ -10,6 +10,7 @@ import dev.vality.disputes.provider.DisputeStatusResult; import dev.vality.disputes.schedule.catcher.WRuntimeExceptionCatcher; import dev.vality.disputes.schedule.client.RemoteClient; import dev.vality.disputes.schedule.handler.DisputeStatusResultHandler; +import dev.vality.disputes.schedule.service.ProviderDataService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/AdjustmentHandler.java similarity index 75% rename from src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java rename to src/main/java/dev/vality/disputes/schedule/handler/AdjustmentHandler.java index 13ce74e..ce076f1 100644 --- a/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java +++ b/src/main/java/dev/vality/disputes/schedule/handler/AdjustmentHandler.java @@ -1,7 +1,7 @@ package dev.vality.disputes.schedule.handler; import dev.vality.disputes.domain.tables.pojos.Dispute; -import dev.vality.disputes.schedule.service.CreateAdjustmentsService; +import dev.vality.disputes.schedule.core.AdjustmentsService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -9,16 +9,16 @@ import java.util.UUID; @RequiredArgsConstructor @Slf4j -public class CreateAdjustmentHandler { +public class AdjustmentHandler { - private final CreateAdjustmentsService createAdjustmentsService; + private final AdjustmentsService adjustmentsService; public UUID handle(Dispute dispute) { final var currentThread = Thread.currentThread(); final var oldName = currentThread.getName(); currentThread.setName("dispute-created-adjustment-id-" + dispute.getId() + "-" + oldName); try { - createAdjustmentsService.callHgForCreateAdjustment(dispute); + adjustmentsService.callHgForCreateAdjustment(dispute); return dispute.getId(); } catch (Throwable ex) { log.error("Received exception while scheduler processed callHgForCreateAdjustment", ex); diff --git a/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java index d90d605..196c9dc 100644 --- a/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java +++ b/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java @@ -1,7 +1,7 @@ package dev.vality.disputes.schedule.handler; import dev.vality.disputes.domain.tables.pojos.Dispute; -import dev.vality.disputes.schedule.service.CreatedDisputesService; +import dev.vality.disputes.schedule.core.CreatedDisputesService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java index a5a40c3..f3ba4ce 100644 --- a/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java +++ b/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java @@ -58,7 +58,7 @@ public class DisputeStatusResultHandler { @Transactional(propagation = Propagation.REQUIRED) public void handleStatusSuccess(Dispute dispute, DisputeStatusResult result) { - callbackNotifier.sendDisputeReadyForCreateAdjustment(List.of(dispute)); + callbackNotifier.sendDisputesReadyForCreateAdjustment(List.of(dispute)); mdcTopicProducer.sendReadyForCreateAdjustments(List.of(dispute)); var changedAmount = result.getStatusSuccess().getChangedAmount().orElse(null); log.info("Trying to set create_adjustment Dispute status {}, {}", dispute, result); diff --git a/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java index 61cf827..94fdfb1 100644 --- a/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java +++ b/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java @@ -1,7 +1,7 @@ package dev.vality.disputes.schedule.handler; import dev.vality.disputes.domain.tables.pojos.Dispute; -import dev.vality.disputes.schedule.service.PendingDisputesService; +import dev.vality.disputes.schedule.core.PendingDisputesService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/dev/vality/disputes/schedule/service/AdjustmentExtractor.java b/src/main/java/dev/vality/disputes/schedule/service/AdjustmentExtractor.java index e791ef5..47285a4 100644 --- a/src/main/java/dev/vality/disputes/schedule/service/AdjustmentExtractor.java +++ b/src/main/java/dev/vality/disputes/schedule/service/AdjustmentExtractor.java @@ -1,11 +1,10 @@ package dev.vality.disputes.schedule.service; -import dev.vality.damsel.domain.Cash; import dev.vality.damsel.domain.InvoicePaymentAdjustment; import dev.vality.damsel.domain.InvoicePaymentStatus; import dev.vality.damsel.payment_processing.InvoicePayment; import dev.vality.disputes.domain.tables.pojos.Dispute; -import jakarta.annotation.Nonnull; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -13,36 +12,37 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; -import static dev.vality.disputes.schedule.converter.InvoicePaymentAdjustmentParamsConverter.DISPUTE_MASK; - @Component +@RequiredArgsConstructor @SuppressWarnings({"ParameterName", "LineLength"}) public class AdjustmentExtractor { - public Optional searchAdjustmentByDispute(InvoicePayment invoicePayment, Dispute dispute) { - return getInvoicePaymentAdjustmentStream(invoicePayment) - .filter(adj -> adj.getReason() != null) - .filter(adj -> isDisputesAdjustment(adj.getReason(), dispute)) - .findFirst() - .or(() -> getInvoicePaymentAdjustmentStream(invoicePayment) - .filter(s -> s.getState() != null) - .filter(s -> s.getState().isSetStatusChange()) - .filter(s -> getTargetStatus(s).isSetCaptured()) - .filter(s -> getTargetStatus(s).getCaptured().getReason() != null) - .filter(s -> isDisputesAdjustment(getTargetStatus(s).getCaptured().getReason(), dispute)) - .findFirst()); + public static final String DISPUTE_MASK = "disputeId=%s"; + + public String getReason(Dispute dispute) { + return Optional.ofNullable(dispute.getReason()) + .map(s -> String.format(DISPUTE_MASK + ", reason=%s", dispute.getId(), s)) + .orElse(String.format(DISPUTE_MASK, dispute.getId())); } - public Long getChangedAmount(@Nonnull InvoicePaymentAdjustment invoicePaymentAdjustment, Long changedAmount) { - return Optional.of(invoicePaymentAdjustment) - .map(s -> getTargetStatus(s).getCaptured().getCost()) - .map(Cash::getAmount) - .or(() -> Optional.ofNullable(changedAmount)) - .orElse(null); + public boolean isCashFlowAdjustmentByDisputeExist(InvoicePayment invoicePayment, Dispute dispute) { + return getInvoicePaymentAdjustmentStream(invoicePayment) + .filter(adj -> isDisputesAdjustment(adj.getReason(), dispute)) + .anyMatch(adj -> adj.getState() != null && adj.getState().isSetCashFlow()); + } + + public boolean isCapturedAdjustmentByDisputeExist(InvoicePayment invoicePayment, Dispute dispute) { + return getInvoicePaymentAdjustmentStream(invoicePayment) + .filter(adj -> isDisputesAdjustment(adj.getReason(), dispute)) + .filter(adj -> adj.getState() != null && adj.getState().isSetStatusChange()) + .filter(adj -> getTargetStatus(adj).isSetCaptured()) + .anyMatch(adj -> isDisputesAdjustment(getTargetStatus(adj).getCaptured().getReason(), dispute)); } private Stream getInvoicePaymentAdjustmentStream(InvoicePayment invoicePayment) { - return Optional.ofNullable(invoicePayment.getAdjustments()).orElse(List.of()).stream(); + return Optional.ofNullable(invoicePayment.getAdjustments()) + .orElse(List.of()) + .stream(); } private InvoicePaymentStatus getTargetStatus(InvoicePaymentAdjustment s) { @@ -50,6 +50,7 @@ public class AdjustmentExtractor { } private boolean isDisputesAdjustment(String reason, Dispute dispute) { - return !StringUtils.isBlank(reason) && reason.contains(String.format(DISPUTE_MASK, dispute.getId())); + return !StringUtils.isBlank(reason) + && reason.equalsIgnoreCase(getReason(dispute)); } } diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java b/src/main/java/dev/vality/disputes/schedule/service/AttachmentsService.java similarity index 97% rename from src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java rename to src/main/java/dev/vality/disputes/schedule/service/AttachmentsService.java index 80de43c..b163f8f 100644 --- a/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java +++ b/src/main/java/dev/vality/disputes/schedule/service/AttachmentsService.java @@ -16,7 +16,7 @@ import java.util.List; @Service @RequiredArgsConstructor @Slf4j -public class CreatedAttachmentsService { +public class AttachmentsService { private final FileMetaDao fileMetaDao; private final FileStorageService fileStorageService; diff --git a/src/main/java/dev/vality/disputes/security/AccessService.java b/src/main/java/dev/vality/disputes/security/AccessService.java index 503d9b4..56e8c77 100644 --- a/src/main/java/dev/vality/disputes/security/AccessService.java +++ b/src/main/java/dev/vality/disputes/security/AccessService.java @@ -3,6 +3,7 @@ package dev.vality.disputes.security; import dev.vality.damsel.payment_processing.InvoicePayment; import dev.vality.disputes.exception.AuthorizationException; import dev.vality.disputes.exception.BouncerException; +import dev.vality.disputes.exception.InvoicingPaymentStatusPendingException; import dev.vality.disputes.exception.NotFoundException; import dev.vality.disputes.security.service.BouncerService; import dev.vality.disputes.security.service.TokenKeeperService; @@ -93,12 +94,17 @@ public class AccessService { private InvoicePayment getInvoicePayment(dev.vality.damsel.payment_processing.Invoice invoice, String paymentId) { log.debug("Processing invoice: {}", invoice.getInvoice().getId()); - return invoice.getPayments().stream() - .filter(invoicePayment -> paymentId.equals(invoicePayment.getPayment().getId()) - && invoicePayment.isSetRoute()) + var invoicePayment = invoice.getPayments().stream() + .filter(p -> paymentId.equals(p.getPayment().getId()) && p.isSetRoute()) .findFirst() // http 404 .orElseThrow(() -> new NotFoundException( - String.format("Payment with id: %s and filled route not found!", paymentId))); + String.format("Payment with id: %s and filled route and status not found!", paymentId))); + log.debug("Processing payment: {}", invoicePayment); + var status = invoicePayment.getPayment().getStatus(); + if (!status.isSetCaptured() && !status.isSetCancelled() && !status.isSetFailed()) { + throw new InvoicingPaymentStatusPendingException(); + } + return invoicePayment; } } diff --git a/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java b/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java index 2019d77..04b0cc3 100644 --- a/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java +++ b/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java @@ -17,7 +17,7 @@ public interface DisputesTgBotService { void sendDisputePoolingExpired(DisputePoolingExpired disputePoolingExpired); - void sendDisputeReadyForCreateAdjustment(List disputeReadyForCreateAdjustments); + void sendDisputesReadyForCreateAdjustment(List disputeReadyForCreateAdjustments); void sendDisputeFailedReviewRequired(DisputeFailedReviewRequired disputeFailedReviewRequired); diff --git a/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java b/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java index d4c4bd7..3f2047d 100644 --- a/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java +++ b/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java @@ -51,13 +51,13 @@ public class DisputesTgBotServiceImpl implements DisputesTgBotService { @Override @SneakyThrows - public void sendDisputeReadyForCreateAdjustment(List disputeReadyForCreateAdjustments) { - var ids = disputeReadyForCreateAdjustments.stream() + public void sendDisputesReadyForCreateAdjustment(List disputesReadyForCreateAdjustments) { + var ids = disputesReadyForCreateAdjustments.stream() .map(DisputeReadyForCreateAdjustment::getId) .map(String::valueOf) .collect(Collectors.joining(", ")); log.debug("Trying to call adminCallbackDisputesTgBotClient.sendDisputeReadyForCreateAdjustment() {}", ids); - var notifications = disputeReadyForCreateAdjustments.stream() + var notifications = disputesReadyForCreateAdjustments.stream() .map(Notification::disputeReadyForCreateAdjustment) .collect(Collectors.toList()); adminCallbackDisputesTgBotClient.notify(new NotificationParamsRequest(notifications)); diff --git a/src/test/java/dev/vality/disputes/schedule/service/CreateAdjustmentsServiceTest.java b/src/test/java/dev/vality/disputes/schedule/service/AdjustmentsServiceTest.java similarity index 65% rename from src/test/java/dev/vality/disputes/schedule/service/CreateAdjustmentsServiceTest.java rename to src/test/java/dev/vality/disputes/schedule/service/AdjustmentsServiceTest.java index 4a95d37..5539ca0 100644 --- a/src/test/java/dev/vality/disputes/schedule/service/CreateAdjustmentsServiceTest.java +++ b/src/test/java/dev/vality/disputes/schedule/service/AdjustmentsServiceTest.java @@ -7,7 +7,8 @@ import dev.vality.disputes.config.WireMockSpringBootITest; import dev.vality.disputes.constant.ErrorReason; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; -import dev.vality.disputes.schedule.converter.InvoicePaymentAdjustmentParamsConverter; +import dev.vality.disputes.schedule.converter.InvoicePaymentCapturedAdjustmentParamsConverter; +import dev.vality.disputes.schedule.core.AdjustmentsService; import dev.vality.disputes.schedule.service.config.DisputeApiTestService; import dev.vality.disputes.schedule.service.config.PendingDisputesTestService; import dev.vality.disputes.util.MockUtil; @@ -19,7 +20,8 @@ import org.springframework.context.annotation.Import; import java.util.List; import java.util.UUID; -import static dev.vality.disputes.util.MockUtil.getInvoicePaymentAdjustment; +import static dev.vality.disputes.util.MockUtil.getCapturedInvoicePaymentAdjustment; +import static dev.vality.disputes.util.MockUtil.getCashFlowInvoicePaymentAdjustment; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -27,20 +29,22 @@ import static org.mockito.Mockito.when; @WireMockSpringBootITest @Import({PendingDisputesTestService.class}) @SuppressWarnings({"ParameterName", "LineLength"}) -public class CreateAdjustmentsServiceTest { +public class AdjustmentsServiceTest { @Autowired private InvoicingSrv.Iface invoicingClient; @Autowired private DisputeDao disputeDao; @Autowired - private CreateAdjustmentsService createAdjustmentsService; + private AdjustmentsService adjustmentsService; @Autowired private DisputeApiTestService disputeApiTestService; @Autowired private PendingDisputesTestService pendingDisputesTestService; @Autowired - private InvoicePaymentAdjustmentParamsConverter invoicePaymentAdjustmentParamsConverter; + private InvoicePaymentCapturedAdjustmentParamsConverter invoicePaymentCapturedAdjustmentParamsConverter; + @Autowired + private AdjustmentExtractor adjustmentExtractor; @Test @SneakyThrows @@ -50,40 +54,17 @@ public class CreateAdjustmentsServiceTest { var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId()); disputeDao.update(disputeId, DisputeStatus.create_adjustment); var dispute = disputeDao.get(disputeId); - createAdjustmentsService.callHgForCreateAdjustment(dispute.get()); + adjustmentsService.callHgForCreateAdjustment(dispute.get()); assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus()); assertEquals(ErrorReason.PAYMENT_NOT_FOUND, disputeDao.get(disputeId).get().getErrorMessage()); } - @Test - @SneakyThrows - public void testDisputesAdjustmentExist() { - var invoiceId = "20McecNnWoy"; - var paymentId = "1"; - var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId()); - disputeDao.update(disputeId, DisputeStatus.create_adjustment); - var invoicePayment = MockUtil.createInvoicePayment(paymentId); - invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured())); - var dispute = disputeDao.get(disputeId); - dispute.get().setReason("test adj"); - var adjustmentId = "adjustmentId"; - var invoicePaymentAdjustment = getInvoicePaymentAdjustment(adjustmentId, invoicePaymentAdjustmentParamsConverter.getReason(dispute.get())); - invoicePayment.setAdjustments(List.of(invoicePaymentAdjustment)); - when(invoicingClient.getPayment(any(), any())).thenReturn(invoicePayment); - createAdjustmentsService.callHgForCreateAdjustment(dispute.get()); - assertEquals(DisputeStatus.succeeded, disputeDao.get(disputeId).get().getStatus()); - disputeDao.update(disputeId, DisputeStatus.failed); - } - @Test @SneakyThrows public void testInvoiceNotFound() { var disputeId = pendingDisputesTestService.callPendingDisputeRemotely(); - var paymentId = "1"; - var invoicePayment = MockUtil.createInvoicePayment(paymentId); - invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured())); var dispute = disputeDao.get(disputeId); - createAdjustmentsService.callHgForCreateAdjustment(dispute.get()); + adjustmentsService.callHgForCreateAdjustment(dispute.get()); assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus()); assertEquals(ErrorReason.INVOICE_NOT_FOUND, disputeDao.get(disputeId).get().getErrorMessage()); } @@ -92,16 +73,33 @@ public class CreateAdjustmentsServiceTest { @SneakyThrows public void testFullSuccessFlow() { var disputeId = pendingDisputesTestService.callPendingDisputeRemotely(); - var paymentId = "1"; - var invoicePayment = MockUtil.createInvoicePayment(paymentId); - invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured())); var dispute = disputeDao.get(disputeId); dispute.get().setReason("test adj"); + dispute.get().setChangedAmount(dispute.get().getAmount() + 1); var adjustmentId = "adjustmentId"; - var reason = invoicePaymentAdjustmentParamsConverter.getReason(dispute.get()); + var reason = adjustmentExtractor.getReason(dispute.get()); when(invoicingClient.createPaymentAdjustment(any(), any(), any())) - .thenReturn(getInvoicePaymentAdjustment(adjustmentId, reason)); - createAdjustmentsService.callHgForCreateAdjustment(dispute.get()); + .thenReturn(getCapturedInvoicePaymentAdjustment(adjustmentId, reason)); + adjustmentsService.callHgForCreateAdjustment(dispute.get()); + assertEquals(DisputeStatus.succeeded, disputeDao.get(disputeId).get().getStatus()); + disputeDao.update(disputeId, DisputeStatus.failed); + } + + @Test + @SneakyThrows + public void testDisputesAdjustmentExist() { + var disputeId = pendingDisputesTestService.callPendingDisputeRemotely(); + var dispute = disputeDao.get(disputeId); + dispute.get().setReason("test adj"); + dispute.get().setChangedAmount(dispute.get().getAmount() + 1); + var adjustmentId = "adjustmentId"; + var invoicePayment = MockUtil.createInvoicePayment(dispute.get().getPaymentId()); + invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured())); + invoicePayment.setAdjustments(List.of( + getCapturedInvoicePaymentAdjustment(adjustmentId, adjustmentExtractor.getReason(dispute.get())), + getCashFlowInvoicePaymentAdjustment(adjustmentId, adjustmentExtractor.getReason(dispute.get())))); + when(invoicingClient.getPayment(any(), any())).thenReturn(invoicePayment); + adjustmentsService.callHgForCreateAdjustment(dispute.get()); assertEquals(DisputeStatus.succeeded, disputeDao.get(disputeId).get().getStatus()); disputeDao.update(disputeId, DisputeStatus.failed); } diff --git a/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java b/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java index 389598c..c0149e5 100644 --- a/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java +++ b/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java @@ -8,6 +8,7 @@ import dev.vality.disputes.constant.ErrorReason; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; import dev.vality.disputes.provider.ProviderDisputesServiceSrv; +import dev.vality.disputes.schedule.core.CreatedDisputesService; import dev.vality.disputes.schedule.service.config.CreatedDisputesTestService; import dev.vality.disputes.schedule.service.config.DisputeApiTestService; import dev.vality.disputes.schedule.service.config.WiremockAddressesHolder; @@ -67,19 +68,6 @@ public class CreatedDisputesServiceTest { assertEquals(ErrorReason.PAYMENT_NOT_FOUND, disputeDao.get(disputeId).get().getErrorMessage()); } - @Test - @SneakyThrows - public void testSkipDisputeWhenPaymentNonFinalStatus() { - var invoiceId = "20McecNnWoy"; - var paymentId = "1"; - var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId()); - when(invoicingClient.getPayment(any(), any())).thenReturn(MockUtil.createInvoicePayment(paymentId)); - var dispute = disputeDao.get(disputeId); - createdDisputesService.callCreateDisputeRemotely(dispute.get()); - assertEquals(DisputeStatus.created, disputeDao.get(disputeId).get().getStatus()); - disputeDao.update(disputeId, DisputeStatus.failed); - } - @Test @SneakyThrows public void testNoAttachments() { diff --git a/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java b/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java index 77e80f2..ddd768d 100644 --- a/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java +++ b/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java @@ -4,6 +4,7 @@ import dev.vality.disputes.config.WireMockSpringBootITest; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; import dev.vality.disputes.provider.ProviderDisputesServiceSrv; +import dev.vality.disputes.schedule.core.PendingDisputesService; import dev.vality.disputes.schedule.service.config.CreatedDisputesTestService; import dev.vality.disputes.schedule.service.config.DisputeApiTestService; import dev.vality.disputes.schedule.service.config.PendingDisputesTestService; diff --git a/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java b/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java index 45d9f2a..5c43373 100644 --- a/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java +++ b/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java @@ -6,7 +6,7 @@ import dev.vality.damsel.payment_processing.InvoicingSrv; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; import dev.vality.disputes.provider.ProviderDisputesServiceSrv; -import dev.vality.disputes.schedule.service.CreatedDisputesService; +import dev.vality.disputes.schedule.core.CreatedDisputesService; import dev.vality.disputes.schedule.service.ProviderIfaceBuilder; import dev.vality.disputes.service.external.DominantService; import dev.vality.disputes.util.MockUtil; diff --git a/src/test/java/dev/vality/disputes/schedule/service/config/PendingDisputesTestService.java b/src/test/java/dev/vality/disputes/schedule/service/config/PendingDisputesTestService.java index e9b35f8..0f7c2b9 100644 --- a/src/test/java/dev/vality/disputes/schedule/service/config/PendingDisputesTestService.java +++ b/src/test/java/dev/vality/disputes/schedule/service/config/PendingDisputesTestService.java @@ -3,7 +3,7 @@ package dev.vality.disputes.schedule.service.config; import dev.vality.disputes.dao.DisputeDao; import dev.vality.disputes.domain.enums.DisputeStatus; import dev.vality.disputes.provider.ProviderDisputesServiceSrv; -import dev.vality.disputes.schedule.service.PendingDisputesService; +import dev.vality.disputes.schedule.core.PendingDisputesService; import dev.vality.disputes.schedule.service.ProviderIfaceBuilder; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/dev/vality/disputes/util/MockUtil.java b/src/test/java/dev/vality/disputes/util/MockUtil.java index 652ce86..96221f4 100644 --- a/src/test/java/dev/vality/disputes/util/MockUtil.java +++ b/src/test/java/dev/vality/disputes/util/MockUtil.java @@ -58,7 +58,9 @@ public class MockUtil { BankCard.class)))))) .setCost(new Cash() .setCurrency(new CurrencyRef().setSymbolicCode("RUB"))) - .setStatus(InvoicePaymentStatus.pending(new InvoicePaymentPending()))) + .setStatus(InvoicePaymentStatus.failed( + new InvoicePaymentFailed(OperationFailure.failure( + new Failure("authorization_failed:unknown")))))) .setRoute(new PaymentRoute() .setProvider(DamselUtil.fillRequiredTBaseObject(new ProviderRef(), ProviderRef.class)) .setTerminal(DamselUtil.fillRequiredTBaseObject(new TerminalRef(), TerminalRef.class))) @@ -170,9 +172,10 @@ public class MockUtil { return DisputeStatusResult.statusPending(new DisputeStatusPendingResult()); } - public static InvoicePaymentAdjustment getInvoicePaymentAdjustment(String adjustmentId, String reason) { + public static InvoicePaymentAdjustment getCapturedInvoicePaymentAdjustment(String adjustmentId, String reason) { return new InvoicePaymentAdjustment() .setId(adjustmentId) + .setReason(reason) .setState(InvoicePaymentAdjustmentState.status_change(new InvoicePaymentAdjustmentStatusChangeState() .setScenario(new InvoicePaymentAdjustmentStatusChange() .setTargetStatus(new InvoicePaymentStatus(InvoicePaymentStatus.captured( @@ -180,6 +183,14 @@ public class MockUtil { .setReason(reason))))))); } + public static InvoicePaymentAdjustment getCashFlowInvoicePaymentAdjustment(String adjustmentId, String reason) { + return new InvoicePaymentAdjustment() + .setId(adjustmentId) + .setReason(reason) + .setState(InvoicePaymentAdjustmentState.cash_flow(new InvoicePaymentAdjustmentCashFlowState() + .setScenario(new InvoicePaymentAdjustmentCashFlow().setNewAmount(10L)))); + } + public static Failure createFailure() { Failure failure = new Failure("some_error"); failure.setSub(new SubFailure("some_suberror"));