From da62d1d7a08d426745f2eca65023110d0f17bdb9 Mon Sep 17 00:00:00 2001 From: Andrea De Rinaldis Date: Thu, 4 Apr 2024 15:22:33 +0200 Subject: [PATCH] [NOD-781] feat: completed debt positions mapping --- .../wispconverter/client/gpd/GPDClient.java | 8 +- .../gpd/model/MultiplePaymentPosition.java | 15 ++ .../advice/GlobalExceptionHandler.java | 2 +- .../exception/AppErrorCodeMessageEnum.java | 5 +- .../wispconverter/service/CacheService.java | 16 +- .../service/ConverterService.java | 3 +- .../service/DebtPositionService.java | 240 ++++++++++++------ .../service/RPTExtractorService.java | 73 ++---- .../service/mapper/CartMapper.java | 9 + .../service/mapper/DebtPositionMapper.java | 7 +- .../service/model/CommonRPTFieldsDTO.java | 4 +- .../model/PaymentNoticeContentDTO.java | 17 ++ .../service/model/RPTContentDTO.java | 2 + src/main/resources/application.properties | 4 +- 14 files changed, 258 insertions(+), 147 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/wispconverter/client/gpd/model/MultiplePaymentPosition.java create mode 100644 src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java diff --git a/src/main/java/it/gov/pagopa/wispconverter/client/gpd/GPDClient.java b/src/main/java/it/gov/pagopa/wispconverter/client/gpd/GPDClient.java index eb5bc1bb..f89fb808 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/client/gpd/GPDClient.java +++ b/src/main/java/it/gov/pagopa/wispconverter/client/gpd/GPDClient.java @@ -1,7 +1,7 @@ package it.gov.pagopa.wispconverter.client.gpd; import feign.FeignException; -import it.gov.pagopa.wispconverter.client.gpd.model.PaymentPosition; +import it.gov.pagopa.wispconverter.client.gpd.model.MultiplePaymentPosition; import it.gov.pagopa.wispconverter.config.client.GPDFeignConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; @@ -19,8 +19,8 @@ public interface GPDClient { maxAttemptsExpression = "${client.retry.max-attempts}", backoff = @Backoff(delayExpression = "${client.retry.max-delay}")) @PostMapping( - value = "${client.gpd.api.insert.path}", + value = "${client.gpd.api.bulk-insert.path}", consumes = MediaType.APPLICATION_JSON_VALUE) - void executeCreation(@PathVariable("organization-fiscal-code") String organizationFiscalCode, - @RequestBody PaymentPosition body); + void executeBulkCreation(@PathVariable("organization-fiscal-code") String organizationFiscalCode, + @RequestBody MultiplePaymentPosition body); } \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/client/gpd/model/MultiplePaymentPosition.java b/src/main/java/it/gov/pagopa/wispconverter/client/gpd/model/MultiplePaymentPosition.java new file mode 100644 index 00000000..12082cf8 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/client/gpd/model/MultiplePaymentPosition.java @@ -0,0 +1,15 @@ +package it.gov.pagopa.wispconverter.client.gpd.model; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@NoArgsConstructor +public class MultiplePaymentPosition implements Serializable { + + private List paymentPositions; + +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java index 348813c7..83feeab2 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java @@ -40,7 +40,7 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { private final MessageSource messageSource; - @Value("${wisp-converter.error-code.uri}") + @Value("${exception.error-code.uri}") private String errorCodeUri; @ExceptionHandler(AppException.class) diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java index 00cf06ee..943c4b17 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java +++ b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java @@ -15,7 +15,10 @@ public enum AppErrorCodeMessageEnum { PARSING_INVALID_XML_NODES(1004, "XML parsing error", "Error while parsing payload. The list of nodes extracted from document must be greater than zero, but currently it is zero.", HttpStatus.BAD_REQUEST), PARSING_INVALID_ZIPPED_PAYLOAD(1005, "ZIP extraction error", "Error while parsing payload. Cannot unzip payload correctly.", HttpStatus.BAD_REQUEST), PARSING_PRIMITIVE_NOT_VALID(1006, "Primitive not valid", "Error while checking primitive. Primitive [{0}] not valid.", HttpStatus.NOT_ACCEPTABLE), - VALIDATION_INVALID_IBANS(1100, "IBANs not valid", "Error while generating debt position for GPD service. The IBAN field must be set if digital stamp is not defined for the transfer.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_MULTIBENEFICIARY_CART(1100, "RPTs not valid", "Error while generating debt position for GPD service. The cart is defined as multi-beneficiary but there are a number of RPTs lower than 2.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_IBANS(1101, "IBANs not valid", "Error while generating debt position for GPD service. The IBAN field must be set if digital stamp is not defined for the transfer.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_DEBTOR(1102, "Debtor subject not valid", "Error while generating debt position for GPD service. The debtor subject information is different between the various RPT of the cart.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_CREDITOR_INSTITUTION(1103, "Creditor institution not valid", "Error while generating debt position for GPD service. The creditor institution information is different between the various RPT of the cart.", HttpStatus.BAD_REQUEST), CONFIGURATION_INVALID_STATION(1200, "Station not valid", "Error while generating cart for Checkout service. No valid station found with code [{0}].", HttpStatus.NOT_FOUND), // --- DB and storage interaction errors --- PERSISTENCE_RPT_NOT_FOUND(2000, "RPT not found", "Error while retrieving RPT. RPT with sessionId [{0}] not found.", HttpStatus.NOT_FOUND), diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java index 06edc8d0..f7b7431e 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java @@ -8,6 +8,7 @@ import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.repository.CacheRepository; import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.PaymentNoticeContentDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -36,16 +37,23 @@ public void storeRequestMappingInCache(CommonRPTFieldsDTO commonRPTFieldsDTO, St try { String idIntermediarioPA = commonRPTFieldsDTO.getCreditorInstitutionBrokerId(); - String noticeNumber = commonRPTFieldsDTO.getNav(); + List noticeNumbers = commonRPTFieldsDTO.getPaymentNotices().stream() + .map(PaymentNoticeContentDTO::getNoticeNumber) + .toList(); + // communicating with APIM policy for caching data for decoupler DecouplerCachingKeys decouplerCachingKeys = DecouplerCachingKeys.builder() - .keys(List.of(String.format(COMPOSITE_TWOVALUES_KEY_TEMPLATE, idIntermediarioPA, noticeNumber))) + .keys(noticeNumbers.stream() + .map(noticeNumber -> String.format(COMPOSITE_TWOVALUES_KEY_TEMPLATE, idIntermediarioPA, noticeNumber)) + .toList()) .build(); this.decouplerCachingClient.storeKeyInCacheByAPIM(decouplerCachingKeys); // save in Redis cache the mapping of the request identifier needed for RT generation in next steps - String requestIDForRTHandling = String.format(CACHING_KEY_TEMPLATE, idIntermediarioPA, noticeNumber); - this.cacheRepository.insert(requestIDForRTHandling, sessionId, this.requestIDMappingTTL); + for (String noticeNumber : noticeNumbers) { + String requestIDForRTHandling = String.format(CACHING_KEY_TEMPLATE, idIntermediarioPA, noticeNumber); + this.cacheRepository.insert(requestIDForRTHandling, sessionId, this.requestIDMappingTTL); + } } catch (FeignException e) { throw new AppException(e, AppErrorCodeMessageEnum.CLIENT_DECOUPLER_CACHING, e.status(), e.getMessage()); diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java index d59cd6d5..ce223178 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java @@ -9,7 +9,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.io.IOException; import java.util.Optional; @Service @@ -28,7 +27,7 @@ public class ConverterService { private final RPTRequestRepository rptRequestRepository; - public String convert(String sessionId) throws IOException { + public String convert(String sessionId) { // get RPT request entity from database RPTRequestEntity rptRequestEntity = getRPTRequestEntity(sessionId); diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java index 098e84ba..7d6ba11c 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java @@ -2,17 +2,14 @@ import feign.FeignException; import it.gov.pagopa.wispconverter.client.gpd.GPDClient; -import it.gov.pagopa.wispconverter.client.gpd.model.PaymentOption; -import it.gov.pagopa.wispconverter.client.gpd.model.PaymentPosition; -import it.gov.pagopa.wispconverter.client.gpd.model.Transfer; -import it.gov.pagopa.wispconverter.client.gpd.model.TransferMetadata; +import it.gov.pagopa.wispconverter.client.gpd.model.*; +import it.gov.pagopa.wispconverter.client.iuvgenerator.IUVGeneratorClient; +import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorRequest; +import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorResponse; import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.service.mapper.DebtPositionMapper; -import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; -import it.gov.pagopa.wispconverter.service.model.DigitalStampDTO; -import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; -import it.gov.pagopa.wispconverter.service.model.TransferDTO; +import it.gov.pagopa.wispconverter.service.model.*; import it.gov.pagopa.wispconverter.service.model.paymentrequest.PaymentRequestDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,7 +18,9 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,6 +31,8 @@ public class DebtPositionService { private final GPDClient gpdClient; + private final IUVGeneratorClient iuvGeneratorClient; + private final DebtPositionMapper mapper; private final Pattern taxonomyPattern = Pattern.compile("([^/]+/[^/]+)/?"); @@ -39,97 +40,188 @@ public class DebtPositionService { @Value("${wisp-converter.poste-italiane.abi-code}") private String posteItalianeABICode; + @Value("${wisp-converter.aux-digit}") + private String auxDigit; + + @Value("${wisp-converter.segregation-code}") + private String segregationCode; + public void createDebtPositions(CommonRPTFieldsDTO rptContentDTOs) { try { // converting RPTs in single payment position - PaymentPosition paymentPosition = extractPaymentPosition(rptContentDTOs); + MultiplePaymentPosition paymentPosition = extractPaymentPositions(rptContentDTOs); // communicating with GPD-core service in order to execute the operation - this.gpdClient.executeCreation(rptContentDTOs.getCreditorInstitutionId(), paymentPosition); + this.gpdClient.executeBulkCreation(rptContentDTOs.getCreditorInstitutionId(), paymentPosition); } catch (FeignException e) { throw new AppException(e, AppErrorCodeMessageEnum.CLIENT_GPD, e.status(), e.getMessage()); } } - private PaymentPosition extractPaymentPosition(CommonRPTFieldsDTO commonRPTFieldsDTO) { + private MultiplePaymentPosition extractPaymentPositions(CommonRPTFieldsDTO commonRPTFieldsDTO) { + + List paymentPositions; + if (Boolean.TRUE.equals(commonRPTFieldsDTO.getIsMultibeneficiary())) { + paymentPositions = extractPaymentPositionsForMultibeneficiary(commonRPTFieldsDTO); + } else { + paymentPositions = extractPaymentPositionsForNonMultibeneficiary(commonRPTFieldsDTO); + } + + MultiplePaymentPosition multiplePaymentPosition = new MultiplePaymentPosition(); + multiplePaymentPosition.setPaymentPositions(paymentPositions); + return multiplePaymentPosition; + } + + private List extractPaymentPositionsForMultibeneficiary(CommonRPTFieldsDTO commonRPTFieldsDTO) { + + if (commonRPTFieldsDTO.getRpts().size() < 2) { + throw new AppException(AppErrorCodeMessageEnum.VALIDATION_INVALID_MULTIBENEFICIARY_CART); + } + RPTContentDTO firstRPTContentDTO = commonRPTFieldsDTO.getRpts().get(0); + + // mapping of transfers + List transfers = new ArrayList<>(); + for (RPTContentDTO rptContentDTO : commonRPTFieldsDTO.getRpts()) { + + PaymentRequestDTO paymentRequestDTO = rptContentDTO.getRpt(); + + int transferIdCounter = 1; + for (TransferDTO transferDTO : paymentRequestDTO.getTransferData().getTransfer()) { + + transfers.add(extractPaymentOptionTransfer(transferDTO, paymentRequestDTO.getDomain().getDomainId(), transferIdCounter)); + transferIdCounter++; + } + } + + // generating notice number and add to common RPT fields + String noticeNumber = getNAVCodeFromIUVGenerator(commonRPTFieldsDTO.getCreditorInstitutionId()); - // - PaymentOption paymentOption = mapper.toPaymentOption(commonRPTFieldsDTO); - paymentOption.setAmount(commonRPTFieldsDTO.getRpts().stream() + // mapping of payment option + Long amount = commonRPTFieldsDTO.getRpts().stream() .map(rptContentDTO -> rptContentDTO.getRpt().getTransferData().getTotalAmount()) .reduce(BigDecimal.valueOf(0L), BigDecimal::add) - .longValue() * 100); - paymentOption.setTransfer(extractPaymentOptionTransfers(commonRPTFieldsDTO)); - // + .longValue() * 100; + PaymentOption paymentOption = mapper.toPaymentOption(firstRPTContentDTO); + paymentOption.setNav(noticeNumber); + paymentOption.setAmount(amount); + paymentOption.setTransfer(transfers); + + // mapping of payment position PaymentPosition paymentPosition = mapper.toPaymentPosition(commonRPTFieldsDTO); + paymentPosition.setIupd(calculateIUPD(commonRPTFieldsDTO.getCreditorInstitutionId())); paymentPosition.setPaymentOption(List.of(paymentOption)); - return paymentPosition; + + // update payment notices to be used for communication with Checkout + commonRPTFieldsDTO.getPaymentNotices().add(PaymentNoticeContentDTO.builder() + .noticeNumber(noticeNumber) + .fiscalCode(firstRPTContentDTO.getRpt().getDomain().getDomainId()) + .amount(amount) + .build()); + + return List.of(paymentPosition); } - private List extractPaymentOptionTransfers(CommonRPTFieldsDTO commonRPTFieldsDTO) { + private List extractPaymentPositionsForNonMultibeneficiary(CommonRPTFieldsDTO commonRPTFieldsDTO) { + List paymentPositions = new LinkedList<>(); - int transferIdCounter = 0; + List paymentNotices = commonRPTFieldsDTO.getPaymentNotices(); - List transfers = new ArrayList<>(); for (RPTContentDTO rptContentDTO : commonRPTFieldsDTO.getRpts()) { - PaymentRequestDTO rpt = rptContentDTO.getRpt(); - - - // organization fiscal code setting - String organizationFiscalCode = getOrganizationFiscalCode(commonRPTFieldsDTO, rpt); - - for (TransferDTO transferDTO : rpt.getTransferData().getTransfer()) { - - // IBAN settings - String iban = transferDTO.getCreditIban(); - //String iban = transferDTO.getDebitIban();; - String postalIban = null; - if (isPostalIBAN(iban)) { - postalIban = iban; - iban = null; - } - - // common definition for the transfer - Transfer transfer = Transfer.builder() - .idTransfer(String.valueOf(++transferIdCounter)) // mandatory - .amount(transferDTO.getAmount().longValue() * 100) // mandatory - .remittanceInformation(transferDTO.getRemittanceInformation()) // mandatory - .category(getTaxonomy(transferDTO)) // mandatory - .transferMetadata(List.of( - TransferMetadata.builder() - .key("DatiSpecificiRiscossione") - .value(transferDTO.getCategory()) - .build() - )) - .build(); - - /* if digital stamp exists, it is a special transfer that does not require IBANs */ - DigitalStampDTO digitalStampDTO = transferDTO.getDigitalStamp(); - if (digitalStampDTO != null) { - transfer.setStamp(mapper.toStamp(digitalStampDTO)); - } - /* - if digital stamp don't exists, it is a common transfer that needs IBAN and, if the cart is multibeneficiary, - needs the explicit setting of the organization fiscal code. - */ - else { - if (iban == null) { - throw new AppException(AppErrorCodeMessageEnum.VALIDATION_INVALID_IBANS); - } - transfer.setIban(iban); - transfer.setPostalIban(postalIban); - transfer.setOrganizationFiscalCode(organizationFiscalCode); - } - - transfers.add(transfer); + PaymentRequestDTO paymentRequestDTO = rptContentDTO.getRpt(); + + // mapping of transfers + int transferIdCounter = 1; + List transfers = new ArrayList<>(); + for (TransferDTO transferDTO : paymentRequestDTO.getTransferData().getTransfer()) { + + transfers.add(extractPaymentOptionTransfer(transferDTO, null, transferIdCounter)); + transferIdCounter++; + } + + // generating notice number and add to common RPT fields + String noticeNumber = getNAVCodeFromIUVGenerator(commonRPTFieldsDTO.getCreditorInstitutionId()); + + // mapping of payment option + Long amount = paymentRequestDTO.getTransferData().getTotalAmount().longValue() * 100; + PaymentOption paymentOption = mapper.toPaymentOption(rptContentDTO); + paymentOption.setAmount(amount); + paymentOption.setNav(noticeNumber); + paymentOption.setTransfer(transfers); + + // mapping of payment position + PaymentPosition paymentPosition = mapper.toPaymentPosition(commonRPTFieldsDTO); + paymentPosition.setIupd(calculateIUPD(commonRPTFieldsDTO.getCreditorInstitutionId())); + paymentPosition.setPaymentOption(List.of(paymentOption)); + paymentPositions.add(paymentPosition); + + // update payment notices to be used for communication with Checkout + paymentNotices.add(PaymentNoticeContentDTO.builder() + .noticeNumber(noticeNumber) + .fiscalCode(paymentRequestDTO.getDomain().getDomainId()) + .amount(amount) + .build()); + } + return paymentPositions; + } + + private String getNAVCodeFromIUVGenerator(String creditorInstitutionCode) { + // generating request body + IUVGeneratorRequest request = IUVGeneratorRequest.builder() + .auxDigit(this.auxDigit) + .segregationCode(this.segregationCode) + .build(); + // communicating with IUV Generator service in order to retrieve response + String navCode; + try { + IUVGeneratorResponse response = this.iuvGeneratorClient.generate(creditorInstitutionCode, request); + navCode = response.getIuv(); + } catch (FeignException e) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_IUVGENERATOR_INVALID_RESPONSE, e.status(), e.getMessage()); + } + return navCode; + } + + + private Transfer extractPaymentOptionTransfer(TransferDTO transferDTO, String organizationFiscalCode, int transferIdCounter) { + + // common definition for the transfer + Transfer transfer = Transfer.builder() + .idTransfer(String.valueOf(transferIdCounter)) + .amount(transferDTO.getAmount().longValue() * 100) + .remittanceInformation(transferDTO.getRemittanceInformation()) + .category(getTaxonomy(transferDTO)) + .transferMetadata(List.of( + TransferMetadata.builder() + .key("DatiSpecificiRiscossione") + .value(transferDTO.getCategory()) + .build() + )) + .build(); + + /* + If digital stamp exists, it is a special transfer that does not require IBANs. + If digital stamp doesn't exists, it is a common transfer that needs IBAN and needs the explicit setting of the organization fiscal code. + */ + DigitalStampDTO digitalStampDTO = transferDTO.getDigitalStamp(); + if (digitalStampDTO != null) { + + transfer.setStamp(mapper.toStamp(digitalStampDTO)); + } else { + + String iban = transferDTO.getCreditIban(); + if (iban == null) { + throw new AppException(AppErrorCodeMessageEnum.VALIDATION_INVALID_IBANS); } + transfer.setIban(iban); + transfer.setPostalIban(isPostalIBAN(iban) ? iban : null); + transfer.setOrganizationFiscalCode(organizationFiscalCode); } - return transfers; + return transfer; } private String getTaxonomy(TransferDTO transferDTO) { @@ -145,7 +237,7 @@ private boolean isPostalIBAN(String iban) { return iban != null && iban.substring(5, 10).equals(posteItalianeABICode); } - private String getOrganizationFiscalCode(CommonRPTFieldsDTO commonRPTFieldsDTO, PaymentRequestDTO rpt) { - return Boolean.TRUE.equals(commonRPTFieldsDTO.getIsMultibeneficiary()) ? rpt.getDomain().getDomainId() : null; + private String calculateIUPD(String creditorInstitutionBroker) { + return "wisp_" + creditorInstitutionBroker + "_" + UUID.randomUUID(); } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java b/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java index 3968d938..9162ffd0 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java @@ -1,15 +1,11 @@ package it.gov.pagopa.wispconverter.service; -import feign.FeignException; import gov.telematici.pagamenti.ws.NodoInviaCarrelloRPT; import gov.telematici.pagamenti.ws.NodoInviaRPT; import gov.telematici.pagamenti.ws.TipoElementoListaRPT; import gov.telematici.pagamenti.ws.ppthead.IntestazioneCarrelloPPT; import gov.telematici.pagamenti.ws.ppthead.IntestazionePPT; import it.gov.digitpa.schemas._2011.pagamenti.CtRichiestaPagamentoTelematico; -import it.gov.pagopa.wispconverter.client.iuvgenerator.IUVGeneratorClient; -import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorRequest; -import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IUVGeneratorResponse; import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.service.mapper.RPTMapper; @@ -20,12 +16,12 @@ import it.gov.pagopa.wispconverter.util.ZipUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.w3c.dom.Element; import org.xmlsoap.schemas.soap.envelope.Envelope; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -37,16 +33,8 @@ public class RPTExtractorService { private final JaxbElementUtil jaxbElementUtil; - private final IUVGeneratorClient iuvGeneratorClient; - private final RPTMapper mapper; - @Value("${wisp-converter.aux-digit}") - private String auxDigit; - - @Value("${wisp-converter.segregation-code}") - private String segregationCode; - public CommonRPTFieldsDTO extractRPTContentDTOs(String primitive, String payload) { Envelope envelope; @@ -76,9 +64,6 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaRPT(Envelope envelo boolean containsDigitalStamp = rpt.getTransferData().getTransfer().stream().anyMatch(transfer -> transfer.getDigitalStamp() != null); return CommonRPTFieldsDTO.builder() - .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoUnivocoVersamento()) - .iuv(rpt.getTransferData().getIuv()) - .nav(getNAVCodeFromIUVGenerator(creditorInstitutionId)) .creditorInstitutionId(creditorInstitutionId) .creditorInstitutionBrokerId(soapHeader.getIdentificativoIntermediarioPA()) .stationId(soapHeader.getIdentificativoStazioneIntermediarioPA()) @@ -87,7 +72,10 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaRPT(Envelope envelo .payerFullName(rpt.getPayer().getName()) .isMultibeneficiary(false) .containsDigitalStamp(containsDigitalStamp) + .paymentNotices(new ArrayList<>()) .rpts(Collections.singletonList(RPTContentDTO.builder() + .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoUnivocoVersamento()) + .iuv(rpt.getTransferData().getIuv()) .rpt(rpt) .containsDigitalStamp(containsDigitalStamp) .build())) @@ -104,7 +92,6 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaCarrelloRPT(Envelop String payerType = null; String payerFiscalCode = null; String fullName = null; - String iuv = null; String streetName = null; String streetNumber = null; String postalCode = null; @@ -119,22 +106,23 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaCarrelloRPT(Envelop // generating RPT PaymentRequestDTO rpt = extractRPT(elementoListaRPT.getRpt()); // validating common fields - creditorInstitutionId = checkUniqueness(creditorInstitutionId, isMultibeneficiary ? + creditorInstitutionId = isMultibeneficiary ? soapHeader.getIdentificativoCarrello().substring(0, 11) : - rpt.getDomain().getDomainId()); - payerType = checkUniqueness(payerType, rpt.getPayer().getSubjectUniqueIdentifier().getType()); - payerFiscalCode = checkUniqueness(payerFiscalCode, rpt.getPayer().getSubjectUniqueIdentifier().getCode()); - fullName = checkUniqueness(fullName, rpt.getPayer().getName()); - iuv = checkUniqueness(iuv, rpt.getTransferData().getIuv()); - streetName = checkUniqueness(streetName, rpt.getPayer().getAddress()); - streetNumber = checkUniqueness(streetNumber, rpt.getPayer().getStreetNumber()); - postalCode = checkUniqueness(postalCode, rpt.getPayer().getPostalCode()); - city = checkUniqueness(city, rpt.getPayer().getCity()); - province = checkUniqueness(province, rpt.getPayer().getProvince()); - nation = checkUniqueness(nation, rpt.getPayer().getNation()); - email = checkUniqueness(email, rpt.getPayer().getEmail()); + checkUniqueness(creditorInstitutionId, rpt.getDomain().getDomainId(), AppErrorCodeMessageEnum.VALIDATION_INVALID_CREDITOR_INSTITUTION); + payerType = checkUniqueness(payerType, rpt.getPayer().getSubjectUniqueIdentifier().getType(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + payerFiscalCode = checkUniqueness(payerFiscalCode, rpt.getPayer().getSubjectUniqueIdentifier().getCode(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + fullName = checkUniqueness(fullName, rpt.getPayer().getName(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + streetName = checkUniqueness(streetName, rpt.getPayer().getAddress(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + streetNumber = checkUniqueness(streetNumber, rpt.getPayer().getStreetNumber(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + postalCode = checkUniqueness(postalCode, rpt.getPayer().getPostalCode(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + city = checkUniqueness(city, rpt.getPayer().getCity(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + province = checkUniqueness(province, rpt.getPayer().getProvince(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + nation = checkUniqueness(nation, rpt.getPayer().getNation(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + email = checkUniqueness(email, rpt.getPayer().getEmail(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); // generating RPT content DTO rptContentDTOs.add(RPTContentDTO.builder() + .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoCarrello()) + .iuv(rpt.getTransferData().getIuv()) .containsDigitalStamp(rpt.getTransferData().getTransfer().stream().anyMatch(transfer -> transfer.getDigitalStamp() != null)) .rpt(rpt) .build()); @@ -142,9 +130,6 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaCarrelloRPT(Envelop return CommonRPTFieldsDTO.builder() .cartId(soapHeader.getIdentificativoCarrello()) - .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoCarrello()) - .iuv(iuv) - .nav(getNAVCodeFromIUVGenerator(creditorInstitutionId)) .creditorInstitutionId(creditorInstitutionId) .creditorInstitutionBrokerId(soapHeader.getIdentificativoIntermediarioPA()) .payerType(payerType) @@ -159,13 +144,14 @@ private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaCarrelloRPT(Envelop .payerEmail(email) .isMultibeneficiary(isMultibeneficiary) .containsDigitalStamp(rptContentDTOs.stream().anyMatch(RPTContentDTO::getContainsDigitalStamp)) + .paymentNotices(new ArrayList<>()) .rpts(rptContentDTOs) .build(); } - private T checkUniqueness(T existingValue, T newValue) { + private T checkUniqueness(T existingValue, T newValue, AppErrorCodeMessageEnum error) { if (existingValue != null && !existingValue.equals(newValue)) { - throw new AppException(AppErrorCodeMessageEnum.GENERIC_ERROR); // TODO to be changed on mapping disambiguation + throw new AppException(error); } return newValue; } @@ -174,21 +160,4 @@ private PaymentRequestDTO extractRPT(byte[] rptBytes) { Element rptElement = this.jaxbElementUtil.convertToRPTElement(rptBytes); return mapper.toPaymentRequestDTO(this.jaxbElementUtil.convertToBean(rptElement, CtRichiestaPagamentoTelematico.class)); } - - private String getNAVCodeFromIUVGenerator(String creditorInstitutionCode) { - // generating request body - IUVGeneratorRequest request = IUVGeneratorRequest.builder() - .auxDigit(this.auxDigit) - .segregationCode(this.segregationCode) - .build(); - // communicating with IUV Generator service in order to retrieve response - String navCode; - try { - IUVGeneratorResponse response = this.iuvGeneratorClient.generate(creditorInstitutionCode, request); - navCode = response.getIuv(); - } catch (FeignException e) { - throw new AppException(AppErrorCodeMessageEnum.CLIENT_IUVGENERATOR_INVALID_RESPONSE, e.status(), e.getMessage()); - } - return navCode; - } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java index 92178f11..c4dd6016 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java @@ -1,7 +1,9 @@ package it.gov.pagopa.wispconverter.service.mapper; import it.gov.pagopa.wispconverter.client.checkout.model.Cart; +import it.gov.pagopa.wispconverter.client.checkout.model.PaymentNotice; import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.PaymentNoticeContentDTO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; @@ -14,4 +16,11 @@ public interface CartMapper { @Mapping(target = "allCCP", constant = "false") @Mapping(source = "stationId", target = "stationId") Cart toCart(CommonRPTFieldsDTO commonRPTFieldsDTO); + + @Mapping(source = "noticeNumber", target = "noticeNumber") + @Mapping(source = "fiscalCode", target = "fiscalCode") + @Mapping(source = "amount", target = "amount") + @Mapping(target = "companyName", constant = "null") + @Mapping(target = "description", constant = "null") + PaymentNotice toPaymentNotice(PaymentNoticeContentDTO paymentNoticeContentDTO); } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java index cac4fe40..c70f8b5b 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java @@ -5,6 +5,7 @@ import it.gov.pagopa.wispconverter.client.gpd.model.Stamp; import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; import it.gov.pagopa.wispconverter.service.model.DigitalStampDTO; +import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; @@ -12,7 +13,6 @@ @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DebtPositionMapper { - @Mapping(source = "iupd", target = "iupd") @Mapping(source = "payerType", target = "type") @Mapping(source = "payerFiscalCode", target = "fiscalCode") @Mapping(source = "payerFullName", target = "fullName") @@ -29,14 +29,13 @@ public interface DebtPositionMapper { PaymentPosition toPaymentPosition(CommonRPTFieldsDTO commonRPTFieldsDTO); @Mapping(source = "iuv", target = "iuv") - @Mapping(source = "nav", target = "nav") - @Mapping(target = "description", expression = "java(null)") + @Mapping(target = "description", constant = "-") @Mapping(target = "isPartialPayment", constant = "false") @Mapping(target = "retentionDate", expression = "java(null)") @Mapping(target = "fee", constant = "0L") @Mapping(target = "notificationFee", constant = "0L") @Mapping(target = "dueDate", expression = "java(java.time.LocalDateTime.now().plusDays(1))") - PaymentOption toPaymentOption(CommonRPTFieldsDTO commonRPTFieldsDTO); + PaymentOption toPaymentOption(RPTContentDTO rptContentDTO); @Mapping(target = "hashDocument", expression = "java(new String(digitalStampDTO.getDocumentHash()))") @Mapping(source = "type", target = "stampType") diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java index 77512ca8..c0ed598b 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java @@ -14,9 +14,6 @@ public class CommonRPTFieldsDTO { private String cartId; - private String iupd; - private String iuv; - private String nav; private String creditorInstitutionId; private String creditorInstitutionBrokerId; private String stationId; @@ -32,5 +29,6 @@ public class CommonRPTFieldsDTO { private String payerEmail; private Boolean isMultibeneficiary; private Boolean containsDigitalStamp; + private List paymentNotices; private List rpts; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java new file mode 100644 index 00000000..d86ed7e0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java @@ -0,0 +1,17 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class PaymentNoticeContentDTO { + + private String noticeNumber; + private String fiscalCode; + private Long amount; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java index 0f239727..ec522164 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java @@ -12,6 +12,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class RPTContentDTO { + private String iupd; + private String iuv; private Boolean containsDigitalStamp; private PaymentRequestDTO rpt; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 893f3bea..1905af5b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -45,7 +45,7 @@ client.retry.max-delay=${CLIENT_RETRY_MAXDELAY:2000} # GPD client configuration client.gpd.host=${CLIENT_GPD_HOST:none} -client.gpd.api.insert.path=/organizations/{organization-fiscal-code}/debtpositions?toPublish=true +client.gpd.api.bulk-insert.path=/organizations/{organization-fiscal-code}/debtpositions/bulk?toPublish=true client.gpd.subscription-key=${CLIENT_GPD_SUBKEY:none} # IUV generator client configuration @@ -68,7 +68,7 @@ client.cache.host=${CLIENT_CACHE_HOST:none} client.cache.subscription-key=${CLIENT_CACHE_SUBKEY:none} # Application domain configuration -wisp-converter.error-code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} +exception.error-code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} wisp-converter.aux-digit=3 wisp-converter.segregation-code=48 wisp-converter.cached-requestid-mapping.ttl.minutes=${CACHED_REQUESTID_MAPPING_TTL_MINUTES:1440}