diff --git a/build.gradle.kts b/build.gradle.kts index f9bf15f..a9087ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,8 +32,8 @@ val springDocOpenApiVersion = "2.7.0" val openApiToolsVersion = "0.2.6" val micrometerVersion = "1.4.1" val postgresJdbcVersion = "42.7.4" - val bouncycastleVersion = "1.79" +val mapStructVersion = "1.6.3" dependencies { implementation("org.springframework.boot:spring-boot-starter") @@ -58,10 +58,20 @@ dependencies { compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") + /** + * Mapstruct + * https://mapstruct.org/ + * mapstruct dependencies must always be placed after the lombok dependency + * or the generated mappers will return an empty object + **/ + implementation("org.mapstruct:mapstruct:$mapStructVersion") + annotationProcessor("org.mapstruct:mapstruct-processor:$mapStructVersion") + // Testing testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.mockito:mockito-core") testImplementation ("org.projectlombok:lombok") + testAnnotationProcessor("org.projectlombok:lombok") testImplementation("com.h2database:h2") } diff --git a/gradle.lockfile b/gradle.lockfile index 4f2c81a..22633b9 100644 --- a/gradle.lockfile +++ b/gradle.lockfile @@ -60,6 +60,8 @@ org.atteo:evo-inflector:1.3=compileClasspath org.bouncycastle:bcprov-jdk18on:1.79=compileClasspath org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath org.jspecify:jspecify:1.0.0=compileClasspath +org.mapstruct:mapstruct-processor:1.6.3=compileClasspath +org.mapstruct:mapstruct:1.6.3=compileClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath org.postgresql:postgresql:42.7.4=compileClasspath org.projectlombok:lombok:1.18.36=compileClasspath diff --git a/openapi/generated.openapi.json b/openapi/generated.openapi.json index 2ee7ebb..e382e53 100644 --- a/openapi/generated.openapi.json +++ b/openapi/generated.openapi.json @@ -2772,6 +2772,70 @@ } } }, + "/debt-positions/{debtPositionId}/finalize-sync-status" : { + "put" : { + "tags" : [ "debt-position" ], + "operationId" : "finalizeSyncStatus", + "parameters" : [ { + "name" : "debtPositionId", + "in" : "path", + "description" : "The ID of the debt position", + "required" : true, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/IupdSyncStatusUpdateDTO" + } + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "Ok", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DebtPositionDTO" + } + } + } + }, + "400" : { + "description" : "Invalid request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DebtPositionErrorDTO" + } + } + } + }, + "500" : { + "description" : "Server Error", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DebtPositionErrorDTO" + } + } + } + } + }, + "security" : [ { + "BearerAuth" : [ ] + } ] + } + }, "/receipts" : { "post" : { "tags" : [ "receipt" ], @@ -2807,6 +2871,16 @@ } } }, + "409" : { + "description" : "Debt position already exists", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ReceiptDTO" + } + } + } + }, "500" : { "description" : "Internal server error", "content" : { @@ -2827,6 +2901,15 @@ "post" : { "tags" : [ "debt-position" ], "operationId" : "createDebtPosition", + "parameters" : [ { + "name" : "massive", + "in" : "query", + "description" : "Indicates if the operation is massive (true) or single (false).", + "required" : false, + "schema" : { + "type" : "boolean" + } + } ], "requestBody" : { "content" : { "application/json" : { @@ -2957,7 +3040,7 @@ } } }, - "DebtPositionType" : { + "Transfer" : { "type" : "object", "properties" : { "creationDate" : { @@ -2971,47 +3054,43 @@ "updateOperatorExternalId" : { "type" : "string" }, - "debtPositionTypeId" : { + "transferId" : { "type" : "integer", "format" : "int64" }, - "brokerId" : { + "installmentId" : { "type" : "integer", "format" : "int64" }, - "code" : { + "orgFiscalCode" : { "type" : "string" }, - "description" : { + "orgName" : { "type" : "string" }, - "orgType" : { - "type" : "string" + "amountCents" : { + "type" : "integer", + "format" : "int64" }, - "macroArea" : { + "remittanceInformation" : { "type" : "string" }, - "serviceType" : { - "type" : "string" + "stamp" : { + "$ref" : "#/components/schemas/Stamp" }, - "collectingReason" : { + "iban" : { "type" : "string" }, - "taxonomyCode" : { + "postalIban" : { "type" : "string" }, - "flagAnonymousFiscalCode" : { - "type" : "boolean" - }, - "flagMandatoryDueDate" : { - "type" : "boolean" - }, - "flagNotifyIo" : { - "type" : "boolean" - }, - "ioTemplateMessage" : { + "category" : { "type" : "string" }, + "transferIndex" : { + "type" : "integer", + "format" : "int64" + }, "_links" : { "$ref" : "#/components/schemas/Links" } @@ -3038,16 +3117,16 @@ } } }, - "PagedModelDebtPositionType" : { + "PagedModelTransfer" : { "type" : "object", "properties" : { "_embedded" : { "type" : "object", "properties" : { - "debtPositionTypes" : { + "transfers" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/DebtPositionType" + "$ref" : "#/components/schemas/Transfer" } } } @@ -3060,6 +3139,20 @@ } } }, + "Stamp" : { + "type" : "object", + "properties" : { + "stampType" : { + "type" : "string" + }, + "stampHashDocument" : { + "type" : "string" + }, + "stampProvincialResidence" : { + "type" : "string" + } + } + }, "DebtPosition" : { "type" : "object", "properties" : { @@ -3085,7 +3178,8 @@ "type" : "string" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "ingestionFlowFileId" : { "type" : "integer", @@ -3141,20 +3235,6 @@ } } }, - "Stamp" : { - "type" : "object", - "properties" : { - "stampType" : { - "type" : "string" - }, - "stampHashDocument" : { - "type" : "string" - }, - "stampProvincialResidence" : { - "type" : "string" - } - } - }, "CollectionModelPaymentOption" : { "type" : "object", "properties" : { @@ -3193,7 +3273,7 @@ } } }, - "DebtPositionTypeOrgOperators" : { + "PaymentOption" : { "type" : "object", "properties" : { "creationDate" : { @@ -3207,32 +3287,51 @@ "updateOperatorExternalId" : { "type" : "string" }, - "debtPositionTypeOrgOperatorId" : { + "paymentOptionId" : { "type" : "integer", "format" : "int64" }, - "debtPositionTypeOrgId" : { + "debtPositionId" : { "type" : "integer", "format" : "int64" }, - "operatorExternalUserId" : { + "totalAmountCents" : { + "type" : "integer", + "format" : "int64" + }, + "status" : { + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] + }, + "multiDebtor" : { + "type" : "boolean" + }, + "dueDate" : { + "type" : "string", + "format" : "date-time" + }, + "description" : { "type" : "string" }, + "paymentOptionType" : { + "type" : "string", + "enum" : [ "SINGLE_INSTALLMENT", "INSTALMENTS", "DOWN_PAYMENT" ] + }, "_links" : { "$ref" : "#/components/schemas/Links" } } }, - "PagedModelDebtPositionTypeOrgOperators" : { + "PagedModelPaymentOption" : { "type" : "object", "properties" : { "_embedded" : { "type" : "object", "properties" : { - "debtPositionTypeOrgOperatorses" : { + "paymentOptions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/DebtPositionTypeOrgOperators" + "$ref" : "#/components/schemas/PaymentOption" } } } @@ -3245,7 +3344,26 @@ } } }, - "DebtPositionTypeOrg" : { + "CollectionModelInstallmentNoPII" : { + "type" : "object", + "properties" : { + "_embedded" : { + "type" : "object", + "properties" : { + "installmentNoPIIs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/InstallmentNoPIIResponse" + } + } + } + }, + "_links" : { + "$ref" : "#/components/schemas/Links" + } + } + }, + "InstallmentNoPII" : { "type" : "object", "properties" : { "creationDate" : { @@ -3259,189 +3377,68 @@ "updateOperatorExternalId" : { "type" : "string" }, - "debtPositionTypeOrgId" : { - "type" : "integer", - "format" : "int64" - }, - "debtPositionTypeId" : { + "installmentId" : { "type" : "integer", "format" : "int64" }, - "organizationId" : { + "paymentOptionId" : { "type" : "integer", "format" : "int64" }, - "balance" : { - "type" : "string" + "status" : { + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, - "code" : { + "iupdPagopa" : { "type" : "string" }, - "description" : { + "iud" : { "type" : "string" }, - "iban" : { + "iuv" : { "type" : "string" }, - "postalIban" : { + "iur" : { "type" : "string" }, - "postalAccountCode" : { + "iuf" : { "type" : "string" }, - "holderPostalCc" : { + "nav" : { "type" : "string" }, - "orgSector" : { - "type" : "string" + "dueDate" : { + "type" : "string", + "format" : "date-time" }, - "xsdDefinitionRef" : { + "paymentTypeCode" : { "type" : "string" }, "amountCents" : { "type" : "integer", "format" : "int64" }, - "externalPaymentUrl" : { + "notificationFeeCents" : { + "type" : "integer", + "format" : "int64" + }, + "remittanceInformation" : { "type" : "string" }, - "flagAnonymousFiscalCode" : { - "type" : "boolean" + "humanFriendlyRemittanceInformation" : { + "type" : "string" }, - "flagMandatoryDueDate" : { - "type" : "boolean" + "balance" : { + "type" : "string" }, - "flagSpontaneous" : { - "type" : "boolean" + "legacyPaymentMetadata" : { + "type" : "string" }, - "flagNotifyIo" : { - "type" : "boolean" + "personalDataId" : { + "type" : "integer", + "format" : "int64" }, - "ioTemplateMessage" : { - "type" : "string" - }, - "flagActive" : { - "type" : "boolean" - }, - "flagNotifyOutcomePush" : { - "type" : "boolean" - }, - "notifyOutcomePushOrgSilServiceId" : { - "type" : "integer", - "format" : "int64" - }, - "flagAmountActualization" : { - "type" : "boolean" - }, - "amountActualizationOrgSilServiceId" : { - "type" : "integer", - "format" : "int64" - }, - "flagExternal" : { - "type" : "boolean" - }, - "_links" : { - "$ref" : "#/components/schemas/Links" - } - } - }, - "PagedModelDebtPositionTypeOrg" : { - "type" : "object", - "properties" : { - "_embedded" : { - "type" : "object", - "properties" : { - "debtPositionTypeOrgs" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/DebtPositionTypeOrg" - } - } - } - }, - "_links" : { - "$ref" : "#/components/schemas/Links" - }, - "page" : { - "$ref" : "#/components/schemas/PageMetadata" - } - } - }, - "InstallmentNoPII" : { - "type" : "object", - "properties" : { - "creationDate" : { - "type" : "string", - "format" : "date-time" - }, - "updateDate" : { - "type" : "string", - "format" : "date-time" - }, - "updateOperatorExternalId" : { - "type" : "string" - }, - "installmentId" : { - "type" : "integer", - "format" : "int64" - }, - "paymentOptionId" : { - "type" : "integer", - "format" : "int64" - }, - "status" : { - "type" : "string" - }, - "iupdPagopa" : { - "type" : "string" - }, - "iud" : { - "type" : "string" - }, - "iuv" : { - "type" : "string" - }, - "iur" : { - "type" : "string" - }, - "iuf" : { - "type" : "string" - }, - "nav" : { - "type" : "string" - }, - "dueDate" : { - "type" : "string", - "format" : "date-time" - }, - "paymentTypeCode" : { - "type" : "string" - }, - "amountCents" : { - "type" : "integer", - "format" : "int64" - }, - "notificationFeeCents" : { - "type" : "integer", - "format" : "int64" - }, - "remittanceInformation" : { - "type" : "string" - }, - "humanFriendlyRemittanceInformation" : { - "type" : "string" - }, - "balance" : { - "type" : "string" - }, - "legacyPaymentMetadata" : { - "type" : "string" - }, - "personalDataId" : { - "type" : "integer", - "format" : "int64" - }, - "debtorEntityType" : { + "debtorEntityType" : { "type" : "string" }, "debtorFiscalCodeHash" : { @@ -3623,7 +3620,7 @@ } } }, - "Transfer" : { + "DebtPositionType" : { "type" : "object", "properties" : { "creationDate" : { @@ -3637,58 +3634,62 @@ "updateOperatorExternalId" : { "type" : "string" }, - "transferId" : { + "debtPositionTypeId" : { "type" : "integer", "format" : "int64" }, - "installmentId" : { + "brokerId" : { "type" : "integer", "format" : "int64" }, - "orgFiscalCode" : { + "code" : { "type" : "string" }, - "orgName" : { + "description" : { "type" : "string" }, - "amountCents" : { - "type" : "integer", - "format" : "int64" - }, - "remittanceInformation" : { + "orgType" : { "type" : "string" }, - "stamp" : { - "$ref" : "#/components/schemas/Stamp" + "macroArea" : { + "type" : "string" }, - "iban" : { + "serviceType" : { "type" : "string" }, - "postalIban" : { + "collectingReason" : { "type" : "string" }, - "category" : { + "taxonomyCode" : { "type" : "string" }, - "transferIndex" : { - "type" : "integer", - "format" : "int64" + "flagAnonymousFiscalCode" : { + "type" : "boolean" + }, + "flagMandatoryDueDate" : { + "type" : "boolean" + }, + "flagNotifyIo" : { + "type" : "boolean" + }, + "ioTemplateMessage" : { + "type" : "string" }, "_links" : { "$ref" : "#/components/schemas/Links" } } }, - "PagedModelTransfer" : { + "PagedModelDebtPositionType" : { "type" : "object", "properties" : { "_embedded" : { "type" : "object", "properties" : { - "transfers" : { + "debtPositionTypes" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/Transfer" + "$ref" : "#/components/schemas/DebtPositionType" } } } @@ -3701,7 +3702,7 @@ } } }, - "PaymentOption" : { + "DebtPositionTypeOrgOperators" : { "type" : "object", "properties" : { "creationDate" : { @@ -3715,50 +3716,32 @@ "updateOperatorExternalId" : { "type" : "string" }, - "paymentOptionId" : { - "type" : "integer", - "format" : "int64" - }, - "debtPositionId" : { + "debtPositionTypeOrgOperatorId" : { "type" : "integer", "format" : "int64" }, - "totalAmountCents" : { + "debtPositionTypeOrgId" : { "type" : "integer", "format" : "int64" }, - "status" : { - "type" : "string" - }, - "multiDebtor" : { - "type" : "boolean" - }, - "dueDate" : { - "type" : "string", - "format" : "date-time" - }, - "description" : { + "operatorExternalUserId" : { "type" : "string" }, - "paymentOptionType" : { - "type" : "string", - "enum" : [ "SINGLE_INSTALLMENT", "INSTALMENTS", "DOWN_PAYMENT" ] - }, "_links" : { "$ref" : "#/components/schemas/Links" } } }, - "PagedModelPaymentOption" : { + "PagedModelDebtPositionTypeOrgOperators" : { "type" : "object", "properties" : { "_embedded" : { "type" : "object", "properties" : { - "paymentOptions" : { + "debtPositionTypeOrgOperatorses" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/PaymentOption" + "$ref" : "#/components/schemas/DebtPositionTypeOrgOperators" } } } @@ -3771,53 +3754,7 @@ } } }, - "CollectionModelInstallmentNoPII" : { - "type" : "object", - "properties" : { - "_embedded" : { - "type" : "object", - "properties" : { - "installmentNoPIIs" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/InstallmentNoPIIResponse" - } - } - } - }, - "_links" : { - "$ref" : "#/components/schemas/Links" - } - } - }, - "DebtPositionTypeOrgOperatorsRequestBody" : { - "type" : "object", - "properties" : { - "creationDate" : { - "type" : "string", - "format" : "date-time" - }, - "updateDate" : { - "type" : "string", - "format" : "date-time" - }, - "updateOperatorExternalId" : { - "type" : "string" - }, - "debtPositionTypeOrgOperatorId" : { - "type" : "integer", - "format" : "int64" - }, - "debtPositionTypeOrgId" : { - "type" : "integer", - "format" : "int64" - }, - "operatorExternalUserId" : { - "type" : "string" - } - } - }, - "DebtPositionTypeOrgRequestBody" : { + "DebtPositionTypeOrg" : { "type" : "object", "properties" : { "creationDate" : { @@ -3911,15 +3848,164 @@ }, "flagExternal" : { "type" : "boolean" + }, + "_links" : { + "$ref" : "#/components/schemas/Links" } } }, - "DebtPositionTypeRequestBody" : { + "PagedModelDebtPositionTypeOrg" : { "type" : "object", "properties" : { - "creationDate" : { - "type" : "string", - "format" : "date-time" + "_embedded" : { + "type" : "object", + "properties" : { + "debtPositionTypeOrgs" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/DebtPositionTypeOrg" + } + } + } + }, + "_links" : { + "$ref" : "#/components/schemas/Links" + }, + "page" : { + "$ref" : "#/components/schemas/PageMetadata" + } + } + }, + "DebtPositionTypeOrgOperatorsRequestBody" : { + "type" : "object", + "properties" : { + "creationDate" : { + "type" : "string", + "format" : "date-time" + }, + "updateDate" : { + "type" : "string", + "format" : "date-time" + }, + "updateOperatorExternalId" : { + "type" : "string" + }, + "debtPositionTypeOrgOperatorId" : { + "type" : "integer", + "format" : "int64" + }, + "debtPositionTypeOrgId" : { + "type" : "integer", + "format" : "int64" + }, + "operatorExternalUserId" : { + "type" : "string" + } + } + }, + "DebtPositionTypeOrgRequestBody" : { + "type" : "object", + "properties" : { + "creationDate" : { + "type" : "string", + "format" : "date-time" + }, + "updateDate" : { + "type" : "string", + "format" : "date-time" + }, + "updateOperatorExternalId" : { + "type" : "string" + }, + "debtPositionTypeOrgId" : { + "type" : "integer", + "format" : "int64" + }, + "debtPositionTypeId" : { + "type" : "integer", + "format" : "int64" + }, + "organizationId" : { + "type" : "integer", + "format" : "int64" + }, + "balance" : { + "type" : "string" + }, + "code" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "iban" : { + "type" : "string" + }, + "postalIban" : { + "type" : "string" + }, + "postalAccountCode" : { + "type" : "string" + }, + "holderPostalCc" : { + "type" : "string" + }, + "orgSector" : { + "type" : "string" + }, + "xsdDefinitionRef" : { + "type" : "string" + }, + "amountCents" : { + "type" : "integer", + "format" : "int64" + }, + "externalPaymentUrl" : { + "type" : "string" + }, + "flagAnonymousFiscalCode" : { + "type" : "boolean" + }, + "flagMandatoryDueDate" : { + "type" : "boolean" + }, + "flagSpontaneous" : { + "type" : "boolean" + }, + "flagNotifyIo" : { + "type" : "boolean" + }, + "ioTemplateMessage" : { + "type" : "string" + }, + "flagActive" : { + "type" : "boolean" + }, + "flagNotifyOutcomePush" : { + "type" : "boolean" + }, + "notifyOutcomePushOrgSilServiceId" : { + "type" : "integer", + "format" : "int64" + }, + "flagAmountActualization" : { + "type" : "boolean" + }, + "amountActualizationOrgSilServiceId" : { + "type" : "integer", + "format" : "int64" + }, + "flagExternal" : { + "type" : "boolean" + } + } + }, + "DebtPositionTypeRequestBody" : { + "type" : "object", + "properties" : { + "creationDate" : { + "type" : "string", + "format" : "date-time" }, "updateDate" : { "type" : "string", @@ -3996,7 +4082,8 @@ "type" : "string" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "ingestionFlowFileId" : { "type" : "integer", @@ -4056,7 +4143,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "iupdPagopa" : { "type" : "string" @@ -4149,7 +4237,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "multiDebtor" : { "type" : "boolean" @@ -4249,7 +4338,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "iupdPagopa" : { "type" : "string" @@ -4336,7 +4426,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "multiDebtor" : { "type" : "boolean" @@ -4511,141 +4602,6 @@ } } }, - "PersonDTO" : { - "required" : [ "email", "entityType", "fiscalCode", "fullName" ], - "type" : "object", - "properties" : { - "entityType" : { - "type" : "string" - }, - "fiscalCode" : { - "type" : "string" - }, - "fullName" : { - "type" : "string" - }, - "address" : { - "type" : "string" - }, - "civic" : { - "type" : "string" - }, - "postalCode" : { - "type" : "string" - }, - "location" : { - "type" : "string" - }, - "province" : { - "type" : "string" - }, - "nation" : { - "type" : "string" - }, - "email" : { - "type" : "string" - } - } - }, - "ReceiptDTO" : { - "type" : "object", - "properties" : { - "receiptId" : { - "type" : "integer", - "format" : "int64" - }, - "installmentId" : { - "type" : "integer", - "format" : "int64" - }, - "paymentReceiptId" : { - "type" : "string" - }, - "noticeNumber" : { - "type" : "string" - }, - "orgFiscalCode" : { - "type" : "string" - }, - "outcome" : { - "type" : "string" - }, - "creditorReferenceId" : { - "type" : "string" - }, - "paymentAmountCents" : { - "type" : "integer", - "format" : "int64" - }, - "description" : { - "type" : "string" - }, - "companyName" : { - "type" : "string" - }, - "officeName" : { - "type" : "string" - }, - "idPsp" : { - "type" : "string" - }, - "pspFiscalCode" : { - "type" : "string" - }, - "pspPartitaIva" : { - "type" : "string" - }, - "pspCompanyName" : { - "type" : "string" - }, - "idChannel" : { - "type" : "string" - }, - "channelDescription" : { - "type" : "string" - }, - "paymentMethod" : { - "type" : "string" - }, - "feeCents" : { - "type" : "integer", - "format" : "int64" - }, - "paymentDateTime" : { - "type" : "string", - "format" : "date-time" - }, - "applicationDate" : { - "type" : "string", - "format" : "date-time" - }, - "transferDate" : { - "type" : "string", - "format" : "date-time" - }, - "receiptBytes" : { - "type" : "string", - "format" : "byte" - }, - "standin" : { - "type" : "boolean" - }, - "debtor" : { - "$ref" : "#/components/schemas/PersonDTO" - }, - "payer" : { - "$ref" : "#/components/schemas/PersonDTO" - }, - "creationDate" : { - "type" : "string", - "format" : "date-time" - }, - "updateDate" : { - "type" : "string", - "format" : "date-time" - } - } - }, "DebtPositionDTO" : { "required" : [ "debtPositionTypeOrgId", "organizationId", "paymentOptions" ], "type" : "object", @@ -4661,7 +4617,8 @@ "type" : "string" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "ingestionFlowFileId" : { "type" : "integer", @@ -4719,7 +4676,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "iupdPagopa" : { "type" : "string" @@ -4802,7 +4760,8 @@ "format" : "int64" }, "status" : { - "type" : "string" + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "PARTIALLY_PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] }, "multiDebtor" : { "type" : "boolean" @@ -4826,6 +4785,42 @@ } } }, + "PersonDTO" : { + "required" : [ "email", "entityType", "fiscalCode", "fullName" ], + "type" : "object", + "properties" : { + "entityType" : { + "type" : "string" + }, + "fiscalCode" : { + "type" : "string" + }, + "fullName" : { + "type" : "string" + }, + "address" : { + "type" : "string" + }, + "civic" : { + "type" : "string" + }, + "postalCode" : { + "type" : "string" + }, + "location" : { + "type" : "string" + }, + "province" : { + "type" : "string" + }, + "nation" : { + "type" : "string" + }, + "email" : { + "type" : "string" + } + } + }, "TransferDTO" : { "required" : [ "amountCents", "category", "orgFiscalCode", "orgName", "remittanceInformation" ], "type" : "object", @@ -4875,6 +4870,130 @@ } } }, + "DebtPositionErrorDTO" : { + "required" : [ "code", "message" ], + "type" : "object", + "properties" : { + "code" : { + "type" : "string", + "enum" : [ "DEBT_POSITION_IVALID_REQUEST", "DEBT_POSITION_GENERIC_ERROR" ] + }, + "message" : { + "type" : "string" + } + } + }, + "IupdSyncStatusUpdateDTO" : { + "type" : "object", + "properties" : { + "newStatus" : { + "type" : "string", + "enum" : [ "TO_SYNC", "REPORTED", "PAID", "CANCELLED", "INVALID", "EXPIRED", "UNPAID" ] + }, + "iupdPagopa" : { + "type" : "string" + } + } + }, + "ReceiptDTO" : { + "type" : "object", + "properties" : { + "receiptId" : { + "type" : "integer", + "format" : "int64" + }, + "installmentId" : { + "type" : "integer", + "format" : "int64" + }, + "paymentReceiptId" : { + "type" : "string" + }, + "noticeNumber" : { + "type" : "string" + }, + "orgFiscalCode" : { + "type" : "string" + }, + "outcome" : { + "type" : "string" + }, + "creditorReferenceId" : { + "type" : "string" + }, + "paymentAmountCents" : { + "type" : "integer", + "format" : "int64" + }, + "description" : { + "type" : "string" + }, + "companyName" : { + "type" : "string" + }, + "officeName" : { + "type" : "string" + }, + "idPsp" : { + "type" : "string" + }, + "pspFiscalCode" : { + "type" : "string" + }, + "pspPartitaIva" : { + "type" : "string" + }, + "pspCompanyName" : { + "type" : "string" + }, + "idChannel" : { + "type" : "string" + }, + "channelDescription" : { + "type" : "string" + }, + "paymentMethod" : { + "type" : "string" + }, + "feeCents" : { + "type" : "integer", + "format" : "int64" + }, + "paymentDateTime" : { + "type" : "string", + "format" : "date-time" + }, + "applicationDate" : { + "type" : "string", + "format" : "date-time" + }, + "transferDate" : { + "type" : "string", + "format" : "date-time" + }, + "receiptBytes" : { + "type" : "string", + "format" : "byte" + }, + "standin" : { + "type" : "boolean" + }, + "debtor" : { + "$ref" : "#/components/schemas/PersonDTO" + }, + "payer" : { + "$ref" : "#/components/schemas/PersonDTO" + }, + "creationDate" : { + "type" : "string", + "format" : "date-time" + }, + "updateDate" : { + "type" : "string", + "format" : "date-time" + } + } + }, "Link" : { "type" : "object", "properties" : { diff --git a/openapi/p4pa-debt-position.openapi.yaml b/openapi/p4pa-debt-position.openapi.yaml index d6f368d..c251187 100644 --- a/openapi/p4pa-debt-position.openapi.yaml +++ b/openapi/p4pa-debt-position.openapi.yaml @@ -12,6 +12,13 @@ paths: tags: - debt-position operationId: createDebtPosition + parameters: + - name: massive + in: query + description: Indicates if the operation is massive (true) or single (false). + required: false + schema: + type: boolean requestBody: content: application/json: @@ -49,8 +56,48 @@ paths: $ref: "#/components/schemas/ReceiptDTO" "400": description: Invalid request + "409": + description: Debt position already exists "500": description: Internal server error + /debt-positions/{debtPositionId}/finalize-sync-status: + put: + tags: + - debt-position + operationId: finalizeSyncStatus + parameters: + - name: debtPositionId + in: path + required: true + schema: + type: integer + format: int64 + description: The ID of the debt position + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SyncStatusUpdateRequestDTO" + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/DebtPositionDTO' + "400": + description: Invalid request + content: + application/json: + schema: + $ref: '#/components/schemas/DebtPositionErrorDTO' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/DebtPositionErrorDTO' security: - BearerAuth: [] components: @@ -74,7 +121,7 @@ components: description: type: string status: - type: string + $ref: "#/components/schemas/DebtPositionStatus" ingestionFlowFileId: type: integer format: int64 @@ -122,7 +169,7 @@ components: type: integer format: int64 status: - type: string + $ref: "#/components/schemas/PaymentOptionStatus" multiDebtor: type: boolean dueDate: @@ -155,7 +202,7 @@ components: type: integer format: int64 status: - type: string + $ref: "#/components/schemas/InstallmentStatus" iupdPagopa: type: string iud: @@ -335,3 +382,60 @@ components: type: string email: type: string + DebtPositionStatus: + type: string + enum: + - TO_SYNC + - REPORTED + - PAID + - PARTIALLY_PAID + - CANCELLED + - INVALID + - EXPIRED + - UNPAID + PaymentOptionStatus: + type: string + enum: + - TO_SYNC + - REPORTED + - PAID + - PARTIALLY_PAID + - CANCELLED + - INVALID + - EXPIRED + - UNPAID + InstallmentStatus: + type: string + enum: + - TO_SYNC + - REPORTED + - PAID + - CANCELLED + - INVALID + - EXPIRED + - UNPAID + SyncStatusUpdateRequestDTO: + type: object + additionalProperties: + $ref: "#/components/schemas/IupdSyncStatusUpdateDTO" + IupdSyncStatusUpdateDTO: + type: object + properties: + newStatus: + $ref: "#/components/schemas/InstallmentStatus" + iupdPagopa: + type: string + + DebtPositionErrorDTO: + type: object + required: + - code + - message + properties: + code: + type: string + enum: + - DEBT_POSITION_IVALID_REQUEST + - DEBT_POSITION_GENERIC_ERROR + message: + type: string diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerImpl.java b/src/main/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerImpl.java index 975eca4..00f7a91 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerImpl.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerImpl.java @@ -2,13 +2,31 @@ import it.gov.pagopa.pu.debtpositions.controller.generated.DebtPositionApi; import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.IupdSyncStatusUpdateDTO; +import it.gov.pagopa.pu.debtpositions.service.statusalign.DebtPositionHierarchyStatusAlignerService; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + @RestController public class DebtPositionControllerImpl implements DebtPositionApi { + + private final DebtPositionHierarchyStatusAlignerService debtPositionHierarchyStatusAlignerService; + + public DebtPositionControllerImpl(DebtPositionHierarchyStatusAlignerService debtPositionHierarchyStatusAlignerService) { + this.debtPositionHierarchyStatusAlignerService = debtPositionHierarchyStatusAlignerService; + } + + @Override + public ResponseEntity createDebtPosition(DebtPositionDTO debtPositionDTO, Boolean massive) { + return DebtPositionApi.super.createDebtPosition(debtPositionDTO, massive); + } + @Override - public ResponseEntity createDebtPosition(DebtPositionDTO debtPositionDTO) { - return DebtPositionApi.super.createDebtPosition(debtPositionDTO); + public ResponseEntity finalizeSyncStatus(Long debtPositionId, Map requestBody) { + DebtPositionDTO body = debtPositionHierarchyStatusAlignerService.finalizeSyncStatus(debtPositionId, requestBody); + return new ResponseEntity<>(body, HttpStatus.OK); } } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/dto/Installment.java b/src/main/java/it/gov/pagopa/pu/debtpositions/dto/Installment.java index 8843957..2c17304 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/dto/Installment.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/dto/Installment.java @@ -1,6 +1,7 @@ package it.gov.pagopa.pu.debtpositions.dto; import com.fasterxml.jackson.annotation.JsonIgnore; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; import it.gov.pagopa.pu.debtpositions.model.Transfer; import lombok.AllArgsConstructor; @@ -20,7 +21,7 @@ public class Installment { private Long installmentId; private Long paymentOptionId; - private String status; + private InstallmentStatus status; private String iupdPagopa; private String iud; private String iuv; diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandler.java b/src/main/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandler.java new file mode 100644 index 0000000..d38c2d2 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandler.java @@ -0,0 +1,46 @@ +package it.gov.pagopa.pu.debtpositions.exception; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionErrorDTO; +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + + +@RestControllerAdvice +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +public class DebtPositionExceptionHandler { + + @ExceptionHandler({InvalidStatusException.class}) + public ResponseEntity handleInternalError(RuntimeException ex, HttpServletRequest request){ + return handleWorkflowErrorException(ex, request, HttpStatus.BAD_REQUEST, DebtPositionErrorDTO.CodeEnum.IVALID_REQUEST); + } + + static ResponseEntity handleWorkflowErrorException(RuntimeException ex, HttpServletRequest request, HttpStatus httpStatus, DebtPositionErrorDTO.CodeEnum errorEnum) { + String message = logException(ex, request, httpStatus); + + return ResponseEntity + .status(httpStatus) + .body(DebtPositionErrorDTO.builder().code(errorEnum).message(message).build()); + } + + private static String logException(RuntimeException ex, HttpServletRequest request, HttpStatus httpStatus) { + String message = ex.getMessage(); + log.info("A {} occurred handling request {}: HttpStatus {} - {}", + ex.getClass(), + getRequestDetails(request), + httpStatus.value(), + message); + return message; + } + + static String getRequestDetails(HttpServletRequest request) { + return "%s %s".formatted(request.getMethod(), request.getRequestURI()); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/exception/custom/InvalidStatusException.java b/src/main/java/it/gov/pagopa/pu/debtpositions/exception/custom/InvalidStatusException.java new file mode 100644 index 0000000..6cb026f --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/exception/custom/InvalidStatusException.java @@ -0,0 +1,8 @@ +package it.gov.pagopa.pu.debtpositions.exception.custom; + +public class InvalidStatusException extends RuntimeException{ + + public InvalidStatusException(String message) { + super(message); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapper.java b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapper.java index eaffb3b..14b443f 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapper.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapper.java @@ -8,6 +8,7 @@ import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; +import java.time.OffsetDateTime; import java.util.*; import java.util.stream.Collector; import java.util.stream.Collectors; @@ -53,4 +54,27 @@ public Pair> mapToModel(DebtPos return Pair.of(debtPosition, installmentMapping); } + + public DebtPositionDTO mapToDto(DebtPosition debtPosition){ + return DebtPositionDTO.builder() + .debtPositionId(debtPosition.getDebtPositionId()) + .iupdOrg(debtPosition.getIupdOrg()) + .description(debtPosition.getDescription()) + .status(debtPosition.getStatus()) + .ingestionFlowFileId(debtPosition.getIngestionFlowFileId()) + .ingestionFlowFileLineNumber(debtPosition.getIngestionFlowFileLineNumber()) + .organizationId(debtPosition.getOrganizationId()) + .debtPositionTypeOrgId(debtPosition.getDebtPositionTypeOrgId()) + .notificationDate(debtPosition.getNotificationDate()) + .validityDate(debtPosition.getValidityDate()) + .flagIuvVolatile(debtPosition.isFlagIuvVolatile()) + .creationDate(OffsetDateTime.from(debtPosition.getCreationDate())) + .updateDate(OffsetDateTime.from(debtPosition.getUpdateDate())) + .paymentOptions( + debtPosition.getPaymentOptions().stream() + .map(paymentOptionMapper::mapToDto) + .toList() + ) .build(); + } } + diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapper.java b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapper.java index 74c8671..3fdfca9 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapper.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapper.java @@ -2,8 +2,11 @@ import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentDTO; +import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; import org.springframework.stereotype.Service; +import java.time.OffsetDateTime; + @Service public class InstallmentMapper { @@ -44,4 +47,32 @@ public Installment mapToModel(InstallmentDTO dto) { return installment; } + public InstallmentDTO mapToDto(InstallmentNoPII installment) { + return InstallmentDTO.builder() + .installmentId(installment.getInstallmentId()) + .paymentOptionId(installment.getPaymentOptionId()) + .status(installment.getStatus()) + .iupdPagopa(installment.getIupdPagopa()) + .iud(installment.getIud()) + .iuv(installment.getIuv()) + .iur(installment.getIur()) + .iuf(installment.getIuf()) + .nav(installment.getNav()) + .dueDate(installment.getDueDate()) + .paymentTypeCode(installment.getPaymentTypeCode()) + .amountCents(installment.getAmountCents()) + .notificationFeeCents(installment.getNotificationFeeCents()) + .remittanceInformation(installment.getRemittanceInformation()) + .humanFriendlyRemittanceInformation(installment.getHumanFriendlyRemittanceInformation()) + .balance(installment.getBalance()) + .legacyPaymentMetadata(installment.getLegacyPaymentMetadata()) + .transfers(installment.getTransfers().stream() + .map(transferMapper::mapToDto) + .toList()) + .creationDate(OffsetDateTime.from(installment.getCreationDate())) + .updateDate(OffsetDateTime.from(installment.getUpdateDate())) + .build(); + } + + } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapper.java b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapper.java index 24e2271..8258efb 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapper.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapper.java @@ -55,4 +55,23 @@ public Pair> mapToModel(Paymen return Pair.of(paymentOption, installmentMapping); } + + public PaymentOptionDTO mapToDto(PaymentOption paymentOption) { + return PaymentOptionDTO.builder() + .paymentOptionId(paymentOption.getPaymentOptionId()) + .debtPositionId(paymentOption.getDebtPositionId()) + .totalAmountCents(paymentOption.getTotalAmountCents()) + .status(paymentOption.getStatus()) + .multiDebtor(paymentOption.isMultiDebtor()) + .dueDate(paymentOption.getDueDate()) + .description(paymentOption.getDescription()) + .paymentOptionType(PaymentOptionDTO.PaymentOptionTypeEnum.valueOf(paymentOption.getPaymentOptionType().name())) + .installments( + paymentOption.getInstallments().stream() + .map(installmentMapper::mapToDto) + .toList() + ) + .build(); + } + } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapper.java b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapper.java index d44fe51..ec4adbb 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapper.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapper.java @@ -21,5 +21,4 @@ public Person mapToModel(PersonDTO dto) { person.setEmail(dto.getEmail()); return person; } - } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapper.java b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapper.java index 08437ac..e6528f5 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapper.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapper.java @@ -24,4 +24,23 @@ public Transfer mapToModel(TransferDTO dto) { return transfer; } + public TransferDTO mapToDto(Transfer transfer) { + return TransferDTO.builder() + .transferId(transfer.getTransferId()) + .installmentId(transfer.getInstallmentId()) + .orgFiscalCode(transfer.getOrgFiscalCode()) + .orgName(transfer.getOrgName()) + .amountCents(transfer.getAmountCents()) + .remittanceInformation(transfer.getRemittanceInformation()) + .stampType(transfer.getStamp().getStampType()) + .stampHashDocument(transfer.getStamp().getStampHashDocument()) + .stampProvincialResidence(transfer.getStamp().getStampProvincialResidence()) + .iban(transfer.getIban()) + .postalIban(transfer.getPostalIban()) + .category(transfer.getCategory()) + .transferIndex(transfer.getTransferIndex()) + .build(); + } + + } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/model/DebtPosition.java b/src/main/java/it/gov/pagopa/pu/debtpositions/model/DebtPosition.java index 009d4c4..75b06de 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/model/DebtPosition.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/model/DebtPosition.java @@ -1,10 +1,8 @@ package it.gov.pagopa.pu.debtpositions.model; +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.*; import java.io.Serializable; import java.time.OffsetDateTime; @@ -27,6 +25,7 @@ @AllArgsConstructor @NoArgsConstructor @Data +@Builder @EqualsAndHashCode(of = "debtPositionId", callSuper = false) public class DebtPosition extends BaseEntity implements Serializable { @@ -36,7 +35,8 @@ public class DebtPosition extends BaseEntity implements Serializable { private Long debtPositionId; private String iupdOrg; private String description; - private String status; + @Enumerated(EnumType.STRING) + private DebtPositionStatus status; private Long ingestionFlowFileId; private Long ingestionFlowFileLineNumber; private Long organizationId; diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/model/InstallmentNoPII.java b/src/main/java/it/gov/pagopa/pu/debtpositions/model/InstallmentNoPII.java index fd51a7a..7886c8f 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/model/InstallmentNoPII.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/model/InstallmentNoPII.java @@ -1,5 +1,6 @@ package it.gov.pagopa.pu.debtpositions.model; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; import jakarta.annotation.Nonnull; import jakarta.persistence.*; import lombok.AllArgsConstructor; @@ -27,7 +28,8 @@ public class InstallmentNoPII extends BaseEntity implements Serializable, Compar @SequenceGenerator(name = "installment_generator", sequenceName = "installment_seq", allocationSize = 1) private Long installmentId; private Long paymentOptionId; - private String status; + @Enumerated(EnumType.STRING) + private InstallmentStatus status; private String iupdPagopa; private String iud; private String iuv; diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/model/PaymentOption.java b/src/main/java/it/gov/pagopa/pu/debtpositions/model/PaymentOption.java index 9d15941..08af719 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/model/PaymentOption.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/model/PaymentOption.java @@ -1,12 +1,10 @@ package it.gov.pagopa.pu.debtpositions.model; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; import it.gov.pagopa.pu.debtpositions.enums.PaymentOptionType; import jakarta.annotation.Nonnull; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.*; import java.io.Serializable; import java.time.OffsetDateTime; @@ -18,6 +16,7 @@ @AllArgsConstructor @NoArgsConstructor @Data +@Builder @EqualsAndHashCode(of = "paymentOptionId", callSuper = false) public class PaymentOption extends BaseEntity implements Serializable, Comparable { @@ -27,7 +26,8 @@ public class PaymentOption extends BaseEntity implements Serializable, Comparabl private Long paymentOptionId; private Long debtPositionId; private Long totalAmountCents; - private String status; + @Enumerated(EnumType.STRING) + private PaymentOptionStatus status; private boolean multiDebtor; private OffsetDateTime dueDate; private String description; diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/model/Transfer.java b/src/main/java/it/gov/pagopa/pu/debtpositions/model/Transfer.java index cd58dcf..fec5c6e 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/model/Transfer.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/model/Transfer.java @@ -3,6 +3,7 @@ import jakarta.annotation.Nonnull; import jakarta.persistence.*; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -15,6 +16,7 @@ @AllArgsConstructor @NoArgsConstructor @Data +@Builder @EqualsAndHashCode(of = "transferId", callSuper = false) public class Transfer extends BaseEntity implements Serializable, Comparable { diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/DebtPositionRepository.java b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/DebtPositionRepository.java index 786892f..4911a04 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/DebtPositionRepository.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/DebtPositionRepository.java @@ -1,13 +1,25 @@ package it.gov.pagopa.pu.debtpositions.repository; +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; @RepositoryRestResource(path = "debt-positions") -public interface DebtPositionRepository extends JpaRepository { +public interface DebtPositionRepository extends JpaRepository { - @EntityGraph(value = "completeDebtPosition") - DebtPosition findOneWithAllDataByDebtPositionId(Long debtPositionId); + @EntityGraph(value = "completeDebtPosition") + DebtPosition findOneWithAllDataByDebtPositionId(Long debtPositionId); + + @RestResource(exported = false) + @Transactional + @Modifying + @Query("UPDATE DebtPosition d SET d.status = :status WHERE d.debtPositionId = :debtPositionId") + void updateStatus(@Param("debtPositionId") Long debtPositionId, @Param("status") DebtPositionStatus status); } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/InstallmentNoPIIRepository.java b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/InstallmentNoPIIRepository.java index a30da6c..7ad0ac9 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/InstallmentNoPIIRepository.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/InstallmentNoPIIRepository.java @@ -1,10 +1,23 @@ package it.gov.pagopa.pu.debtpositions.repository; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; @RepositoryRestResource(path = "installments") public interface InstallmentNoPIIRepository extends JpaRepository { + @RestResource(exported = false) + @Transactional + @Modifying + @Query("UPDATE InstallmentNoPII i SET i.status = :status, i.iupdPagopa = :iupdPagopa WHERE i.installmentId = :installmentId") + void updateStatusAndIupdPagopa(@Param("installmentId") Long installmentId, @Param("iupdPagopa") String iupdPagopa, @Param("status") InstallmentStatus status); + + } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/PaymentOptionRepository.java b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/PaymentOptionRepository.java index d2613bb..195100f 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/repository/PaymentOptionRepository.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/repository/PaymentOptionRepository.java @@ -1,10 +1,22 @@ package it.gov.pagopa.pu.debtpositions.repository; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; @RepositoryRestResource(path = "payment-options") public interface PaymentOptionRepository extends JpaRepository { + @RestResource(exported = false) + @Transactional + @Modifying + @Query("UPDATE PaymentOption p SET p.status = :status WHERE p.paymentOptionId = :paymentOptionId") + void updateStatus(@Param("paymentOptionId") Long paymentOptionId, @Param("status") PaymentOptionStatus status); + } diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/DebtPositionServiceImpl.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/DebtPositionServiceImpl.java index 09b8d71..f9900e6 100644 --- a/src/main/java/it/gov/pagopa/pu/debtpositions/service/DebtPositionServiceImpl.java +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/DebtPositionServiceImpl.java @@ -2,16 +2,21 @@ import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; -import it.gov.pagopa.pu.debtpositions.mapper.*; +import it.gov.pagopa.pu.debtpositions.mapper.DebtPositionMapper; import it.gov.pagopa.pu.debtpositions.model.DebtPosition; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; import it.gov.pagopa.pu.debtpositions.model.PaymentOption; -import it.gov.pagopa.pu.debtpositions.repository.*; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import it.gov.pagopa.pu.debtpositions.repository.InstallmentPIIRepository; +import it.gov.pagopa.pu.debtpositions.repository.PaymentOptionRepository; +import it.gov.pagopa.pu.debtpositions.repository.TransferRepository; import jakarta.transaction.Transactional; import org.springframework.data.util.Pair; +import org.springframework.stereotype.Service; import java.util.Map; +@Service public class DebtPositionServiceImpl implements DebtPositionService { private final DebtPositionRepository debtPositionRepository; diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerService.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerService.java new file mode 100644 index 0000000..23c37c6 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerService.java @@ -0,0 +1,11 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.IupdSyncStatusUpdateDTO; + +import java.util.Map; + +public interface DebtPositionHierarchyStatusAlignerService { + + DebtPositionDTO finalizeSyncStatus(Long debtPositionId, Map syncStatusDTO); +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceImpl.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceImpl.java new file mode 100644 index 0000000..3a041f4 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceImpl.java @@ -0,0 +1,77 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.IupdSyncStatusUpdateDTO; +import it.gov.pagopa.pu.debtpositions.mapper.DebtPositionMapper; +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import it.gov.pagopa.pu.debtpositions.repository.InstallmentNoPIIRepository; +import it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition.DebtPositionInnerStatusAlignerService; +import it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption.PaymentOptionInnerStatusAlignerService; +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Map; + +import static it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus.TO_SYNC; + +@Service +@Slf4j +public class DebtPositionHierarchyStatusAlignerServiceImpl implements DebtPositionHierarchyStatusAlignerService { + + private final DebtPositionRepository debtPositionRepository; + private final InstallmentNoPIIRepository installmentNoPIIRepository; + private final PaymentOptionInnerStatusAlignerService paymentOptionInnerStatusAlignerService; + private final DebtPositionInnerStatusAlignerService debtPositionInnerStatusAlignerService; + private final DebtPositionMapper debtPositionMapper; + + + public DebtPositionHierarchyStatusAlignerServiceImpl(DebtPositionRepository debtPositionRepository, + InstallmentNoPIIRepository installmentNoPIIRepository, PaymentOptionInnerStatusAlignerService paymentOptionInnerStatusAlignerService, DebtPositionInnerStatusAlignerService debtPositionInnerStatusAlignerService, DebtPositionMapper debtPositionMapper) { + this.debtPositionRepository = debtPositionRepository; + this.installmentNoPIIRepository = installmentNoPIIRepository; + this.paymentOptionInnerStatusAlignerService = paymentOptionInnerStatusAlignerService; + this.debtPositionInnerStatusAlignerService = debtPositionInnerStatusAlignerService; + this.debtPositionMapper = debtPositionMapper; + } + + @Transactional + @Override + public DebtPositionDTO finalizeSyncStatus(Long debtPositionId, Map syncStatusDTO) { + DebtPosition debtPosition = debtPositionRepository.findOneWithAllDataByDebtPositionId(debtPositionId); + + debtPosition.getPaymentOptions().forEach(paymentOption -> + paymentOption.getInstallments().stream() + .filter(installment -> { + boolean isToSync = TO_SYNC.equals(installment.getStatus()); + boolean hasIud = syncStatusDTO.containsKey(installment.getIud()); + + if (!hasIud) { + log.error("Installment with IUD [{}] is not present in the syncStatusDTO map", installment.getIud()); + } else if (!isToSync) { + log.error("Installment with IUD [{}] does not have TO_SYNC status", installment.getIud()); + } + + return isToSync && hasIud; + }) + .forEach(installment -> { + IupdSyncStatusUpdateDTO updateDTO = syncStatusDTO.get(installment.getIud()); + + InstallmentStatus newStatus = updateDTO.getNewStatus(); + installment.setStatus(newStatus); + installmentNoPIIRepository.updateStatusAndIupdPagopa( + installment.getInstallmentId(), + updateDTO.getIupdPagopa(), + newStatus + ); + }) + ); + + debtPosition.getPaymentOptions().forEach(paymentOptionInnerStatusAlignerService::updatePaymentOptionStatus); + debtPositionInnerStatusAlignerService.updateDebtPositionStatus(debtPosition); + + return debtPositionMapper.mapToDto(debtPosition); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/StatusRulesHandler.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/StatusRulesHandler.java new file mode 100644 index 0000000..37b7483 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/StatusRulesHandler.java @@ -0,0 +1,81 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign; + +import java.util.List; +import java.util.Set; + +public abstract class StatusRulesHandler, T, D> { + + private final E syncStatus; + private final E paidStatus; + private final E unpaidStatus; + private final E expiredStatus; + private final E cancelledStatus; + private final E reportedStatus; + private final E invalidStatus; + + protected StatusRulesHandler(E syncStatus, E paidStatus, E unpaidStatus, E expiredStatus, E cancelledStatus, E reportedStatus, E invalidStatus) { + this.syncStatus = syncStatus; + this.paidStatus = paidStatus; + this.unpaidStatus = unpaidStatus; + this.expiredStatus = expiredStatus; + this.cancelledStatus = cancelledStatus; + this.reportedStatus = reportedStatus; + this.invalidStatus = invalidStatus; + } + + public void updateEntityStatus(T entity) { + List childStatuses = getChildStatuses(entity); + D newStatus = calculateNewStatus(childStatuses); + setStatus(entity, newStatus); + storeStatus(entity, newStatus); + } + + protected abstract List getChildStatuses(T entity); + + protected abstract D calculateNewStatus(List childStatuses); + + protected abstract void setStatus(T entity, D newStatus); + + protected abstract void storeStatus(T entity, D newStatus); + + public boolean isToSync(List childrenStatusList) { + return childrenStatusList.contains(syncStatus); + } + + public boolean isPartiallyPaid(List childrenStatusList) { + return childrenStatusList.contains(paidStatus) && + (childrenStatusList.contains(unpaidStatus) || childrenStatusList.contains(expiredStatus)); + } + + public boolean isUnpaid(List childrenStatusList) { + return allMatch(childrenStatusList, unpaidStatus, Set.of(cancelledStatus)); + } + + public boolean isPaid(List childrenStatusList) { + return allMatch(childrenStatusList, paidStatus, Set.of(cancelledStatus)); + } + + public boolean isReported(List childrenStatusList) { + return allMatch(childrenStatusList, reportedStatus, Set.of(cancelledStatus)); + } + + public boolean isInvalid(List childrenStatusList) { + return allMatch(childrenStatusList, invalidStatus, Set.of(cancelledStatus)); + } + + public boolean isCancelled(List childrenStatusList) { + return allMatch(childrenStatusList, cancelledStatus, Set.of()); + } + + public boolean isExpired(List childrenStatusList) { + return allMatch(childrenStatusList, expiredStatus, Set.of()); + } + + private boolean allMatch(List statusList, E requiredState, Set allowedStatuses) { + if (statusList.isEmpty()) { + return false; + } + return statusList.contains(requiredState) && + statusList.stream().allMatch(status -> requiredState.equals(status) || allowedStatuses.contains(status)); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerService.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerService.java new file mode 100644 index 0000000..fc896e0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerService.java @@ -0,0 +1,8 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition; + +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; + +public interface DebtPositionInnerStatusAlignerService { + + void updateDebtPositionStatus(DebtPosition debtPosition); +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceImpl.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceImpl.java new file mode 100644 index 0000000..09408ab --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceImpl.java @@ -0,0 +1,18 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition; + +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import org.springframework.stereotype.Service; + +@Service +public class DebtPositionInnerStatusAlignerServiceImpl extends DebtPositionStatusChecker implements DebtPositionInnerStatusAlignerService { + + public DebtPositionInnerStatusAlignerServiceImpl(DebtPositionRepository debtPositionRepository) { + super(debtPositionRepository); + } + + @Override + public void updateDebtPositionStatus(DebtPosition debtPosition) { + updateEntityStatus(debtPosition); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusChecker.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusChecker.java new file mode 100644 index 0000000..5727554 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusChecker.java @@ -0,0 +1,63 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import it.gov.pagopa.pu.debtpositions.service.statusalign.StatusRulesHandler; + +import java.util.List; + +import static it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus.*; + +public class DebtPositionStatusChecker extends StatusRulesHandler { + + private final DebtPositionRepository debtPositionRepository; + + public DebtPositionStatusChecker(DebtPositionRepository debtPositionRepository) { + super(TO_SYNC, PAID, UNPAID, EXPIRED, CANCELLED, REPORTED, INVALID); + this.debtPositionRepository = debtPositionRepository; + } + + @Override + public DebtPositionStatus calculateNewStatus(List paymentOptionStatusList) { + if (isToSync(paymentOptionStatusList)){ + return DebtPositionStatus.TO_SYNC; + } else if (isPartiallyPaid(paymentOptionStatusList)){ + return DebtPositionStatus.PARTIALLY_PAID; + } else if (isUnpaid(paymentOptionStatusList)){ + return DebtPositionStatus.UNPAID; + } else if (isPaid(paymentOptionStatusList)){ + return DebtPositionStatus.PAID; + } else if (isReported(paymentOptionStatusList)){ + return DebtPositionStatus.REPORTED; + } else if (isInvalid(paymentOptionStatusList)){ + return DebtPositionStatus.INVALID; + } else if (isCancelled(paymentOptionStatusList)){ + return DebtPositionStatus.CANCELLED; + } else if (isExpired(paymentOptionStatusList)){ + return DebtPositionStatus.EXPIRED; + } else { + throw new InvalidStatusException("Unable to determine status for DebtPosition"); + } + } + + @Override + protected List getChildStatuses(DebtPosition debtPosition) { + return debtPosition.getPaymentOptions().stream() + .map(PaymentOption::getStatus) + .toList(); + } + + @Override + protected void setStatus(DebtPosition debtPosition, DebtPositionStatus newStatus) { + debtPosition.setStatus(newStatus); + } + + @Override + protected void storeStatus(DebtPosition debtPosition, DebtPositionStatus newStatus) { + debtPositionRepository.updateStatus(debtPosition.getDebtPositionId(), newStatus); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerService.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerService.java new file mode 100644 index 0000000..0b54fb1 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerService.java @@ -0,0 +1,8 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption; + +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; + +public interface PaymentOptionInnerStatusAlignerService { + + void updatePaymentOptionStatus(PaymentOption paymentOption); +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceImpl.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceImpl.java new file mode 100644 index 0000000..8dd7020 --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceImpl.java @@ -0,0 +1,19 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption; + +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.PaymentOptionRepository; +import org.springframework.stereotype.Service; + +@Service +public class PaymentOptionInnerStatusAlignerServiceImpl extends PaymentOptionStatusChecker implements PaymentOptionInnerStatusAlignerService{ + + + public PaymentOptionInnerStatusAlignerServiceImpl(PaymentOptionRepository paymentOptionRepository) { + super(paymentOptionRepository); + } + + @Override + public void updatePaymentOptionStatus(PaymentOption paymentOption) { + updateEntityStatus(paymentOption); + } +} diff --git a/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusChecker.java b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusChecker.java new file mode 100644 index 0000000..c40adcf --- /dev/null +++ b/src/main/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusChecker.java @@ -0,0 +1,64 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption; + +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.PaymentOptionRepository; +import it.gov.pagopa.pu.debtpositions.service.statusalign.StatusRulesHandler; + +import java.util.List; + +import static it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus.TO_SYNC; + +public class PaymentOptionStatusChecker extends StatusRulesHandler { + + private final PaymentOptionRepository paymentOptionRepository; + + public PaymentOptionStatusChecker(PaymentOptionRepository paymentOptionRepository) { + super(InstallmentStatus.TO_SYNC, InstallmentStatus.PAID, InstallmentStatus.UNPAID, + InstallmentStatus.EXPIRED, InstallmentStatus.CANCELLED, InstallmentStatus.REPORTED, InstallmentStatus.INVALID); + this.paymentOptionRepository = paymentOptionRepository; + } + + @Override + public PaymentOptionStatus calculateNewStatus(List installmentStatusList) { + if (isToSync(installmentStatusList)) { + return TO_SYNC; + } else if (isPartiallyPaid(installmentStatusList)) { + return PaymentOptionStatus.PARTIALLY_PAID; + } else if (isUnpaid(installmentStatusList)) { + return PaymentOptionStatus.UNPAID; + } else if (isPaid(installmentStatusList)) { + return PaymentOptionStatus.PAID; + } else if (isReported(installmentStatusList)) { + return PaymentOptionStatus.REPORTED; + } else if (isInvalid(installmentStatusList)) { + return PaymentOptionStatus.INVALID; + } else if (isCancelled(installmentStatusList)) { + return PaymentOptionStatus.CANCELLED; + } else if (isExpired(installmentStatusList)) { + return PaymentOptionStatus.EXPIRED; + } else { + throw new InvalidStatusException("Unable to determine status for PaymentOption"); + } + } + + @Override + protected List getChildStatuses(PaymentOption paymentOption) { + return paymentOption.getInstallments().stream() + .map(InstallmentNoPII::getStatus) + .toList(); + } + + @Override + protected void setStatus(PaymentOption paymentOption, PaymentOptionStatus newStatus) { + paymentOption.setStatus(newStatus); + } + + @Override + protected void storeStatus(PaymentOption paymentOption, PaymentOptionStatus newStatus) { + paymentOptionRepository.updateStatus(paymentOption.getPaymentOptionId(), newStatus); + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerTest.java new file mode 100644 index 0000000..98fc8fd --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/controller/DebtPositionControllerTest.java @@ -0,0 +1,64 @@ +package it.gov.pagopa.pu.debtpositions.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.IupdSyncStatusUpdateDTO; +import it.gov.pagopa.pu.debtpositions.service.statusalign.DebtPositionHierarchyStatusAlignerService; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.util.HashMap; +import java.util.Map; + +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPositionDTO; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(DebtPositionControllerImpl.class) +@AutoConfigureMockMvc(addFilters = false) +class DebtPositionControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockitoBean + private DebtPositionHierarchyStatusAlignerService service; + + @Test + void whenFinalizeSyncStatusThenOk() throws Exception { + Long id = 1L; + InstallmentStatus newStatus = InstallmentStatus.TO_SYNC; + + Map syncStatusDTO = new HashMap<>(); + IupdSyncStatusUpdateDTO iupdSyncStatusUpdateDTO = IupdSyncStatusUpdateDTO.builder() + .newStatus(newStatus) + .iupdPagopa("iupdPagoPa") + .build(); + + syncStatusDTO.put("iud", iupdSyncStatusUpdateDTO); + + Mockito.when(service.finalizeSyncStatus(id, syncStatusDTO)).thenReturn(buildDebtPositionDTO()); + + MvcResult result = mockMvc.perform( + put("/debt-positions/1/finalize-sync-status") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(syncStatusDTO))) + .andExpect(status().isOk()) + .andReturn(); + + DebtPositionDTO resultResponse = objectMapper.readValue(result.getResponse().getContentAsString(), DebtPositionDTO.class); + assertEquals(buildDebtPositionDTO(), resultResponse); + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandlerTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandlerTest.java new file mode 100644 index 0000000..b5805ba --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/exception/DebtPositionExceptionHandlerTest.java @@ -0,0 +1,60 @@ +package it.gov.pagopa.pu.debtpositions.exception; + +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import static org.mockito.Mockito.doThrow; + +@ExtendWith({SpringExtension.class}) +@WebMvcTest(value = {DebtPositionExceptionHandlerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) +@ContextConfiguration(classes = { + DebtPositionExceptionHandlerTest.TestController.class, + DebtPositionExceptionHandler.class}) +public class DebtPositionExceptionHandlerTest { + + public static final String DATA = "data"; + @Autowired + private MockMvc mockMvc; + + @MockitoSpyBean + private TestController testControllerSpy; + + @RestController + @Slf4j + static class TestController { + + @GetMapping("/test") + String testEndpoint(@RequestParam(DATA) String data) { + return "OK"; + } + } + + @Test + void handleInvalidStatusExceptionError() throws Exception { + doThrow(new InvalidStatusException("Error")).when(testControllerSpy).testEndpoint(DATA); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .param(DATA, DATA) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("DEBT_POSITION_IVALID_REQUEST")) + .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Error")); + + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapperTest.java index 3f069b7..fb36844 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/DebtPositionMapperTest.java @@ -1,46 +1,61 @@ package it.gov.pagopa.pu.debtpositions.mapper; -import it.gov.pagopa.pu.debtpositions.citizen.service.DataCipherService; import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; import it.gov.pagopa.pu.debtpositions.model.DebtPosition; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.util.Pair; +import java.util.HashMap; import java.util.Map; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; -import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.*; -import static org.junit.jupiter.api.Assertions.*; - +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPosition; +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPositionDTO; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.buildInstallment; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.buildInstallmentNoPII; +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOption; +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOptionDTO; + +@ExtendWith(MockitoExtension.class) class DebtPositionMapperTest { @Mock - private DataCipherService dataCipherServiceMock; + private PaymentOptionMapper paymentOptionMapperMock; private DebtPositionMapper debtPositionMapper; @BeforeEach - void init(){ - PersonMapper personMapper = new PersonMapper(); - TransferMapper transferMapper = new TransferMapper(); - InstallmentMapper installmentMapper = new InstallmentMapper(personMapper, transferMapper); - InstallmentPIIMapper installmentPIIMapper = new InstallmentPIIMapper(dataCipherServiceMock); - PaymentOptionMapper paymentOptionMapper = new PaymentOptionMapper(installmentMapper, installmentPIIMapper); - debtPositionMapper = new DebtPositionMapper(paymentOptionMapper); + void setUp(){ + debtPositionMapper = new DebtPositionMapper(paymentOptionMapperMock); } @Test void givenValidDebtPositionDTO_whenMapToModel_thenReturnDebtPositionAndInstallmentMap() { DebtPosition debtPositionExpected = buildDebtPosition(); + debtPositionExpected.setStatus(DebtPositionStatus.UNPAID); DebtPositionDTO debtPositionDTO = buildDebtPositionDTO(); + Map installmentMap = new HashMap<>(); + installmentMap.put(buildInstallmentNoPII(), buildInstallment()); + + PaymentOption paymentOption = buildPaymentOption(); + Pair> paymentOptionPair = Pair.of(paymentOption, installmentMap); + + Mockito.when(paymentOptionMapperMock.mapToModel(buildPaymentOptionDTO())).thenReturn(paymentOptionPair); + Pair> result = debtPositionMapper.mapToModel(debtPositionDTO); - assertEquals(debtPositionExpected, result.getFirst()); + reflectionEqualsByName(debtPositionExpected, result.getFirst()); checkNotNullFields(result.getFirst(), "updateOperatorExternalId"); } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapperTest.java index 1baf93d..048c60f 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentMapperTest.java @@ -2,26 +2,50 @@ import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; -import static org.junit.jupiter.api.Assertions.*; -import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.*; - +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.buildInstallmentDTO; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.buildInstallmentNoUpdate; +import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPerson; +import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPersonDTO; +import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransfer; +import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransferDTO; + +@ExtendWith(MockitoExtension.class) class InstallmentMapperTest { - private final PersonMapper personMapper = new PersonMapper(); - private final TransferMapper transferMapper = new TransferMapper(); - private final InstallmentMapper installmentMapper = new InstallmentMapper(personMapper, transferMapper); + @Mock + private PersonMapper personMapperMock; + @Mock + private TransferMapper transferMapperMock; + + private InstallmentMapper installmentMapper; + + @BeforeEach + void setUp(){ + installmentMapper = new InstallmentMapper(personMapperMock, transferMapperMock); + } @Test void givenValidInstallmentDTO_WhenMapToModel_ThenReturnInstallment() { Installment installmentExpected = buildInstallmentNoUpdate(); + installmentExpected.setStatus(InstallmentStatus.UNPAID); InstallmentDTO installmentDTO = buildInstallmentDTO(); + Mockito.when(personMapperMock.mapToModel(buildPersonDTO())).thenReturn(buildPerson()); + Mockito.when(transferMapperMock.mapToModel(buildTransferDTO())).thenReturn(buildTransfer()); + Installment result = installmentMapper.mapToModel(installmentDTO); - assertEquals(installmentExpected, result); + reflectionEqualsByName(installmentExpected, result); checkNotNullFields(result, "updateOperatorExternalId", "noPII"); } } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentPIIMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentPIIMapperTest.java index 88966eb..c5d1ea9 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentPIIMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/InstallmentPIIMapperTest.java @@ -14,6 +14,7 @@ import org.springframework.data.util.Pair; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -46,8 +47,8 @@ void testMap(){ Pair result = mapper.map(installment); - assertEquals(installmentNoPIIExpected, result.getFirst()); - assertEquals(installmentPIIDTOExpected, result.getSecond()); + reflectionEqualsByName(installmentNoPIIExpected, result.getFirst()); + reflectionEqualsByName(installmentPIIDTOExpected, result.getSecond()); checkNotNullFields(result.getFirst(), "transfers", "personalDataId", "debtorFiscalCodeHash"); checkNotNullFields(result.getSecond()); } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapperTest.java index 1b2ad19..27f827e 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PaymentOptionMapperTest.java @@ -1,39 +1,54 @@ package it.gov.pagopa.pu.debtpositions.mapper; -import it.gov.pagopa.pu.debtpositions.citizen.service.DataCipherService; import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.util.Pair; import java.util.Map; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; -import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.*; -import static org.junit.jupiter.api.Assertions.*; +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.*; +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOption; +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOptionDTO; +@ExtendWith(MockitoExtension.class) class PaymentOptionMapperTest { @Mock - private DataCipherService dataCipherServiceMock; + private InstallmentMapper installmentMapperMock; + @Mock + private InstallmentPIIMapper installmentPIIMapperMock; + + private PaymentOptionMapper paymentOptionMapper; - private final PersonMapper personMapper = new PersonMapper(); - private final TransferMapper transferMapper = new TransferMapper(); - private final InstallmentMapper installmentMapper = new InstallmentMapper(personMapper, transferMapper); - private final InstallmentPIIMapper installmentPIIMapper = new InstallmentPIIMapper(dataCipherServiceMock); - private final PaymentOptionMapper paymentOptionMapper = new PaymentOptionMapper(installmentMapper, installmentPIIMapper); + @BeforeEach + void setUp(){ + paymentOptionMapper = new PaymentOptionMapper(installmentMapperMock, installmentPIIMapperMock); + } @Test void givenValidPaymentOptionDTO_WhenMapToModel_ThenReturnPaymentOptionAndInstallmentMap() { PaymentOption paymentOptionExpected = buildPaymentOption(); + paymentOptionExpected.setStatus(PaymentOptionStatus.UNPAID); PaymentOptionDTO paymentOptionDTO = buildPaymentOptionDTO(); + Mockito.when(installmentMapperMock.mapToModel(buildInstallmentDTO())).thenReturn(buildInstallment()); + + Mockito.when(installmentPIIMapperMock.map(buildInstallment())).thenReturn(Pair.of(buildInstallmentNoPII(), buildInstallmentPIIDTO())); + Pair> result = paymentOptionMapper.mapToModel(paymentOptionDTO); - assertEquals(paymentOptionExpected, result.getFirst()); + reflectionEqualsByName(paymentOptionExpected, result.getFirst()); checkNotNullFields(result.getFirst(), "updateOperatorExternalId", "creationDate", "updateDate"); } } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapperTest.java index 2135b07..bd157b8 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/PersonMapperTest.java @@ -2,15 +2,25 @@ import it.gov.pagopa.pu.debtpositions.dto.Person; import it.gov.pagopa.pu.debtpositions.dto.generated.PersonDTO; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; -import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.*; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPerson; +import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPersonDTO; +@ExtendWith(MockitoExtension.class) class PersonMapperTest { - private final PersonMapper personMapper = new PersonMapper(); + private PersonMapper personMapper; + + @BeforeEach + void setUp() { + personMapper = new PersonMapper(); + } @Test void givenValidPersonDTO_WhenMapToModel_ThenReturnPerson() { @@ -19,7 +29,7 @@ void givenValidPersonDTO_WhenMapToModel_ThenReturnPerson() { Person result = personMapper.mapToModel(personDTO); - assertEquals(personExpected, result); + reflectionEqualsByName(personExpected, result); checkNotNullFields(result); } } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapperTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapperTest.java index 835a033..a944047 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapperTest.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/mapper/TransferMapperTest.java @@ -2,15 +2,25 @@ import it.gov.pagopa.pu.debtpositions.dto.generated.TransferDTO; import it.gov.pagopa.pu.debtpositions.model.Transfer; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import static it.gov.pagopa.pu.debtpositions.util.TestUtils.checkNotNullFields; -import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.*; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransfer; +import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransferDTO; +@ExtendWith(MockitoExtension.class) class TransferMapperTest { - private final TransferMapper transferMapper = new TransferMapper(); + private TransferMapper transferMapper; + + @BeforeEach + void setUp(){ + transferMapper = new TransferMapper(); + } @Test void givenValidTransferDTO_WhenMapToModel_ThenReturnTransfer() { @@ -19,7 +29,7 @@ void givenValidTransferDTO_WhenMapToModel_ThenReturnTransfer() { Transfer result = transferMapper.mapToModel(transferDTO); - assertEquals(transferExpected, result); + reflectionEqualsByName(transferExpected, result); checkNotNullFields(result, "updateOperatorExternalId", "creationDate", "updateDate"); } } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceTest.java new file mode 100644 index 0000000..5bfed30 --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/DebtPositionHierarchyStatusAlignerServiceTest.java @@ -0,0 +1,126 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign; + +import it.gov.pagopa.pu.debtpositions.dto.generated.*; +import it.gov.pagopa.pu.debtpositions.mapper.DebtPositionMapper; +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import it.gov.pagopa.pu.debtpositions.repository.InstallmentNoPIIRepository; +import it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition.DebtPositionInnerStatusAlignerService; +import it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption.PaymentOptionInnerStatusAlignerService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +import static it.gov.pagopa.pu.debtpositions.util.TestUtils.reflectionEqualsByName; +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPosition; +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPositionDTO; +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOption; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class DebtPositionHierarchyStatusAlignerServiceTest { + + @Mock + private DebtPositionRepository debtPositionRepositoryMock; + @Mock + private InstallmentNoPIIRepository installmentNoPIIRepositoryMock; + @Mock + private PaymentOptionInnerStatusAlignerService paymentOptionInnerStatusAlignerServiceMock; + @Mock + private DebtPositionInnerStatusAlignerService debtPositionInnerStatusAlignerServiceMock; + @Mock + private DebtPositionMapper debtPositionMapperMock; + + private DebtPositionHierarchyStatusAlignerService service; + + @BeforeEach + void setUp() { + service = new DebtPositionHierarchyStatusAlignerServiceImpl(debtPositionRepositoryMock, installmentNoPIIRepositoryMock, paymentOptionInnerStatusAlignerServiceMock, debtPositionInnerStatusAlignerServiceMock, debtPositionMapperMock); + } + + @Test + void givenFinalizeSyncStatusThenOk() { + Long id = 1L; + InstallmentStatus newStatus = InstallmentStatus.UNPAID; + String iupdPagoPa = "iupdPagoPa"; + DebtPosition debtPosition = buildDebtPosition(); + + Mockito.when(debtPositionRepositoryMock.findOneWithAllDataByDebtPositionId(id)).thenReturn(debtPosition); + Mockito.doNothing().when(installmentNoPIIRepositoryMock).updateStatusAndIupdPagopa(id, iupdPagoPa, newStatus); + Mockito.doNothing().when(paymentOptionInnerStatusAlignerServiceMock).updatePaymentOptionStatus(buildPaymentOption()); + Mockito.doNothing().when(debtPositionInnerStatusAlignerServiceMock).updateDebtPositionStatus(debtPosition); + Mockito.when(debtPositionMapperMock.mapToDto(debtPosition)).thenReturn(buildDebtPositionDTO()); + + Map syncStatusDTO = new HashMap<>(); + IupdSyncStatusUpdateDTO iupdSyncStatusUpdateDTO = IupdSyncStatusUpdateDTO.builder() + .newStatus(newStatus) + .iupdPagopa(iupdPagoPa) + .build(); + + syncStatusDTO.put("iud", iupdSyncStatusUpdateDTO); + DebtPositionDTO result = service.finalizeSyncStatus(id, syncStatusDTO); + + assertEquals(DebtPositionStatus.UNPAID, result.getStatus()); + assertEquals(PaymentOptionStatus.UNPAID, result.getPaymentOptions().getFirst().getStatus()); + assertEquals(InstallmentStatus.UNPAID, result.getPaymentOptions().getFirst().getInstallments().getFirst().getStatus()); + reflectionEqualsByName(buildDebtPositionDTO(), result); + } + + @Test + void givenFinalizeSyncStatusWhenIsNotSyncThenDoNotUpdateStatus() { + Long id = 1L; + InstallmentStatus newStatus = InstallmentStatus.UNPAID; + String iupdPagoPa = "iupdPagoPa"; + DebtPosition debtPosition = buildDebtPosition(); + debtPosition.getPaymentOptions().getFirst().getInstallments().getFirst().setStatus(InstallmentStatus.PAID); + + Mockito.when(debtPositionRepositoryMock.findOneWithAllDataByDebtPositionId(id)).thenReturn(debtPosition); + + Map syncStatusDTO = new HashMap<>(); + IupdSyncStatusUpdateDTO iupdSyncStatusUpdateDTO = IupdSyncStatusUpdateDTO.builder() + .newStatus(newStatus) + .iupdPagopa(iupdPagoPa) + .build(); + + syncStatusDTO.put("iud", iupdSyncStatusUpdateDTO); + + DebtPositionDTO result = service.finalizeSyncStatus(id, syncStatusDTO); + + assertNull(result); + verify(debtPositionRepositoryMock).findOneWithAllDataByDebtPositionId(id); + verify(installmentNoPIIRepositoryMock, times(0)).updateStatusAndIupdPagopa(id, iupdPagoPa, newStatus); + } + + @Test + void givenFinalizeSyncStatusWhenDoesNotHaveIudThenDoNotUpdateStatus() { + Long id = 1L; + InstallmentStatus newStatus = InstallmentStatus.UNPAID; + String iupdPagoPa = "iupdPagoPa"; + DebtPosition debtPosition = buildDebtPosition(); + + Mockito.when(debtPositionRepositoryMock.findOneWithAllDataByDebtPositionId(id)).thenReturn(debtPosition); + + Map syncStatusDTO = new HashMap<>(); + IupdSyncStatusUpdateDTO iupdSyncStatusUpdateDTO = IupdSyncStatusUpdateDTO.builder() + .newStatus(newStatus) + .iupdPagopa(iupdPagoPa) + .build(); + + syncStatusDTO.put("fake-iud", iupdSyncStatusUpdateDTO); + + DebtPositionDTO result = service.finalizeSyncStatus(id, syncStatusDTO); + + assertNull(result); + verify(debtPositionRepositoryMock).findOneWithAllDataByDebtPositionId(id); + verify(installmentNoPIIRepositoryMock, times(0)).updateStatusAndIupdPagopa(id, iupdPagoPa, newStatus); + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceTest.java new file mode 100644 index 0000000..54b060f --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionInnerStatusAlignerServiceTest.java @@ -0,0 +1,61 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.TreeSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class DebtPositionInnerStatusAlignerServiceTest { + + @Mock + private DebtPositionRepository debtPositionRepositoryMock; + + private DebtPositionInnerStatusAlignerService debtPositionInnerStatusAlignerService; + + @BeforeEach + void setUp() { + debtPositionInnerStatusAlignerService = new DebtPositionInnerStatusAlignerServiceImpl(debtPositionRepositoryMock); + } + + @Test + void testUpdateDebtPositionStatus() { + // given + DebtPosition debtPosition = new DebtPosition(); + debtPosition.setDebtPositionId(1L); + + PaymentOption paymentOption1 = new PaymentOption(); + paymentOption1.setPaymentOptionId(1L); + paymentOption1.setStatus(PaymentOptionStatus.TO_SYNC); + + PaymentOption paymentOption2 = new PaymentOption(); + paymentOption2.setPaymentOptionId(2L); + paymentOption2.setStatus(PaymentOptionStatus.PAID); + + debtPosition.setPaymentOptions(new TreeSet<>(List.of(paymentOption1, paymentOption2))); + + Mockito.doNothing().when(debtPositionRepositoryMock).updateStatus(1L, DebtPositionStatus.TO_SYNC); + + // when + debtPositionInnerStatusAlignerService.updateDebtPositionStatus(debtPosition); + + // then + assertEquals(DebtPositionStatus.TO_SYNC, debtPosition.getStatus()); + verify(debtPositionRepositoryMock, times(1)).updateStatus(1L, DebtPositionStatus.TO_SYNC); + } +} + diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusCheckerTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusCheckerTest.java new file mode 100644 index 0000000..afe185b --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/debtposition/DebtPositionStatusCheckerTest.java @@ -0,0 +1,218 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.debtposition; + +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import it.gov.pagopa.pu.debtpositions.model.DebtPosition; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.DebtPositionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.TreeSet; + +import static it.gov.pagopa.pu.debtpositions.util.faker.DebtPositionFaker.buildDebtPosition; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(MockitoExtension.class) +class DebtPositionStatusCheckerTest { + + @Mock + private DebtPositionRepository debtPositionRepositoryMock; + + private DebtPositionStatusChecker checker; + + @BeforeEach + void setUp() { + checker = new DebtPositionStatusChecker(debtPositionRepositoryMock); + } + + /** + * Test if the status is TO_SYNC when at least one PaymentOption has status TO_SYNC. + */ + @Test + void testCalculateNewStatus_ToSync() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.TO_SYNC, PaymentOptionStatus.PAID); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.TO_SYNC, result); + } + + /** + * Test if the status is PARTIALLY_PAID when there is at least one PAID and one UNPAID paymentOption. + */ + @Test + void testCalculateNewStatus_PartiallyPaid() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.PAID, PaymentOptionStatus.UNPAID); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.PARTIALLY_PAID, result); + } + + /** + * Test if the status is PARTIALLY_PAID when there is at least one PAID and one EXPIRED paymentOption. + */ + @Test + void testDeterminePaymentOptionStatus_PartiallyPaid2() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.PAID, PaymentOptionStatus.EXPIRED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.PARTIALLY_PAID, result); + } + + /** + * Test if the status is UNPAID when all paymentOptions are UNPAID. + */ + @Test + void testCalculateNewStatus_Unpaid() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.UNPAID, PaymentOptionStatus.UNPAID); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.UNPAID, result); + } + + /** + * Test if the status is UNPAID when all paymentOptions are CANCELLED, with at least one UNPAID. + */ + @Test + void testDeterminePaymentOptionStatus_Unpaid2() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.UNPAID, PaymentOptionStatus.CANCELLED, PaymentOptionStatus.CANCELLED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.UNPAID, result); + } + + /** + * Test if the status is PAID when all paymentOptions are PAID. + */ + @Test + void testCalculateNewStatus_Paid() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.PAID, PaymentOptionStatus.PAID); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.PAID, result); + } + + /** + * Test if the status is PAID when all paymentOptions are CANCELLED, with at least one PAID. + */ + @Test + void testDeterminePaymentOptionStatus_Paid2() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.PAID, PaymentOptionStatus.CANCELLED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.PAID, result); + } + + /** + * Test if the status is REPORTED when all paymentOptions are CANCELLED, with at least one REPORTED. + */ + @Test + void testCalculateNewStatus_Reported() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.REPORTED, PaymentOptionStatus.CANCELLED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.REPORTED, result); + } + + /** + * Test if the status is REPORTED when all paymentOptions are REPORTED. + */ + @Test + void testDeterminePaymentOptionStatus_Reported2() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.REPORTED, PaymentOptionStatus.REPORTED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.REPORTED, result); + } + + /** + * Test if the status is INVALID when all paymentOptions are INVALID. + */ + @Test + void testCalculateNewStatus_Invalid() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.INVALID, PaymentOptionStatus.INVALID); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.INVALID, result); + } + + /** + * Test if the status is INVALID when all paymentOptions are CANCELLED, with at least one INVALID. + */ + @Test + void testDeterminePaymentOptionStatus_Invalid2() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.INVALID, PaymentOptionStatus.CANCELLED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.INVALID, result); + } + + /** + * Test if the status is CANCELLED when all paymentOptions are CANCELLED. + */ + @Test + void testCalculateNewStatus_Cancelled() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.CANCELLED, PaymentOptionStatus.CANCELLED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.CANCELLED, result); + } + + /** + * Test if the status is EXPIRED when all paymentOptions are EXPIRED. + */ + @Test + void testCalculateNewStatus_Expired() { + List paymentOptionStatusList = List.of(PaymentOptionStatus.EXPIRED, PaymentOptionStatus.EXPIRED); + DebtPositionStatus result = checker.calculateNewStatus(paymentOptionStatusList); + assertEquals(DebtPositionStatus.EXPIRED, result); + } + + /** + * Test if an exception is thrown when the list of paymentOptions is empty. + */ + @Test + void testCalculateNewStatus_InvalidStatus() { + List paymentOptionStatusList = List.of(); + Exception exception = assertThrows(InvalidStatusException.class, () -> checker.calculateNewStatus(paymentOptionStatusList)); + assertEquals("Unable to determine status for DebtPosition", exception.getMessage()); + } + + @Test + void testGetChildStatuses() { + DebtPosition debtPosition = new DebtPosition(); + PaymentOption option1 = new PaymentOption(); + option1.setPaymentOptionId(1L); + option1.setStatus(PaymentOptionStatus.PAID); + + PaymentOption option2 = new PaymentOption(); + option2.setPaymentOptionId(2L); + option2.setStatus(PaymentOptionStatus.UNPAID); + + debtPosition.setPaymentOptions(new TreeSet<>(List.of(option1, option2))); + + List statuses = checker.getChildStatuses(debtPosition); + + assertEquals(2, statuses.size()); + assertEquals(PaymentOptionStatus.PAID, statuses.get(0)); + assertEquals(PaymentOptionStatus.UNPAID, statuses.get(1)); + } + + @Test + void testSetStatus() { + DebtPosition debtPosition = buildDebtPosition(); + DebtPositionStatus newStatus = DebtPositionStatus.PAID; + + checker.setStatus(debtPosition, newStatus); + + assertEquals(newStatus, debtPosition.getStatus()); + } + + @Test + void testStoreStatus() { + DebtPosition debtPosition = buildDebtPosition(); + DebtPositionStatus newStatus = DebtPositionStatus.PAID; + + Mockito.doNothing().when(debtPositionRepositoryMock).updateStatus(1L, newStatus); + + checker.storeStatus(debtPosition, newStatus); + + Mockito.verify(debtPositionRepositoryMock).updateStatus(1L, newStatus); + } +} + diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceTest.java new file mode 100644 index 0000000..eb0ee89 --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionInnerStatusAlignerServiceTest.java @@ -0,0 +1,62 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption; + +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.PaymentOptionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.TreeSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class PaymentOptionInnerStatusAlignerServiceTest { + + @Mock + private PaymentOptionRepository paymentOptionRepositoryMock; + + private PaymentOptionInnerStatusAlignerService service; + + @BeforeEach + void setUp() { + service = new PaymentOptionInnerStatusAlignerServiceImpl(paymentOptionRepositoryMock); + } + + @Test + void testUpdatePaymentOptionStatus(){ + // given + PaymentOption paymentOption = new PaymentOption(); + paymentOption.setPaymentOptionId(1L); + + InstallmentNoPII installmentNoPII1 = new InstallmentNoPII(); + installmentNoPII1.setInstallmentId(1L); + installmentNoPII1.setStatus(InstallmentStatus.TO_SYNC); + + InstallmentNoPII installmentNoPII2 = new InstallmentNoPII(); + installmentNoPII2.setInstallmentId(1L); + installmentNoPII2.setStatus(InstallmentStatus.PAID); + + paymentOption.setInstallments(new TreeSet<>(List.of(installmentNoPII1, installmentNoPII2))); + + Mockito.doNothing().when(paymentOptionRepositoryMock).updateStatus(1L, PaymentOptionStatus.TO_SYNC); + + // when + service.updatePaymentOptionStatus(paymentOption); + + // then + assertEquals(PaymentOptionStatus.TO_SYNC, paymentOption.getStatus()); + verify(paymentOptionRepositoryMock, times(1)).updateStatus(1L, PaymentOptionStatus.TO_SYNC); + } + + +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusCheckerTest.java b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusCheckerTest.java new file mode 100644 index 0000000..38a5b52 --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/service/statusalign/paymentoption/PaymentOptionStatusCheckerTest.java @@ -0,0 +1,217 @@ +package it.gov.pagopa.pu.debtpositions.service.statusalign.paymentoption; + +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; +import it.gov.pagopa.pu.debtpositions.exception.custom.InvalidStatusException; +import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; +import it.gov.pagopa.pu.debtpositions.model.PaymentOption; +import it.gov.pagopa.pu.debtpositions.repository.PaymentOptionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.TreeSet; + +import static it.gov.pagopa.pu.debtpositions.util.faker.PaymentOptionFaker.buildPaymentOption; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(MockitoExtension.class) +class PaymentOptionStatusCheckerTest { + + @Mock + private PaymentOptionRepository paymentOptionRepositoryMock; + + private PaymentOptionStatusChecker checker; + + @BeforeEach + void setUp() { + checker = new PaymentOptionStatusChecker(paymentOptionRepositoryMock); + } + + /** + * Test if the status is TO_SYNC when at least one Installment has status TO_SYNC. + */ + @Test + void testCalculateNewStatus_ToSync() { + List installmentStatusList = List.of(InstallmentStatus.TO_SYNC, InstallmentStatus.PAID); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.TO_SYNC, result); + } + + /** + * Test if the status is PARTIALLY_PAID when there is at least one PAID and one UNPAID installment. + */ + @Test + void testCalculateNewStatus_PartiallyPaid() { + List installmentStatusList = List.of(InstallmentStatus.PAID, InstallmentStatus.UNPAID); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.PARTIALLY_PAID, result); + } + + /** + * Test if the status is PARTIALLY_PAID when there is at least one PAID and one EXPIRED installment. + */ + @Test + void testCalculateNewStatus_PartiallyPaid2() { + List installmentStatusList = List.of(InstallmentStatus.PAID, InstallmentStatus.EXPIRED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.PARTIALLY_PAID, result); + } + + /** + * Test if the status is UNPAID when all installments are UNPAID. + */ + @Test + void testCalculateNewStatus_Unpaid() { + List installmentStatusList = List.of(InstallmentStatus.UNPAID, InstallmentStatus.UNPAID); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.UNPAID, result); + } + + /** + * Test if the status is UNPAID when all installments are CANCELLED, with at least one UNPAID. + */ + @Test + void testCalculateNewStatus_Unpaid2() { + List installmentStatusList = List.of(InstallmentStatus.UNPAID, InstallmentStatus.CANCELLED, InstallmentStatus.CANCELLED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.UNPAID, result); + } + + /** + * Test if the status is PAID when all installments are PAID. + */ + @Test + void testCalculateNewStatus_Paid() { + List installmentStatusList = List.of(InstallmentStatus.PAID, InstallmentStatus.PAID); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.PAID, result); + } + + /** + * Test if the status is PAID when all installments are CANCELLED, with at least one PAID. + */ + @Test + void testCalculateNewStatus_Paid2() { + List installmentStatusList = List.of(InstallmentStatus.PAID, InstallmentStatus.CANCELLED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.PAID, result); + } + + /** + * Test if the status is REPORTED when all installments are CANCELLED, with at least one REPORTED. + */ + @Test + void testCalculateNewStatus_Reported() { + List installmentStatusList = List.of(InstallmentStatus.REPORTED, InstallmentStatus.CANCELLED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.REPORTED, result); + } + + /** + * Test if the status is REPORTED when all installments are REPORTED. + */ + @Test + void testCalculateNewStatus_Reported2() { + List installmentStatusList = List.of(InstallmentStatus.REPORTED, InstallmentStatus.REPORTED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.REPORTED, result); + } + + /** + * Test if the status is INVALID when all installments are INVALID. + */ + @Test + void testCalculateNewStatus_Invalid() { + List installmentStatusList = List.of(InstallmentStatus.INVALID, InstallmentStatus.INVALID); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.INVALID, result); + } + + /** + * Test if the status is INVALID when all installments are CANCELLED, with at least one INVALID. + */ + @Test + void testCalculateNewStatus_Invalid2() { + List installmentStatusList = List.of(InstallmentStatus.INVALID, InstallmentStatus.CANCELLED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.INVALID, result); + } + + /** + * Test if the status is CANCELLED when all installments are CANCELLED. + */ + @Test + void testCalculateNewStatus_Cancelled() { + List installmentStatusList = List.of(InstallmentStatus.CANCELLED, InstallmentStatus.CANCELLED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.CANCELLED, result); + } + + /** + * Test if the status is EXPIRED when all installments are EXPIRED. + */ + @Test + void testCalculateNewStatus_Expired() { + List installmentStatusList = List.of(InstallmentStatus.EXPIRED, InstallmentStatus.EXPIRED); + PaymentOptionStatus result = checker.calculateNewStatus(installmentStatusList); + assertEquals(PaymentOptionStatus.EXPIRED, result); + } + + /** + * Test if an exception is thrown when the list of installments is empty. + */ + @Test + void testCalculateNewStatus_InvalidStatus() { + List installmentStatusList = List.of(); + Exception exception = assertThrows(InvalidStatusException.class, () -> checker.calculateNewStatus(installmentStatusList)); + assertEquals("Unable to determine status for PaymentOption", exception.getMessage()); + } + + @Test + void testGetChildStatuses() { + PaymentOption paymentOption = new PaymentOption(); + InstallmentNoPII installmentNoPII1 = new InstallmentNoPII(); + installmentNoPII1.setInstallmentId(1L); + installmentNoPII1.setStatus(InstallmentStatus.PAID); + + InstallmentNoPII installmentNoPII2 = new InstallmentNoPII(); + installmentNoPII2.setInstallmentId(2L); + installmentNoPII2.setStatus(InstallmentStatus.UNPAID); + + paymentOption.setInstallments(new TreeSet<>(List.of(installmentNoPII1, installmentNoPII2))); + + List statuses = checker.getChildStatuses(paymentOption); + + assertEquals(2, statuses.size()); + assertEquals(InstallmentStatus.PAID, statuses.get(0)); + assertEquals(InstallmentStatus.UNPAID, statuses.get(1)); + } + + @Test + void testSetStatus() { + PaymentOption paymentOption = buildPaymentOption(); + PaymentOptionStatus newStatus = PaymentOptionStatus.PAID; + + checker.setStatus(paymentOption, newStatus); + + assertEquals(newStatus, paymentOption.getStatus()); + } + + @Test + void testStoreStatus() { + PaymentOption paymentOption = buildPaymentOption(); + PaymentOptionStatus newStatus = PaymentOptionStatus.PAID; + + Mockito.doNothing().when(paymentOptionRepositoryMock).updateStatus(1L, newStatus); + + checker.storeStatus(paymentOption, newStatus); + + Mockito.verify(paymentOptionRepositoryMock).updateStatus(1L, newStatus); + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/util/ReflectionUtils.java b/src/test/java/it/gov/pagopa/pu/debtpositions/util/ReflectionUtils.java new file mode 100644 index 0000000..e25685b --- /dev/null +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/util/ReflectionUtils.java @@ -0,0 +1,40 @@ +package it.gov.pagopa.pu.debtpositions.util; + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * The Class ReflectionUtils. + */ +public class ReflectionUtils { + + /** + * It will transform the input enum into a String, using the same logic of Jackson + */ + public static String enum2String(Enum o) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + final Object result = enum2Object(o); + + return result != null ? result.toString() : null; + } + + /** + * It will transform the input enum into an Object, using the same logic of Jackson + */ + private static Object enum2Object(Enum o) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method jsonValueMethod = null; + for (Method m : o.getClass().getMethods()) { + if (m.getAnnotation(JsonValue.class) != null) { + jsonValueMethod = m; + break; + } + } + + if (jsonValueMethod == null) { + jsonValueMethod = o.getClass().getMethod("toString"); + } + + return jsonValueMethod.invoke(o); + } +} diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/util/TestUtils.java b/src/test/java/it/gov/pagopa/pu/debtpositions/util/TestUtils.java index c285205..605cfad 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/util/TestUtils.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/util/TestUtils.java @@ -1,12 +1,19 @@ package it.gov.pagopa.pu.debtpositions.util; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Assertions; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.time.chrono.ChronoZonedDateTime; +import java.util.*; +@Slf4j public class TestUtils { + private TestUtils(){} /** @@ -22,4 +29,93 @@ public static void checkNotNullFields(Object o, String... excludedFields) { f -> !excludedFieldsSet.contains(f.getName())); } + public static List reflectionEqualsByName(Object o1, Object o2, String... ignoredFields) { + Set ignoredFieldSet = new HashSet<>(Arrays.asList(ignoredFields)); + Assertions.assertFalse(o1 == null ^ o2 == null, String.format("Both objects have to be null or not null:%n%s%n%s", o1 == null ? "null" : o1.getClass().getName(), o2 == null ? "null" : o2.getClass().getName())); + + List checked = new ArrayList<>(); + + if (o1 != null) { + for (Method m1 : o1.getClass().getMethods()) { + if ((m1.getName().startsWith("get") || m1.getName().startsWith("is")) && m1.getParameterCount() == 0 && !"getClass".equals(m1.getName())) { + String fieldName = StringUtils.uncapitalize(m1.getName().replaceFirst("^(?:get|is)", "")); + if (ignoredFieldSet.contains(fieldName)) { + continue; + } + Method m2 = null; + try { + m2 = Arrays.stream(o2.getClass().getMethods()).filter(m -> m.getName().equalsIgnoreCase(m1.getName()) && m.getParameterCount() == m1.getParameterCount()).findFirst().orElse(null); + + if (m2 == null) { + throw new NoSuchMethodException(); + } + + Object v1 = m1.invoke(o1); + Object v2 = m2.invoke(o2); + + boolean result = true; + + Assertions.assertFalse(v1 == null ^ v2 == null, String.format("Both objects have to be null or not null:%n%s = %s%n%s = %s", m1, v1 == null ? "null" : v1.getClass().getName(), m2, v2 == null ? "null" : v2.getClass().getName())); + if (v1 != null) { + if (v1.equals(v2)) { + //Do Nothing + } else if (v1 instanceof Comparable && v2 instanceof Comparable && ((Comparable) v1).compareTo(v2) == 0) { + //Do Nothing + } else if (OffsetDateTime.class.isAssignableFrom(v1.getClass()) && OffsetDateTime.class.isAssignableFrom(v2.getClass())) { + result = ((OffsetDateTime) v1).isEqual((OffsetDateTime) v2); + } else if (ChronoZonedDateTime.class.isAssignableFrom(v1.getClass()) && ChronoZonedDateTime.class.isAssignableFrom(v2.getClass())) { + result = ((ChronoZonedDateTime) v1).isEqual((ChronoZonedDateTime) v2); + } else if (v1.getClass().isAssignableFrom(v2.getClass()) && ((v1.getClass().isPrimitive() && v2.getClass().isPrimitive()) || (hasStandardEquals(v1.getClass()) && hasStandardEquals(v2.getClass())))) { + result = false; + } else if (BigInteger.class.isAssignableFrom(v1.getClass()) && Integer.class.isAssignableFrom(v2.getClass())) { + result = ((BigInteger) v1).intValue() == ((int) v2); + } else if (BigInteger.class.isAssignableFrom(v2.getClass()) && Integer.class.isAssignableFrom(v1.getClass())) { + result = ((BigInteger) v2).intValue() == ((int) v1); + } else if (BigInteger.class.isAssignableFrom(v1.getClass()) && Long.class.isAssignableFrom(v2.getClass())) { + result = ((BigInteger) v1).longValue() == ((long) v2); + } else if (BigInteger.class.isAssignableFrom(v2.getClass()) && Long.class.isAssignableFrom(v1.getClass())) { + result = ((BigInteger) v2).longValue() == ((long) v1); + } else if (String.class.isAssignableFrom(v1.getClass()) && Enum.class.isAssignableFrom(v2.getClass())) { + v2 = ReflectionUtils.enum2String((Enum) v2); + result = v1.equals(v2); + } else if (String.class.isAssignableFrom(v2.getClass()) && Enum.class.isAssignableFrom(v1.getClass())) { + v1 = ReflectionUtils.enum2String((Enum) v1); + result = v2.equals(v1); + } else { + boolean equals = v1.toString().equals(v2.toString()); + if (Enum.class.isAssignableFrom(v2.getClass()) && Enum.class.isAssignableFrom(v1.getClass())) { + result = equals; + } else if (String.class.isAssignableFrom(v1.getClass()) || String.class.isAssignableFrom(v2.getClass())) { + result = equals; + } else { + checked.addAll(reflectionEqualsByName(v1, v2)); + } + } + + checked.add(m1); + } + + Assertions.assertTrue(result, String.format("Invalid compare between methods%n%s = %s%n%s = %s", m1, v1, m2, v2)); + } catch (NoSuchMethodException e) { + log.warn("Method {} is not defined in {}{}", m1, o2.getClass().getName(), e.getMessage()); + } catch (IllegalAccessException | + InvocationTargetException e) { + throw new IllegalStateException(String.format("[ERROR] Something gone wrong comparing %s with %s%n%s", m1, m2, e.getMessage())); + } + } + } + } + return checked; + } + + private static boolean hasStandardEquals(Class clazz) { + try { + return !clazz.getMethod("equals", Object.class).equals(Object.class.getMethod("equals", Object.class)); + } catch (NoSuchMethodException e) { + // This exception cannot be thrown + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/DebtPositionFaker.java b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/DebtPositionFaker.java index 47291c3..82d6dfe 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/DebtPositionFaker.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/DebtPositionFaker.java @@ -1,6 +1,7 @@ package it.gov.pagopa.pu.debtpositions.util.faker; import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.DebtPositionStatus; import it.gov.pagopa.pu.debtpositions.model.DebtPosition; import java.time.OffsetDateTime; @@ -20,7 +21,7 @@ public static DebtPosition buildDebtPosition() { debtPosition.setDebtPositionId(1L); debtPosition.setIupdOrg("IUPD_ORG"); debtPosition.setDescription("Test Description"); - debtPosition.setStatus("ACTIVE"); + debtPosition.setStatus(DebtPositionStatus.TO_SYNC); debtPosition.setIngestionFlowFileId(1001L); debtPosition.setIngestionFlowFileLineNumber(10L); debtPosition.setOrganizationId(500L); @@ -39,7 +40,7 @@ public static DebtPositionDTO buildDebtPositionDTO() { debtPositionDTO.setDebtPositionId(1L); debtPositionDTO.setIupdOrg("IUPD_ORG"); debtPositionDTO.setDescription("Test Description"); - debtPositionDTO.setStatus("ACTIVE"); + debtPositionDTO.setStatus(DebtPositionStatus.UNPAID); debtPositionDTO.setIngestionFlowFileId(1001L); debtPositionDTO.setIngestionFlowFileLineNumber(10L); debtPositionDTO.setOrganizationId(500L); diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/InstallmentFaker.java b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/InstallmentFaker.java index 8e4b451..23e2ef6 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/InstallmentFaker.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/InstallmentFaker.java @@ -3,6 +3,7 @@ import it.gov.pagopa.pu.debtpositions.dto.Installment; import it.gov.pagopa.pu.debtpositions.dto.InstallmentPIIDTO; import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.InstallmentStatus; import it.gov.pagopa.pu.debtpositions.model.InstallmentNoPII; import java.time.LocalDateTime; @@ -13,6 +14,7 @@ import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPerson; import static it.gov.pagopa.pu.debtpositions.util.faker.PersonFaker.buildPersonDTO; import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransfer; +import static it.gov.pagopa.pu.debtpositions.util.faker.TransferFaker.buildTransferDTO; public class InstallmentFaker { @@ -23,7 +25,7 @@ public static Installment buildInstallment(){ return Installment.builder() .installmentId(1L) .paymentOptionId(1L) - .status("status") + .status(InstallmentStatus.TO_SYNC) .iupdPagopa("iupdPagoPa") .iud("iud") .iuv("iuv") @@ -50,7 +52,7 @@ public static InstallmentNoPII buildInstallmentNoPII(){ return InstallmentNoPII.builder() .installmentId(1L) .paymentOptionId(1L) - .status("status") + .status(InstallmentStatus.TO_SYNC) .iupdPagopa("iupdPagoPa") .iud("iud") .iuv("iuv") @@ -69,7 +71,7 @@ public static InstallmentNoPII buildInstallmentNoPII(){ .balance("balance") .creationDate(date) .updateDate(date) - .updateOperatorExternalId("OPERATOREXTERNALID") + .updateOperatorExternalId("OPERATOREXTERNALUSERID") .build(); } @@ -83,7 +85,7 @@ public static Installment buildInstallmentNoUpdate(){ return Installment.builder() .installmentId(1L) .paymentOptionId(1L) - .status("status") + .status(InstallmentStatus.TO_SYNC) .iupdPagopa("iupdPagoPa") .iud("iud") .iuv("iuv") @@ -99,7 +101,7 @@ public static Installment buildInstallmentNoUpdate(){ .humanFriendlyRemittanceInformation("humanFriendlyRemittanceInformation") .balance("balance") .debtor(buildPerson()) - .transfers(List.of()) + .transfers(List.of(buildTransfer())) .creationDate(date) .updateDate(date) .build(); @@ -109,7 +111,7 @@ public static InstallmentDTO buildInstallmentDTO() { return InstallmentDTO.builder() .installmentId(1L) .paymentOptionId(1L) - .status("status") + .status(InstallmentStatus.UNPAID) .iupdPagopa("iupdPagoPa") .iud("iud") .iuv("iuv") @@ -125,7 +127,7 @@ public static InstallmentDTO buildInstallmentDTO() { .humanFriendlyRemittanceInformation("humanFriendlyRemittanceInformation") .balance("balance") .debtor(buildPersonDTO()) - .transfers(List.of()) + .transfers(List.of(buildTransferDTO())) .creationDate(date.atOffset(ZoneOffset.UTC)) .updateDate(date.atOffset(ZoneOffset.UTC)) .build(); diff --git a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/PaymentOptionFaker.java b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/PaymentOptionFaker.java index 3c09bd4..109e7ec 100644 --- a/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/PaymentOptionFaker.java +++ b/src/test/java/it/gov/pagopa/pu/debtpositions/util/faker/PaymentOptionFaker.java @@ -1,6 +1,7 @@ package it.gov.pagopa.pu.debtpositions.util.faker; import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionDTO; +import it.gov.pagopa.pu.debtpositions.dto.generated.PaymentOptionStatus; import it.gov.pagopa.pu.debtpositions.enums.PaymentOptionType; import it.gov.pagopa.pu.debtpositions.model.PaymentOption; @@ -9,6 +10,8 @@ import java.util.List; import java.util.TreeSet; +import static it.gov.pagopa.pu.debtpositions.util.faker.InstallmentFaker.*; + public class PaymentOptionFaker { private static final OffsetDateTime DATE = OffsetDateTime.of(2025, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); @@ -19,25 +22,26 @@ public static PaymentOption buildPaymentOption() { paymentOption.setDebtPositionId(1L); paymentOption.setTotalAmountCents(2000L); paymentOption.setDueDate(DATE); - paymentOption.setStatus("PENDING"); + paymentOption.setStatus(PaymentOptionStatus.TO_SYNC); paymentOption.setMultiDebtor(true); paymentOption.setDescription("Payment description"); paymentOption.setPaymentOptionType(PaymentOptionType.SINGLE_INSTALLMENT); - paymentOption.setInstallments(new TreeSet<>()); + paymentOption.setInstallments(new TreeSet<>(List.of(buildInstallmentNoPII()))); return paymentOption; } + public static PaymentOptionDTO buildPaymentOptionDTO() { PaymentOptionDTO paymentOptionDTO = new PaymentOptionDTO(); paymentOptionDTO.setPaymentOptionId(1L); paymentOptionDTO.setDebtPositionId(1L); paymentOptionDTO.setTotalAmountCents(2000L); paymentOptionDTO.setDueDate(DATE); - paymentOptionDTO.setStatus("PENDING"); + paymentOptionDTO.setStatus(PaymentOptionStatus.UNPAID); paymentOptionDTO.setMultiDebtor(true); paymentOptionDTO.setDescription("Payment description"); paymentOptionDTO.setPaymentOptionType(PaymentOptionDTO.PaymentOptionTypeEnum.SINGLE_INSTALLMENT); - paymentOptionDTO.setInstallments(List.of()); + paymentOptionDTO.setInstallments(List.of(buildInstallmentDTO())); return paymentOptionDTO; } }