Skip to content

Commit

Permalink
[PAGOPA-2146] feat(Service): Add recover by IUV
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-ang committed Sep 17, 2024
1 parent 05532e9 commit 2940206
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public class RecoveryReceiptPaymentResponse {

private String iuv;
private String ccp;
}
private String ci;
}
234 changes: 116 additions & 118 deletions src/main/java/it/gov/pagopa/wispconverter/service/RecoveryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

@Service
Expand All @@ -43,10 +45,12 @@ public class RecoveryService {

private static final String STATUS_RT_SEND_SUCCESS = "RT_SEND_SUCCESS";

private static final String MOCK_NOTICE_NUMBER = "348000000000000000";
private static final String CREATED_NEW_PAYMENT_POSITION_IN_GPD = "CREATED_NEW_PAYMENT_POSITION_IN_GPD";

private static final String RECOVERY_VALID_START_DATE = "2024-09-03";

private static final String MOCK_NOTICE_NUMBER = "348000000000000000";

private static final List<String> BUSINESS_PROCESSES = List.of("receipt-ok", "receipt-ko", "ecommerce-hang-timeout-trigger");

private final ReceiptController receiptController;
Expand All @@ -59,88 +63,56 @@ public class RecoveryService {

private final ReService reService;

@Value("${wisp-converter.cached-requestid-mapping.ttl.minutes}")
private Long requestIDMappingTTL;
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

@Value("${wisp-converter.cached-requestid-mapping.ttl.minutes:1440}")
public Long requestIDMappingTTL;

@Value("${wisp-converter.recovery.receipt-generation.wait-time.minutes:60}")
private Long receiptGenerationWaitTime;
public Long receiptGenerationWaitTime;

public void recoverReceiptKO(String creditorInstitution, String iuv, String dateFrom, String dateTo) {
List<ReEventEntity> reEvents = reEventRepository.findByIuvAndOrganizationId(dateFrom, dateTo, iuv, creditorInstitution);
if(!areValidDates(dateFrom, dateTo)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The lower bound cannot be lower than [%s], the upper bound cannot be higher than [%s]",
RECOVERY_VALID_START_DATE, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
}

List<ReEventEntity> rptEvents = reEvents.stream()
.filter(event -> RPT_ACCETTATA_NODO.equals(event.getStatus()))
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();

List<ReEventEntity> rtSendEvents = reEvents.stream()
.filter(event -> RT_SEND_SUCCESS.equals(event.getStatus()))
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();

List<ReEventEntity> cacheEvents = reEvents.stream()
.filter(event -> EVENT_TYPE_FOR_RECEIPTKO_SEARCH.equals(event.getStatus()))
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();
// date validation
if(rptEvents.size() > rtSendEvents.size()) {
String noticeNumber, sessionId;
Long ttlInSeconds = this.requestIDMappingTTL;
// it is required to send a receipt-ko
if(!cacheEvents.isEmpty()) {
ReEventEntity event = cacheEvents.get(cacheEvents.size() - 1);
noticeNumber = event.getNoticeNumber();
sessionId = event.getSessionId();
} else {
ReEventEntity event = rptEvents.get(rptEvents.size() - 1);
sessionId = event.getSessionId();
noticeNumber = MOCK_NOTICE_NUMBER;
ttlInSeconds = 10L;
}
List<ReEventEntity> reEvents = reEventRepository.findByIuvAndOrganizationId(dateFrom, dateTo, iuv, creditorInstitution);
List<ReEventEntity> events = reEvents.stream()
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();

String navToIuvMapping = String.format(DecouplerService.MAP_CACHING_KEY_TEMPLATE, creditorInstitution, noticeNumber);
String iuvToSessionIdMapping = String.format(DecouplerService.CACHING_KEY_TEMPLATE, creditorInstitution, iuv);
Set<String> noRedirectSet = Set.of("RPT_ACCETTATA_NODO", "RPT_PARCHEGGIATA_NODO");
Set<String> breakAfterRedirectSet = Set.of("GENERATED_NAV_FOR_NEW_PAYMENT_POSITION", "CREATED_NEW_PAYMENT_POSITION_IN_GPD",
"GENERATED_CACHE_ABOUT_RPT_FOR_DECOUPLER", "GENERATED_CACHE_ABOUT_RPT_FOR_RT_GENERATION", "SAVED_RPT_IN_CART_RECEIVED_REDIRECT_URL_FROM_CHECKOUT");

this.cacheRepository.insert(navToIuvMapping, iuvToSessionIdMapping, ttlInSeconds, ChronoUnit.SECONDS, true);
this.cacheRepository.insert(iuvToSessionIdMapping, sessionId, ttlInSeconds, ChronoUnit.SECONDS, true);
if(!events.isEmpty()) {
ReEventEntity lastEvent = events.get(0);
String status = lastEvent.getStatus();
String noticeNumber;

try {
MDC.put(Constants.MDC_BUSINESS_PROCESS, "receipt-ko");
generateRE(Constants.PAA_INVIA_RT, null, InternalStepStatus.RT_START_RECONCILIATION_PROCESS, creditorInstitution, iuv, noticeNumber, "NA", sessionId);
String receiptKoRequest = ReceiptDto.builder()
.fiscalCode(creditorInstitution)
.noticeNumber(noticeNumber)
.build()
.toString();
if(noRedirectSet.contains(status)) {
noticeNumber = MOCK_NOTICE_NUMBER;
} else if(breakAfterRedirectSet.contains(status)) {
noticeNumber = lastEvent.getNoticeNumber();
} else return;

this.receiptController.receiptKo(receiptKoRequest);
generateRE(Constants.PAA_INVIA_RT, "Success", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, creditorInstitution, iuv, noticeNumber, "NA", sessionId);
MDC.remove(Constants.MDC_BUSINESS_PROCESS);
} catch (Exception e) {
generateRE(Constants.PAA_INVIA_RT, "Failure", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, creditorInstitution, iuv, noticeNumber, "NA", sessionId);
throw new AppException(e, AppErrorCodeMessageEnum.ERROR, e.getMessage());
}
this.recoverReceiptKO(creditorInstitution, noticeNumber, iuv, lastEvent.getSessionId(), lastEvent.getCcp(), dateFrom, dateTo);
}

}

public RecoveryReceiptResponse recoverReceiptKOForCreditorInstitution(String creditorInstitution, String dateFrom, String dateTo) {

LocalDate lowerLimit = LocalDate.parse(RECOVERY_VALID_START_DATE, DateTimeFormatter.ISO_LOCAL_DATE);
if (LocalDate.parse(dateFrom, DateTimeFormatter.ISO_LOCAL_DATE).isBefore(lowerLimit)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The lower bound cannot be lower than [%s]", RECOVERY_VALID_START_DATE));
}

LocalDate now = LocalDate.now();
LocalDate parse = LocalDate.parse(dateTo, DateTimeFormatter.ISO_LOCAL_DATE);
if (parse.isAfter(now)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The upper bound cannot be higher than [%s]", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
if(!areValidDates(dateFrom, dateTo)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The lower bound cannot be lower than [%s], the upper bound cannot be higher than [%s]",
RECOVERY_VALID_START_DATE, now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
}

LocalDate parse = LocalDate.parse(dateTo, DateTimeFormatter.ISO_LOCAL_DATE);
String dateToRefactored;
if (now.isEqual(parse)) {
ZonedDateTime nowMinusMinutes = ZonedDateTime.now(ZoneOffset.UTC).minusMinutes(receiptGenerationWaitTime);
dateToRefactored = nowMinusMinutes.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
dateToRefactored = nowMinusMinutes.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
log.info("Upper bound forced to {}", dateToRefactored);
} else {
dateToRefactored = dateTo + " 23:59:59";
Expand All @@ -149,12 +121,13 @@ public RecoveryReceiptResponse recoverReceiptKOForCreditorInstitution(String cre

List<RTEntity> receiptRTs = rtRepository.findByOrganizationId(creditorInstitution, dateFrom, dateToRefactored);
List<RecoveryReceiptPaymentResponse> paymentsToReconcile = receiptRTs.stream().map(entity -> RecoveryReceiptPaymentResponse.builder()
.iuv(entity.getIuv())
.ccp(entity.getCcp())
.build())
.toList();
.iuv(entity.getIuv())
.ccp(entity.getCcp())
.ci(entity.getIdDominio())
.build())
.toList();

CompletableFuture<Boolean> executeRecovery = recoverReceiptKOAsync(dateFrom, dateTo, creditorInstitution, paymentsToReconcile);
CompletableFuture<Boolean> executeRecovery = recoverReceiptKOAsync(dateFrom, dateTo, paymentsToReconcile);
executeRecovery
.thenAccept(value -> log.info("Reconciliation for creditor institution [{}] in date range [{}-{}] completed!", creditorInstitution, dateFrom, dateTo))
.exceptionally(e -> {
Expand All @@ -163,65 +136,90 @@ public RecoveryReceiptResponse recoverReceiptKOForCreditorInstitution(String cre
});

return RecoveryReceiptResponse.builder()
.payments(paymentsToReconcile)
.build();
.payments(paymentsToReconcile)
.build();
}

private CompletableFuture<Boolean> recoverReceiptKOAsync(String dateFrom, String dateTo, String creditorInstitution, List<RecoveryReceiptPaymentResponse> paymentsToReconcile) {

return CompletableFuture.supplyAsync(() -> {

for (RecoveryReceiptPaymentResponse payment : paymentsToReconcile) {

String iuv = payment.getIuv();
String ccp = payment.getCcp();

try {
List<ReEventEntity> reEvents = reEventRepository.findByIuvAndOrganizationId(dateFrom, dateTo, iuv, creditorInstitution);

List<ReEventEntity> filteredEvents = reEvents.stream()
.filter(event -> EVENT_TYPE_FOR_RECEIPTKO_SEARCH.equals(event.getStatus()))
.filter(event -> ccp.equals(event.getCcp()))
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();
private boolean areValidDates(String dateFrom, String dateTo) {
LocalDate lowerLimit = LocalDate.parse(RECOVERY_VALID_START_DATE, DateTimeFormatter.ISO_LOCAL_DATE);
if (LocalDate.parse(dateFrom, DateTimeFormatter.ISO_LOCAL_DATE).isBefore(lowerLimit)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The lower bound cannot be lower than [%s]", RECOVERY_VALID_START_DATE));
}

int numberOfEvents = filteredEvents.size();
if (numberOfEvents > 0) {
LocalDate now = LocalDate.now();
LocalDate parse = LocalDate.parse(dateTo, DateTimeFormatter.ISO_LOCAL_DATE);
if (parse.isAfter(now)) {
throw new AppException(AppErrorCodeMessageEnum.ERROR, String.format("The upper bound cannot be higher than [%s]", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
}

ReEventEntity event = filteredEvents.get(numberOfEvents - 1);
String noticeNumber = event.getNoticeNumber();
String sessionId = event.getSessionId();
return true;
}

// search by sessionId, then filter by status=RT_SEND_SUCCESS. If there is zero, then proceed
List<ReEventEntity> reEventsRT = reEventRepository.findBySessionIdAndStatus(dateFrom, dateTo, sessionId, STATUS_RT_SEND_SUCCESS);
private CompletableFuture<Boolean> recoverReceiptKOAsync(String dateFrom, String dateTo, List<RecoveryReceiptPaymentResponse> paymentsToReconcile) {
return CompletableFuture.supplyAsync(() -> recoverReceiptKOByRecoveryPayment(dateFrom, dateTo, paymentsToReconcile));
}

if (reEventsRT.isEmpty()) {
String navToIuvMapping = String.format(DecouplerService.MAP_CACHING_KEY_TEMPLATE, creditorInstitution, noticeNumber);
String iuvToSessionIdMapping = String.format(DecouplerService.CACHING_KEY_TEMPLATE, creditorInstitution, iuv);
this.cacheRepository.insert(navToIuvMapping, iuvToSessionIdMapping, this.requestIDMappingTTL);
this.cacheRepository.insert(iuvToSessionIdMapping, sessionId, this.requestIDMappingTTL);
private boolean recoverReceiptKOByRecoveryPayment(String dateFrom, String dateTo, List<RecoveryReceiptPaymentResponse> paymentsToReconcile) {

MDC.put(Constants.MDC_BUSINESS_PROCESS, "receipt-ko");
generateRE(Constants.PAA_INVIA_RT, null, InternalStepStatus.RT_START_RECONCILIATION_PROCESS, creditorInstitution, iuv, noticeNumber, ccp, sessionId);
String receiptKoRequest = ReceiptDto.builder()
.fiscalCode(creditorInstitution)
.noticeNumber(noticeNumber)
.build()
.toString();
this.receiptController.receiptKo(receiptKoRequest);
generateRE(Constants.PAA_INVIA_RT, "Success", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, creditorInstitution, iuv, noticeNumber, ccp, sessionId);
MDC.remove(Constants.MDC_BUSINESS_PROCESS);
}
}
for (RecoveryReceiptPaymentResponse payment : paymentsToReconcile) {
String iuv = payment.getIuv();
String ccp = payment.getCcp();
String ci = payment.getCi();

} catch (Exception e) {
generateRE(Constants.PAA_INVIA_RT, "Failure", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, creditorInstitution, iuv, null, ccp, null);
throw new AppException(e, AppErrorCodeMessageEnum.ERROR, e.getMessage());
try {
List<ReEventEntity> reEvents = reEventRepository.findByIuvAndOrganizationId(dateFrom, dateTo, iuv, ci);

List<ReEventEntity> filteredEvents = reEvents.stream()
.filter(event -> EVENT_TYPE_FOR_RECEIPTKO_SEARCH.equals(event.getStatus()))
.filter(event -> ccp.equals(event.getCcp()))
.sorted(Comparator.comparing(ReEventEntity::getInsertedTimestamp))
.toList();

int numberOfEvents = filteredEvents.size();
if (numberOfEvents > 0) {
ReEventEntity event = filteredEvents.get(numberOfEvents - 1);
String noticeNumber = event.getNoticeNumber();
String sessionId = event.getSessionId();

log.info("[RECOVERY-MISSING-RT] Recovery with receipt-ko for ci = {}, iuv = {}, ccp = {}, sessionId = {}", ci, iuv, ccp, sessionId);
this.recoverReceiptKO(ci, noticeNumber, iuv, sessionId, ccp, dateFrom, dateTo);
}
} catch (Exception e) {
generateRE(Constants.PAA_INVIA_RT, "Failure", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, ci, iuv, null, ccp, null);
throw new AppException(e, AppErrorCodeMessageEnum.ERROR, e.getMessage());
}
}

return true;
}

return true;
});
// check if there is a successful RT submission, if there isn't prepare cached data and send receipt-ko
public void recoverReceiptKO(String ci, String noticeNumber, String iuv, String sessionId, String ccp, String dateFrom, String dateTo) {
// search by sessionId, then filter by status=RT_SEND_SUCCESS. If there is zero, then proceed
List<ReEventEntity> reEventsRT = reEventRepository.findBySessionIdAndStatus(dateFrom, dateTo, sessionId, STATUS_RT_SEND_SUCCESS);

if (reEventsRT.isEmpty()) {
String navToIuvMapping = String.format(DecouplerService.MAP_CACHING_KEY_TEMPLATE, ci, noticeNumber);
String iuvToSessionIdMapping = String.format(DecouplerService.CACHING_KEY_TEMPLATE, ci, iuv);
this.cacheRepository.insert(navToIuvMapping, iuvToSessionIdMapping, this.requestIDMappingTTL, ChronoUnit.MINUTES,true);
this.cacheRepository.insert(iuvToSessionIdMapping, sessionId, this.requestIDMappingTTL, ChronoUnit.MINUTES,true);

MDC.put(Constants.MDC_BUSINESS_PROCESS, "receipt-ko");
generateRE(Constants.PAA_INVIA_RT, null, InternalStepStatus.RT_START_RECONCILIATION_PROCESS, ci, iuv, noticeNumber, ccp, sessionId);
String receiptKoRequest = ReceiptDto.builder()
.fiscalCode(ci)
.noticeNumber(noticeNumber)
.build()
.toString();
try {
this.receiptController.receiptKo(receiptKoRequest);
} catch (Exception e) {
generateRE(Constants.PAA_INVIA_RT, "Failure", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, ci, iuv, noticeNumber, ccp, sessionId);
throw new AppException(e, AppErrorCodeMessageEnum.ERROR, e.getMessage());
}
generateRE(Constants.PAA_INVIA_RT, "Success", InternalStepStatus.RT_END_RECONCILIATION_PROCESS, ci, iuv, noticeNumber, ccp, sessionId);
MDC.remove(Constants.MDC_BUSINESS_PROCESS);
}
}

private void generateRE(String primitive, String operationStatus, InternalStepStatus status, String domainId, String iuv, String noticeNumber, String ccp, String sessionId) {
Expand All @@ -239,4 +237,4 @@ private void generateRE(String primitive, String operationStatus, InternalStepSt
.build();
reService.addRe(reEvent);
}
}
}

0 comments on commit 2940206

Please sign in to comment.